cancel
Showing results for 
Search instead for 
Did you mean: 

usage of CSRF token in ABAP report for POST request

former_member203179
Participant

Question 1:

I have problems while using REST POST operations in ABAP report in context of the CSRF token

Background: Testing the possibilities of consuming oData services with ABAP reports and handling JSON content

Problem: I always get :

Satus:        403

Response: CSRF token validation failed finisdh

Example ABAP report:

1) first GET to fetch the token

2) make the post with HEADER parameter fetched token X-CSRF-Token


PARAMETERS: partner TYPE but000-partner,

            invoice TYPE vbrk-vbeln.

START-OF-SELECTION.

  TRY.

      DATA: lv_service_url TYPE string,

            lo_http_client TYPE REF TO if_http_client,

            lo_rest_client TYPE REF TO cl_rest_http_client,

            lo_response    TYPE REF TO if_rest_entity,

            lv_http_status TYPE i,

            lv_token       TYPE string.

* (1)

** GET CSRF token

      cl_http_client=>create_by_url(

        EXPORTING  url   = lv_service_url " oData service URL

        IMPORTING  client = lo_http_client

        EXCEPTIONS OTHERS = 1 ).

      CHECK sy-subrc EQ 0.

      lo_http_client->request->set_content_type( 'application/json' ).

      CREATE OBJECT lo_rest_client EXPORTING io_http_client = lo_http_client.

* fetching token

      lo_rest_client->if_rest_client~set_request_header( EXPORTING iv_name = 'X-CSRF-Token' iv_value = 'Fetch' ).

      lo_rest_client->if_rest_client~get( EXCEPTIONS OTHERS = 1 ).

      CHECK sy-subrc EQ 0.

      lo_response = lo_rest_client->if_rest_client~get_response_entity( ).

      lv_http_status = lo_response->get_header_field( '~status_code' ).

      CHECK lv_http_status EQ '200'.

      " get token for POST request

      lv_token = lo_response->get_header_field( 'X-CSRF-Token' ).

      FREE: lo_http_client, lo_rest_client.

* (2)

** POST with CSRF token

      DATA LO_REQUEST TYPE REF TO if_rest_entity.

      cl_http_client=>create_by_url(

        EXPORTING  url = lv_service_url " oData service URL

        IMPORTING  client = lo_http_client

        EXCEPTIONS OTHERS = 1 ).

      CHECK sy-subrc EQ 0.

      lo_http_client->request->set_content_type( 'application/json' ).

      CREATE OBJECT lo_rest_client EXPORTING io_http_client = lo_http_client.

* build Example request data to send

      DATA lv_json_post_data TYPE string.

      lv_json_post_data = | \{ | &&

                          |   "user":"{ sy-uname }", | &&

                          |   "partner":"{ partner }", | &&

                          |   "invoice":"{ invoice }"  | &&

                          | \} |.

      lo_request = lo_rest_client->if_rest_client~create_request_entity( ).

      CHECK lo_request IS BOUND.

      lo_request->set_header_field( iv_name = 'X-CSRF-Token' iv_value = lv_token ).

      lo_request->set_content_type( iv_media_type = if_rest_media_type=>gc_appl_json ).

      lo_request->set_string_data( lv_json_post_data ).

* POST

      lo_rest_client->if_rest_resource~post( lo_request ).

* Collect response

      lo_response = lo_rest_client->if_rest_client~get_response_entity( ).

      lv_http_status = lo_response->get_header_field( '~status_code' ).

      DATA lv_response TYPE string.

      lv_response = lo_response->get_string_data( ).

      CASE lv_http_status.

        WHEN '201'.

* JSON to ABAP

          DATA lr_json_deserializer TYPE REF TO cl_trex_json_deserializer.

          CREATE OBJECT lr_json_deserializer.

          DATA ls_test_content TYPE ztestrest.

          lr_json_deserializer->deserialize( EXPORTING json = lv_response IMPORTING abap = ls_test_content ).

          WRITE: /'CreadedBacklinkID:', ls_test_content-hashid.

        WHEN OTHERS.

          WRITE 😕 'Satus:', lv_http_status.

          WRITE 😕 'Response:', lv_response.

      ENDCASE.

    CATCH cx_root.

  ENDTRY.

It works without problems when I deactivate the CSRF token in the SICF for this service with parameter ~CHECK_CSRF_TOKEN = 0

When I deactivating the CSRF token there is the need to use the header parameter X-Requested-With :


lo_request->set_header_field( iv_name = 'X-Requested-With' iv_value = 'X' ).


and it also works in a browser REST test client. Problems are only visible when using the ABAP oo rest client

Question 2 :

The other problem, when the service works the response from the Gateway is XML per default and I have no clue how to say that I also want the returning entity in the server response also in JSON.

Can anyone help me here ?

Thanks in advance,

Thomas

Accepted Solutions (1)

Accepted Solutions (1)

former_member184867
Active Contributor
0 Kudos

Question 1: You can have a look at the framework code /IWFND/CL_OCI_REQ_PROCESSOR method /IWFND/IF_OCI_REQ_PROCESSOR~UPDATE_ENTRY, especially at the line where lo_http_wrapper->put( ).

Question 2:Add $format=json at teh URL, this will return you the data in JSON format

former_member203179
Participant
0 Kudos

Hi,

thanks for your time and help.

the mentioned methods are interesting.

regarding Question 1:

I checked the SAP methods and it seems they are going the same way :

I adapted my code to use the special /iwcor/if_rest_entity entity and the /iwcor/if_rest_client REST client but still the same.

When I check the request header of the entity object before the post it looks good :

but after calling the POST method the request entity has the following header always with error 'CSRF token validation failed'

I then tested the same token with the Chrome RESTclient and also there the token is invalid.

So it seems more a problem that the fetched token is invalid already when I call the POST request ?

But it works when I fetch a token in the Rest client and use this one - I now also realize that with the Chrome Rest Client I get always the same token as long the token is active. But the fetch method in ABAP gives me always another token which is always invalid ^^

regarding Question 2:

the problem is that I can't add $format=json in a POST request - that will end up in this error :

{

  "error":{

    "code":"005056A509B11EE1B9A8FBE8A100F78B",

    "message":{

      "lang":"en",

      "value":"The Data Services Request contains SystemQueryOptions that are not allowed for this Request Type"

    }

  }

}


it seems specially for POST or UPDATE request we can't use the option $format=json and so we need to parse the XML with a writer to JSON by our own

AshwinDutt
Active Contributor
0 Kudos

Hello Thomas,

For Question 2,

Try adding an Header -> Accept : application/json


We add this header to get the response of POST operations in json format.


Try and check once.


Regards,

Ashwin

former_member203179
Participant
0 Kudos

This gives me a JSON response for a POST request

Thanks a lot

And regarding Question 1:

* the ABAP GET request always generates a different token - this should be always the same as long the token is valid

* testing to use the valid token from the external REST client for the POST request also ends up in the message 'invalid token'

AshwinDutt
Active Contributor
0 Kudos

Hello Thomas,

I have not tried posting data the way you are trying. I will also check once and update if i am able to do.

Regards,

Ashwin

former_member184867
Active Contributor
0 Kudos

Hi,

I had a look at your code. I can see you are calling teh below line

  cl_http_client=>create_by_url( )

two times. In the first call you fetch the CSRF token and in the second call you use the previously fetched CSRF value. I think this causes the issue.

As you are instantiating the lo_http_client twice, you are creating two different sessions. Thus the CSRF from first session is invalid for the second session and you get the CSRF error.

You should reuse lo_http_client second time, instead of creating it once more.

Hopefully this would solve the CSRF issue.

Refer to the class I mentioned, exactly the same thing is happening there as well

Regards

Atanu

former_member184867
Active Contributor
0 Kudos

To post in JSON format, try the following

1.add http header Content-Type:Application/json

2.HTTP Method: POST

3. URL: http://,...../Entityset

4. Proper JSON Payload

former_member203179
Participant
0 Kudos

Yes this is correct regarding the sending payload (request).

But the question was about the response - so the system gets the request and then an ID is generated for the new entry and we get a resposne with the enety and here we can parse the new ID.

The correct answer here was adding the http header key 'Accept' and then the response from the POST is also in json :


      lo_rest_client->set_request_header(

            EXPORTING iv_name = 'Accept'

                      iv_value = 'application/json' ).

former_member203179
Participant
0 Kudos

I started with a scenario like that and only separated it because I thought the error is that I use the same http client. I just tested it again and yes I get the same error.

That is not the reason and also the token should be valid for 24 hours and not dependend on the session.

I also already realized that I get the same token with different browser REST clients but the ABAP rest GET token fetch method always generates a new token, I think here the problem already starts.

former_member184867
Active Contributor
0 Kudos

exactly

former_member184867
Active Contributor
0 Kudos

Though I am not sure, may be you can check /IWFND/CL_SODATA_HTTP_HANDLER method CHECK_SERVICE_ACCESS in debug.

former_member203179
Participant
0 Kudos

There are differences in the releases :

1. Release < 7.03/7.31 or the security session management is inactive: An own CSRF cookie gets generated (sap-XSRF_<SystemID>_<SAPClient>) and this CSRF token remains valid for 24 hours (86400 seconds).

2. Release >= 7.03/7.31, the validity is bound to the security session, which depends on the system parameter http/security_session_timeout value (see transaction RZ11 for details on this parameter). By default, the security session management is active in these releases."

Cross-Site Request Forgery Protection - SAP Gateway Foundation (SAP_GWFND) - SAP Library

So it depends on the session and I found also the table the session ID and the CSRF token is saved

SECURITY_CONTEXT and in the ->POST() call itself he generates a new entry in this table - when I hard change the token to the token from the new created table line it works ^^

1) after get post and before POST call

2) when I debug in POST call before method call /IWCOR/IF_REST_CSRF_HANDLER~VALIDATE_CSRF_TOKEN

system calls fuba  TH_GET_SECURITY_CONTEXT_REF which returns the LINK for the newest generated token - here 2767E995468E11E5A473005056B03327

here the short extract from GET to POST with same object and only after execute the POST the second CSRF token is created somehow


      lo_rest_client->get( exceptions others = 1 ).

* Get Token after GET

      lv_token = lo_rest_client->get_response_header( /iwfnd/if_oci_common=>gc_xcsrf_token ).

** POST with CSRF token

* Set the CSRF token to the header

      lo_rest_client->set_request_header(

            exporting iv_name = /iwfnd/if_oci_common=>gc_xcsrf_token

                      iv_value = lv_token ).

      lo_request ?= lo_rest_client->create_request_entity( ).

* set data

      lo_request->set_string_data( lv_json_post_data ).

* POST  / until here there is only one token in table SECURITY_CONTEXT

      lo_rest_client->post( lo_request ).

Answers (2)

Answers (2)

0 Kudos

Hi

I have problems while using REST POST operations in ABAP report in context of the CSRF token .

I am getting a error using the same code as 'CSRF~Token' required with response 403 .

abcl0653
Product and Topic Expert
Product and Topic Expert
0 Kudos

Hello Thomas,

For question 1, we had the same issue, every get call will return a new token, that's the problem, after we setting abap http client to accpet cookie, it works fine.

lo_http_client->PROPERTYTYPE_ACCEPT_COOKIE = if_http_client=>co_enabled

.

former_member203179
Participant
0 Kudos

Thanks for the Info - I also realized that we need to forward the Session Cookie receiving from the GET request together with the CSRF token info - I think this is done automatically when cookies are turned ok

LiaoAlex
Associate
Associate
0 Kudos

Hello,

I also encountered the same issue, it seems you have overcome it.

Could you tell me what exact Property/Value should be added the HTTP request header, to GET or POST request?

Thanks so much,

Alex.

former_member203179
Participant
0 Kudos

To process the POST you need to start with the GET request and a "x-csrf-token: fetch".

And here the request header will contain a automatically created "cookie" :

And the response header will deliver the "x-csrf-token" :

And in the POST request you not only should include the "x-csrf-token" but also the content of the first request cookie :

LiaoAlex
Associate
Associate
0 Kudos

Hi Thomas,

So many thanks for your helpful answer, it perfectly solved my issue.

Thanks and best regards,

Alex.