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
Level 2
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
  • 12492 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

Feike_Visser1
Adobe Employee
Adobe Employee
December 29, 2015

Do you see in the browser a request being made to get the token?

Level 3
December 29, 2015

Have you added the necessary csrf client lib(granite.csrf.standalone) as dependency to your form component ? 

Check details here.https://docs.adobe.com/docs/en/aem/6-1/develop/security/csrf-protection.html

yasha34006128
Level 2
December 30, 2015

Thanks for replying.

Yes I do see a request made to http://localhost:4502/libs/granite/csrf/token.json And it provides the valid token as it is. The token value is first collected from this URL and later passed as an parameter value for parameter name :cq_csrf_token. The same is been retrieved in the doFilter method also mentioned in the question post.

Kunal_Gaba_
Kunal_Gaba_Accepted solution
December 30, 2015

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}

harish_bhatt
December 30, 2015

I'm also facing the same issue but with publisher. When i debugged, it seems to have problem in below method of com.adobe.granite.csrf.impl.CSRFFilter

protected boolean isValidRequest(HttpServletRequest request)
  {
    String csrf = request.getParameter(":cq_csrf_token");
    if (StringUtils.isBlank(csrf))
    {
      csrf = request.getHeader("CSRF-Token");
      if (StringUtils.isBlank(csrf))
      {
        this.logger.info("isValidRequest: empty CSRF token - rejecting");
        return false;
      }
    }
    String[] tokenParts = csrf.split("\\.");
    if (ArrayUtils.getLength(tokenParts) != 2)
    {
      this.logger.info("isValidRequest: not well formed CSRF token - rejecting");
      return false;
    }
    String token;
    try
    {
      String user = request.getRemoteUser();
      ClaimsSet claimsSet = ((JWT)new JWTReader().read("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + csrf)).getClaimsSet();
      
      long expirationTime = claimsSet.getExpirationTime();
      long buildAt = claimsSet.getIssuedAt();
      long expiresIn = expirationTime - buildAt;
      
      token = this.jwsBuilderFactory.getInstance("HS256").setSubject(user).setExpiresIn(expiresIn).setIssuedAt(claimsSet.getIssuedAt()).setCustomClaimsSetField("scope", ":aem_csrf_token_value").build();
    }
    catch (CryptoException e)
    {
      this.logger.error("isValidRequest: failed to generate CSRF token on validation", e);
      return false;
    }
    boolean valid = this.jwsValidator.validate(token);
    if (!valid)
    {
      this.logger.info("isValidRequest: the CSRF token is expired");
      return false;
    }
    String signature = ((JWT)new JWTReader().read(token)).getSignature();
    return signature.equals(tokenParts[1]);
  }

Signature is not matching, hence returning false. Same configuration is there on author and working fine there. Any clue or pointer.

 

Exception:-

31.12.2015 01:35:09.021 *INFO* [127.0.0.1 [1451505909011] POST /bin/****Servlet HTTP/1.1] com.adobe.granite.csrf.impl.CSRFFilter doFilter: the provided CSRF token is invalid

Kunal_Gaba_
December 30, 2015

Have you replicated the crypto keys (/etc/keys/hmac) in all of your AEM boxes as mentioned in the below doc? 

https://docs.adobe.com/docs/en/aem/6-1/develop/security/csrf-protection.html

harish_bhatt
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.
harish_bhatt
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.

Kunal_Gaba_
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
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.