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.
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.
-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 the ECM-CS-XSRF-Token cookie value and pass the
cookie value as the ECM-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 the
HttpOnly 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 the ECM-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.