Need help in resolving and understanding com.adobe.granite.csrf.impl.CSRFFilter doFilter: the provided CSRF token is invalid in AEM 6.1 | Community
Skip to main content
yasha34006128
New Participant
December 29, 2015
Solved

Need help in resolving and understanding com.adobe.granite.csrf.impl.CSRFFilter doFilter: the provided CSRF token is invalid in AEM 6.1

  • December 29, 2015
  • 16 replies
  • 12491 views

Problem Overview:

We are upgrading our system from CQ5.4 to AEM 6.1. In the existing code we have referred to POST.jsp using ajax, where we are invoking Authentication services to authenticate an external user to the system. We are getting the below error when we do an ajax post to the resource POST.jsp. in the error.log. com.adobe.granite.csrf.impl.CSRFFilter doFilter: the provided CSRF token is invalid

What have we tried:

  1. To fix this we tried the solution as per AEM 6.1 docs https://docs.adobe.com/docs/en/aem/6-1/administer/security/security-checklist.html#par_title_1046104842.
    But it didn't worked.
  2. We tried removing POST from the filter methods in Adobe Granite CSRF Filter configuration from system/console/configMgr. This worked but this affects the security of our system since it allows other external system to POST data. (Correct me if I am wrong on security)
  3. We tried adding google chrome browser user agent in the Safe User Agents in Adobe Granite CSRF Filter configuration from system/console/configMgr. This worked but the application can be used from various other user agents which we cannot keep on whitelisting in the Safe User Agents.

Also we decomplied the com.adobe.granite.csrf.impl.CSRFFilter and found the below code:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; if ((request.getAuthType() != null) && (isFilteredMethod(request)) && (doFilterBasedOnUserAgent(request)) && (!isValidRequest(request))) { HttpServletResponse response = (HttpServletResponse)res; this.logger.info("doFilter: the provided CSRF token is invalid"); response.sendError(403); return; } chain.doFilter(req, res); }

In the above code, The isFilteredMethod checks if the current request(POST) is present in the configured filtered methods of Adobe Granite CSRF Filter.
The doFilterBasedOnUserAgent checks if the current request's User Agent is absent in the configured User agents.

request.getAuthType is "FORM" in our case. So this will not lead to (403).

The isValidRequest gets the request param :cq_csrf_token and checks if this parameter value is valid. (Observed this and is valid in our case and sends the parameter)
So what is the significance of filtered methods and user agents ? and why does CSRF sends 403 for POST.jsp although all CQ and AEM 5.x versions support this way of POSTING request? Please NOTE: We are facing this in our local dev environment in AUTHOR.

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by Kunal_Gaba_

The default expiration of the token is set to 10 minutes since it is issued. I hope the token which is submitted with the AJAX request is not expired. 

You can verify the issued time and expired time of the token generated by doing base64 decoding of the claims set part (which is the first string before the dot). 

Example - 

{"token":"eyJleHAiOjE0NTE1MDEwNzksImlhdCI6MTQ1MTUwMDQ3OX0.brSE904pU-1_YCCcuAmmdwiguWblMXn0PF3In7AThBY"}

If you decode "eyJleHAiOjE0NTE1MDEwNzksImlhdCI6MTQ1MTUwMDQ3OX0" then you get the following string -

{"exp":1451501079,"iat":1451500479}

16 replies

Kunal_Gaba_
New Participant
January 12, 2016

Hi Yash, 

I have checked the token values and the expired time is 10 minutes greater than the issued time which is as expected. 

exp - 1452592341  --> GMT: Tue, 12 Jan 2016 09:52:21 GMT

iat - 1452591741 --> GMT: Tue, 12 Jan 2016 09:42:21 GMT

You can use http://www.epochconverter.com/ for doing this conversion. 

-Kunal

yasha34006128
New Participant
January 12, 2016

Thanks @shopsinm, Kunal23 for the replies and code. Please correct my understanding added below.

The default script or the one shared by @shopsinm basically gets the token value from /libs/granite/csrf/token.json.

The default script intercepts and sets this value as a hidden parameter in the form which is then sent in the next POST request.

Alternatively script shared by @shopsinm fetches the value manually and sets it in the header.

However when I hit this URL http://localhost:4502/libs/granite/csrf/token.json in the browser the token value obtained is

eyJleHAiOjE0NTI1OTIzNDEsImlhdCI6MTQ1MjU5MTc0MX0

decodes to {"exp":1452592341,"iat":1452591741} converted to date and shows Sat Jan 17 1970 19:29:52 which is expired/old date.

So either default script or the one shared doesn't works for us.

Please let me know if I am misunderstanding this.

Kunal_Gaba_
New Participant
January 7, 2016

Hi Yash, 

You can refer the code sample shared by @shopsinm.

The default CSRF JS adds the token field in the form when the form is loaded initially. The script intercepts the submission handler of the form and sets the token attribute again in the form with the refreshed value just before the form is submitted. I guess in your case since your firing AJAX post requests for the submissions the default script can not intercept the submission handler and set the token value again.  So you need to always get the token again from the server and set it either as form parameter or in the request header before sending the request. 

The default script file used for CSRF is - http://localhost:4502/etc/clientlibs/granite/jquery/granite/csrf.js

yasha34006128
New Participant
January 7, 2016

Hi Kunal23 we do not send the token as part of our custom AJAX request we only send username and password. How can we identify which javascript sends the parameter :cq_csrf? To identify all the request params I used below code in the POST.jsp.

Map<String, String[]> parameters = request.getParameterMap(); for(String parameter : parameters.keySet()) { String[] values = parameters.get(parameter); log.info("Param name: "+ parameter + " value: "+ values[0]); }
Kunal_Gaba_
New Participant
January 4, 2016

Have you written a custom javascript code to send the token as part of your AJAX request ? I guess the default CSRF javascript does not inject the tokens in AJAX calls. If you have written custom JS code then make sure to refresh the token periodically after every 5 minutes before you send the request via AJAX. Thats what the default OOTB CSRF JS does. 

yasha34006128
New Participant
January 4, 2016

Thanks for the help kunal23. How can I modify the expiration date? I do see the expiration date and issue date is old.

harish_bhatt
New Participant
December 31, 2015

Just to add one more thing. I'm directly hitting the publisher not dispatcher. So i think /etc/key/hmac can be out of sync as long as i'm directly hitting particular publisher.

Kunal_Gaba_
New Participant
December 30, 2015

Yes it is a binary property. You need to create a package containing /etc/key node and install it on all of your AEM instances. 

harish_bhatt
New Participant
December 30, 2015

i cannot see /etc/key/hmac in crx. What i have is /etc/key. We have hman binary file on key folder as property. Name of the property is hmac. Please see attached image.

harish_bhatt
New Participant
December 30, 2015
As we are on the process of upgrade to AEM 6.1. We do not have whole set up ready in full working condition. Replication is not working. But as per the process I've created the package from author for the same and installed it to publisher.