Configuring security against Cross-Site Request Forgery (CSRF)
Because Content Services GraphQL API is a stateless application, you must configure a token exchange for your applications to protect against Cross-Site Request Forgery (CSRF) attacks.
Cross-Site Request Forgery (CSRF/XSRF) is an attack that forces an end user to run unwanted actions on a web application where they are currently authenticated. In such an attack, if a user tries to access their intended application, the malicious code can inject commands to make the application do what the malicious code wants. This malicious code might be running in another tab or window of the browser.
CSRF attacks are common attacks on web-based applications. Normally these attacks can be prevented by generating a random number or string on the first request to the application. The application saves this token in the session. In subsequent requests the client passes this token along with its requests, and the server validates this token from the client. In this way, malicious websites cannot post valid requests even if they have access to a valid session in a browser.
In Content Services GraphQL, this feature is enabled by default. All applications that use the
GraphQL API must pass a ECM-CS-XSRF-Token
token in the header and cookie.
ECM-CS-XSRF-Token
,
in every request header as well as in a cookie. The application validates the requests to make sure
that these token values are identical. Content Services GraphQL rejects the requests if the token
from the client request does not match the expected value. Java applications that use a Rest assured
library application can pass the header and cookie as
follows:String xsrfToken = java.util.UUID.randomUUID().toString();
io.restassured.RestAssured.given()
.relaxedHTTPSValidation()
.spec(spec)
.contentType("application/json")
.auth().preemptive().oauth2(accessToken )
.header("Accept", Constants.JSON_CONTENT_TYPE).and()
.header("Content-Type", Constants.JSON_CONTENT_TYPE).and()
.header("ECM-CS-XSRF-Token", xsrfToken).and()
.cookie("ECM-CS-XSRF-Token", xsrfToken).and()
.body(GraphqlQueryjsonObj.toString())
.post(graphqlEndPointURL);
-Decm.content.graphql.xsrf.validate.disable=TRUE
-Dcom.ibm.ecm.content.graphql.enable.graphiql=TRUE
Do not disable the feature in a production environment.
For any applications that uses Content Services GraphQL, it is recommended that the GraphQL
client generates the token and set it as a cookie on the client browser, and saves the same value in
the user session. When the client request comes, the GraphQL client can validate the
ECM-CS-XSRF-Token
cookie against the value that was saved in the session. When the
request is passed to Content Services GraphQL, these values are in the header
ECM-CS-XSRF-Token
and cookie EM-CS-XSRF-Token
. If the application
that uses the Content Services GraphQL API is stateless, then that application can generate a token
and pass the same token in both the header ECM-CS-XSRF-Token
and cookie
ECM-CS-XSRF-Token
to the Content Services GraphQL.
- Bypassing the XSRF check for the ping page
-
You can also configure your environment to bypass the XSRF check for the ping page. In an authoring or development environment, this configuration might be helpful for applications that need to do a health check or access the ping page, but have no way of passing additional headers and cookies in the request.
To bypass the XSRF check for the ping page, set the following JVM argument to TRUE:-Decm.content.graphql.disable.xsrf.validation.for.ping=TRUE
This JVM argument also sets the random string as the cookie value for
ECM-CS-XSRF-Token
on the client. This can help for clients with restrictions on appending a cookie to the request that is being sent to the GraphQL server. In those cases, set the-Decm.content.graphql.disable.xsrf.validation.for.ping=TRUE
JVM argument on the GraphQL server, access the ping page, then read theECM-CS-XSRF-Token
cookie value and pass the cookie value as theECM-CS-XSRF-Token
token header. - Enabling XSRF validation with GraphiQL enabled
-
You can enable the XSRF validation at the same time when GraphiQL UI is enabled. Though the default is still to disable XSRF validation if GraphiQL is enabled, the JVM argument can be used to explicitly enable XSRF validation when GraphiQL is enabled. The JVM argument must be set to false along with the option to enable GraphiQL.
-Decm.content.graphql.xsrf.validate.disable=FALSE -Dcom.ibm.ecm.content.graphql.enable.graphiql=TRUE
- Enabling the XSRF cookie
HttpOnly
attribute -
As mentioned earlier in the topic, you can use the ping page to establish the
ECM-CS-XSRF-Token
cookie. To maintain security standards, the cookies set by the server should also include theHttpOnly
attribute. For example,set-cookie: ECM-CS-XSRF-Token=adfafeeb-566f-4827-b4a8-a0a9a2142124; Path=/; Secure; HttpOnly; SameSite=None
Based on the client, when GraphQL requests are sent with the attribute set, it might not be possible to read the cookie value to use as the token value. For example, javascript, which runs inside a browser is prevented from reading these cookie values. The
HttpOnly
attribute is not included by default when the ping page sets theECM-CS-XSRF-Token
cookie. It can be enabled with the following JVM argument:-Decm.content.graphql.xsrf.cookie.httponly.enable=true
A client can still access this token value because the same value is returned by the ping page as a header value. The client can read the header value rather than the cookie value and pass that value back as the
ECM-CS-XSRF-Token
token header in a subsequent GraphQL request. The following javascript example calls the ping page, and makes a GraphQL request by using the returned token value.var xsrftok = null; function graphQLFetcher(graphQLBody) { var pingPath = gqlHost + "/ping"; var pingHdrs = { Accept: 'application/json', 'Content-Type': 'application/json', }; var gqlPath = gqlHost + "/graphql"; var hdrs = { Accept: 'application/json', 'Content-Type': 'application/json', } return fetch( pingPath, { method: 'get', headers: pingHdrs, credentials: 'include', } ).then(function(pingResp) { var pingXsrf = pingResp.headers.get('ecm-cs-xsrf-token'); if (pingXsrf) { xsrftok = pingXsrf; } if (xsrftok) { hdrs["ECM-CS-XSRF-Token"] = xsrftok; } return fetch( gqlPath, { method: 'post', headers: hdrs, body: JSON.stringify(graphQLBody), credentials: 'include' }, ).then(function (response) { return response.json().catch(function() { return response.text(); }); }); }); }
This example always calls to the ping page before it calls the GraphQL endpoint. In a real application, you need to call the ping page for the first time to establish the XSRF token and then use it in repeated calls to GraphQL.