CSRFVerifyToken issues

631 views
Skip to first unread message

Kristin

unread,
Oct 14, 2014, 10:03:41 AM10/14/14
to cfu...@googlegroups.com
Hi All,

I have implemented CSRF token verification in a site (CF10), and I am seeing a lot of errors validating the token where there shouldn't be (valid users).  I (think) I followed the documentation, and a few blog posts in the implementation, but clearly I have something wrong.

In Application.cfc, I have set an Application var, for the key (using generateSecretKey). 

In each form, I have a hidden field csrftoken, with a value of "#CSRFGenerateToken(Application.TKey)#">
When the form is processed, I validate the token: CSRFVerifyToken(form.csrfToken, Application.TKey)

In a number of cases, this is failing - I can see that the token was submitted, and these are valid users.

So I was reading about it on the OWASP site, and it says "In general, developers need only generate this token once for the current session."  Might this be why I'm having an issue? Users are using the back button or something?

Also, for more important forms, like login, I am using "force new" - could this be causing the issues?

Should I just be setting the token in the OnSessionStart?  If I want to Force New for certain forms, will that cause issues?

Thanks!

Kristin

Pete Freitag

unread,
Oct 14, 2014, 11:18:49 AM10/14/14
to cfu...@googlegroups.com
Hi Kristin,

You can only have one valid token per category, the category is the value that you have passed into CSRFGenerateToken, Application.TKey in your case. So the problem you might run into in your case is that if you call CSRFGenerateToken more than once on a page (for example if you have two forms on the page) only the last call the CSRFGenerateToken is valid. Further if the user has a new tab open under the same session, or an AJAX call generates a new token, then the previously generated token will become invalid.

The category does not need to be a secure random value (like generateSecretKey) because it is never shown to the user, the CSRFGenerateToken function takes care of generating a random token value that the user gets. So instead of passing in Application.TKey you can simply pass in a unique name of a form / page, that way each form/page gets it's own token value.

--
Pete Freitag - Adobe Community Professional
http://foundeo.com/ - ColdFusion Consulting & Products
http://hackmycf.com - Is your ColdFusion Server Secure?
http://www.youtube.com/watch?v=ubESB87vl5U - FuseGuard your CFML in 10 minutes


--
You received this message because you are subscribed to the Google Groups "Central New York ColdFusion Users Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cfugcny+u...@googlegroups.com.
To post to this group, send email to cfu...@googlegroups.com.
Visit this group at http://groups.google.com/group/cfugcny.
For more options, visit https://groups.google.com/d/optout.

Kristin

unread,
Oct 14, 2014, 12:00:57 PM10/14/14
to cfu...@googlegroups.com
Hi Pete,

I figured out that the "more than one form" might be an issue on one of my pages, but there is only one page with that issue.  The strange thing is, I can't seem to replicate the issue in my testing, with multiple tabs, using the back button, etc., anything I do, any browser - I am always able to verify.

On the page with multiple forms, if call CSRFGenerateToken(thisParticularForm) in each form, and each form on the page has a different value for thisParticularForm, then as long as I validate with CSRFVerifyToken(form.csrfToken, thisParticularForm), it will have the right value to verify?

Or, can I generate the token before I output the form, set it to a var, and then use it (the same one) in the (multiple) forms on the page, and validate against that var?

In most cases there is only one form on the page, and there are no ajax calls, yet they still fail.  It happens fairly often. 

Thanks,

Kristin

Charles Robertson

unread,
Aug 30, 2017, 12:10:44 PM8/30/17
to Central New York ColdFusion Users Group
 Kristin & Pete

I may be going mad here. But, if an attacker copies the form, using Firebug, and then pastes it to a page on his/her website, adds malicious values or extra form fields, and then submits the form, it will pass CSRF validation, because the hidden token form field with the copied token will be submitted along with the other fields. How does this make our forms more secure?

Checking the CGI.HTTP_REFERER is a more reliable security check.

But, maybe, I have got this all wrong?

Thanks

Charlie

Pete Freitag

unread,
Aug 30, 2017, 12:22:46 PM8/30/17
to Central New York ColdFusion Users Group
Hi Charlie,

Yes that is correct if the attacker modifies the form it will still pass CSRF validation, it is not meant to protect against this. CSRF is concerned about a case where the attacker tricks an authenticated user into performing an action, this is the easiest example I can think of to illustrate the threat of CSRF:

Attacker creates webpage http://attack-kittens.com/ with a picture of kittens, but also with a hidden image tag as follows:


Now every time someone visits his kitten page that if you have a valid session (you are currently logged in to) bank.example.com your browser will make a request to:


AND it will send your cookies for bank.example.com with that request, so it allows the attacker to perform the action on the authenticated user's behalf, in this case transferring 5000 into their account.


Checking the HTTP_REFERER will not stop the form tampering either, the only way to take care of that is to have server side validation of your forms in place. It might stop SOME but not ALL CSRF attacks.

Hope that helps clarify.

--
Pete Freitag

ad...@establishmindfulness.com

unread,
Aug 30, 2017, 7:48:40 PM8/30/17
to Central New York ColdFusion Users Group

Sorry Pete. I just had a look at your link, and see that the form is hidden and submitted on page load. Got it now...

It seems that Ben Forta, who wrote an excellent book called:

"Coldfusion 10 Enhancements and Improvements"

On page 257, he sets out a CSRF example, similar to the one, I cited in my first reply. But, as you & I, agree, this type of form request would pass token validation, if the token field & token were copied and submitted maliciously.

In fact, the POST example in the link you provided would also pass validation, if the correct token was submitted.

So, I don't really understand, how this secures forms, unless the attacker omits to pass the token.

Perhaps my test was incorrect, because I carried it out within the same browser, but from different domains. If I use a different browser [Firefox] to POST to site A [Chrome], maybe validation will fail, even, if I pass the correct token value in the hidden token field?

For CSRFVerifyToken to work, does it rely on the attack origin's browser being different to the browser that the token is set in.

However, I thought that a users session is tied to its application. So that sessions are sandboxed on different applications.

I had expected my original test to fail token validation, because site A is tied to a different application to site B, despite the fact that I was passing the correct token from the attack site.





On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrot Hi All,
Message has been deleted

ad...@establishmindfulness.com

unread,
Aug 30, 2017, 7:48:40 PM8/30/17
to Central New York ColdFusion Users Group
Pete. Thanks for this explanation. I now understand CSRF more clearly.

In relation to form tampering, NOT CSRF:

On the issue of CGI.HTTP_REFERER, my code checks to see if the request comes from the same domain, and if not, I redirect the user to the home page, before any form processing takes place.

The scenario:

1) Attacker copies the form HTML on site A.

2) Pastes the HTML on a page that the attacker creates on his/her own server. Tampers with the form

3) Submits the form

The CGI.HTTP_REFERER domain stub will not match site A's domain. Action can then be taken, if the match fails.

UPDATE:

To prevent the attacker from copying the hidden token field with token, I add the token field with token just before the form is submitted, using JavaScript.

The attacker, when inspecting the HTML, in Firebug, will be lulled into a false sense that the developer, who created the form, is an idiot for not having added a hidden token field.

The attacker then carries out point 2 & 3 above.

The checking code on site A, detects that no token field was submitted and immediately aborts form processing. In this case, aborting occurs if:

1) the CGI.HTTP_REFERER stub does not match site A's domain value

OR

2) the token field is not submitted

Or

3) the token field is submitted, but the token does not validate

Kristin

unread,
Aug 31, 2017, 7:51:59 AM8/31/17
to Central New York ColdFusion Users Group
I don't think you should ever rely on http_referer.  It is not always included - it may be blank, and can be changed by client software.  Some anitvirus and firewall software will strip the referer, and I think a proxy will strip them as well.

Kristin

ad...@establishmindfulness.com

unread,
Aug 31, 2017, 12:43:34 PM8/31/17
to Central New York ColdFusion Users Group
Good point Kristin. The dreaded HTTP_ prefix ;) But, I am more concerned about the vast array of loopholes in the CSRFVerifyToken functionality.

Having read the information from the link Pete supplied, it seems that this method is almost pointless, in terms of its security gains. If I decide to use it, I will be using CSRFGenerateToken(), in its most basic form, without any arguments. It is kind of pointless using a key or force refresh, because the important part is the SESSION_ID that the token is tied to, and not the token itself. Using a random string makes zero difference, because the attacker can obtain the token easily, by Firebugging the form. So, I will just let the system generate the string for me.


On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrote:
Message has been deleted
Message has been deleted

Pete Freitag

unread,
Aug 31, 2017, 12:57:59 PM8/31/17
to cfu...@googlegroups.com
On Thu, Aug 31, 2017 at 12:07 PM, <ad...@establishmindfulness.com> wrote:
Good point Kristin. The dreaded HTTP_ prefix ;) But, I am more concerned about the vast array of loopholes in the CSRFVerifyToken functionality.

Having read the information from the link Pete supplied, it seems that this method is almost pointless, in terms of its security gains. If I decide to use it, I will be using CSRFGenerateToken(), in its most basic form, without any arguments. It is kind of pointless using a key or force refresh, because the important part is the SESSION_ID that the token is tied to, and not the token itself. Using a random string makes zero difference, because the attacker can obtain the token easily, by Firebugging the form. So, I will just let the system generate the string for me.

I think you are misinterpreting the info, the attacker does not have access to the CSRF token, they do not have access to firebug either in the CSRF attack scenario. The attacker does not have access to the victims browser, the victim is logged into the site they have session id's which are stored in cookies, but the CSRF token is not stored in the cookie, so in order to prevent request forgery you have to send the token. So the CSRF token is important and it should be random and of sufficient length.

If you are assuming that the attacker already has access to the browser and the session of the user, then that is called a hijacked session, and is another scenario to consider.

Another caveat I hesitate to even mention is that if your site has XSS vulnerabilities, then all your CSRF protections are moot (unless you are forcing password re-entry) because the attacker can then steal the CSRF token via AJAX requests, of course then it is no longer a cross site request forgery, it is a same site request forgery.

As far as form tampering goes, the CSRF token does not do anything to protect against that since the attacker in that case can use Firebug or craft their own HTTP request however they want. The difference between form tampering and CSRF is that with CSRF there is a logged in victim on another computer that the attacker is targeting. With form tampering the attacker is just messing with requests, nothing in the HTTP request payload should be trusted, everything needs to be validated.

--
Pete Freitag
https://foundeo.com/ - ColdFusion Consulting & Products
http://hackmycf.com - CFML Server Security Scanner

charlie arehart

unread,
Aug 31, 2017, 1:44:23 PM8/31/17
to cfu...@googlegroups.com
Message has been deleted

ad...@establishmindfulness.com

unread,
Aug 31, 2017, 2:06:04 PM8/31/17
to Central New York ColdFusion Users Group
Pete. I understand what you are saying.

But consider this:

1) If the attacker has access to the form's URL, which he/she must have in order to succeed, the attacker could visit the legitimate form by pasting the form's URL into the browser's address bar, assuming that it is a public form.
For forms behind a login, the attacker could create an account and access the form this way.

2) The attacker would then have access to the form and the hidden form field's token. The attacker could then copy the form's HTML and then add it to a page, visited by the victim.

The attacker would then change the form fields 'acct' and 'amount' values, and the value of the submit button. See below:

<form action="http://bank.com/transfer.do" method="POST">
<input type="hidden" name="acct" value="MARIA"/>
<input type="hidden" name="amount" value="100000"/>
<input type="hidden" name="csrftoken" value="foobar"/>
<input type="submit" value="View my pictures"/>
</form>

When the victim visits the page and clicks on the form's spoofed submit button, the form will get posted and pass verification.

The link you provided:

https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

Seems to suggest that this is possible.

The only difference between your link's example and my one, is that my example has added a hidden 'csrftoken' field to the form

Of course, this assumes that the web page that this HTML is being posted to, has no security measures that prevent unescaped HTML from being posted.

This would be a shocking oversight, but I guess, it is possible?




On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrote:

Pete Freitag

unread,
Aug 31, 2017, 2:13:05 PM8/31/17
to cfu...@googlegroups.com
On Thu, Aug 31, 2017 at 2:00 PM, <ad...@establishmindfulness.com> wrote:

Pete. I understand what you are saying.

But consider this:

1) If the attacker has access to the form's URL, which he/she must have in order to succeed, the attacker could visit the legitimate form by pasting the form's URL into the browser's address bar, assuming that it is a public form.
For forms behind a login, the attacker could create an account and access the form this way.

2) The attacker would then have access to the form and the hidden form field's token. The attacker could then copy the form's HTML and then add it to a page, visited by the victim.

The attacker would then change the form fields 'acct' and 'amount' values, and the value of the submit button. See below:

&gt;form action="http://bank.com/transfer.do" method="POST"&lt;
&gt;input type="hidden" name="acct" value="MARIA"/&lt;
&gt;input type="hidden" name="amount" value="100000"/&lt;
&gt;input type="hidden" name="csrftoken" value="foobar"/&lt;
&gt;input type="submit" value="View my pictures"/&lt;
&gt;/form&lt;


When the victim visits the page and clicks on the form's spoofed submit button, the form will get posted and pass verification.


No, it should not pass validation because the CSRF token is associated with the current logged in user's session. So the CSRF token that the attacker has generated will be different than the CSRF token that is valid for the victim user.

If you tested this and it found otherwise it could be because you were using the same session, you need to invalidate the session, or better yet test it out with two different browsers one browser simulating the attacker one browser simulating the victim.

Here is a really dumbed down version of how it works omitting the key and forceNew attributes, 


public void function simpleCSRFGenerateToken() {
    if (!structKeyExists(session, "csrfToken")) {
          session.csrfToken = hash(generateSecretKey("AES"), "SHA-512");
    }
    return session.csrfToken;
}

public boolean function simpleCSRFVerifyToken(token) {
    if (!structKeyExists(session, "csrfToken")) {
        return false;
    }
    return compareNoCase(session.csrfToken, arguments.csrfToken) == 0;
}

So as you can see, it is really storing the tokens in the session which means that they are different for each session.

ad...@establishmindfulness.com

unread,
Aug 31, 2017, 2:15:40 PM8/31/17
to Central New York ColdFusion Users Group
Pete. Please forgive me, if you find my line of enquiry, frustrating. But, I really need to understand the 'nuts & bolts', so to speak!

I am sure you are right about all of this, because of your extensive experience in this area, but I need to make sure, I have all the bases covered, before implementing this solution.

Thanks for your patience.



On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrote:

Pete Freitag

unread,
Aug 31, 2017, 2:19:42 PM8/31/17
to cfu...@googlegroups.com
On Thu, Aug 31, 2017 at 2:15 PM, <ad...@establishmindfulness.com> wrote:
Pete. Please forgive me, if you find my line of enquiry, frustrating. But, I really need to understand the 'nuts & bolts', so to speak!

I am sure you are right about all of this, because of your extensive experience in this area, but I need to make sure, I have all the bases covered, before implementing this solution.

Thanks for your patience.

No problem at all, I would suggest trying it out in two browser windows (clear cookies first to ensure you are not sharing session cookies) to make sure you understand it. Feel free to continue asking if you still have questions. 

ad...@establishmindfulness.com

unread,
Aug 31, 2017, 2:28:26 PM8/31/17
to Central New York ColdFusion Users Group
Pete. Light bulb moment!

Now, I understand. So, when you create the token, it is tied to the session.

I will test it, using 2 different browsers, as suggested.

Does, it make that much difference, adding a different key for each form & using the 'forceNew' argument?

Would you recommend this?



On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrote:

Pete Freitag

unread,
Aug 31, 2017, 3:11:25 PM8/31/17
to cfu...@googlegroups.com
On Thu, Aug 31, 2017 at 2:28 PM, <ad...@establishmindfulness.com> wrote:
Pete. Light bulb moment!

Now, I understand. So, when you create the token, it is tied to the session.

Bingo! :-)
 
Does, it make that much difference, adding a different key for each form & using the 'forceNew' argument?

Would you recommend this?

This is debatable, I will point you here for a detailed exploration: https://security.stackexchange.com/questions/22903/why-refresh-csrf-token-per-form-request

I would say if you have a certain form that is sensitive it doesn't hurt to use a unique key/category for it. You might also want to forceNew upon successful login and make sure you call SessionInvalidate upon logout.

ad...@establishmindfulness.com

unread,
Aug 31, 2017, 3:45:51 PM8/31/17
to Central New York ColdFusion Users Group
Pete

I assume, the 'key' argument works like this:

session.csrfToken = hash(arguments.key, "SHA-512")

And the 'forceNew' argument just removes:

if (!structKeyExists(session, "csrfToken")) {
...
}

This overwriting the:

session.csrfToken

Each time.

Is this correct?






On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrote:

Pete Freitag

unread,
Aug 31, 2017, 4:07:04 PM8/31/17
to cfu...@googlegroups.com
On Thu, Aug 31, 2017 at 3:45 PM, <ad...@establishmindfulness.com> wrote:
Pete

I assume, the 'key' argument works like this:

session.csrfToken = hash(arguments.key, "SHA-512")

The above would not be secure because it would generate the same token whenever the key is the same. 

Think of key as a category allowing you to have multiple tokens for different purposes in your app. The generateSecretKey functions is simply used to generate a random string, we still want it to be random.

Here's how it would be implemented in our simple example:

public void function simpleCSRFGenerateToken(key="default", forceNew=false) {
    if (!structKeyExists(session, "csrfTokens")) {
        session.csrfTokens = structNew();     
    }
    if (arguments.forceNew || !structKeyExists(session.csrfTokens, arguments.key) {
        session.csrfTokens[arguments.key] = hash(generateSecretKey("AES"), "SHA-512");
    }
    return session.csrfTokens[arguments.key];
}

public boolean function simpleCSRFVerifyToken(token, key="default") {
    if (!structKeyExists(session, "csrfTokens") || !structKeyExists(session.csrfTokens, arguments.key)) {
        return false;
    }
    return compare(session.csrfTokens[arguments.key], arguments.token) == 0;
}

ad...@establishmindfulness.com

unread,
Aug 31, 2017, 4:24:03 PM8/31/17
to Central New York ColdFusion Users Group
Amazing. Thanks so much for your help.

I fully understand this now!



On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrote:

ad...@establishmindfulness.com

unread,
Aug 31, 2017, 5:10:34 PM8/31/17
to Central New York ColdFusion Users Group
Pete

One last question, if I may?

I want to use the same key and forceNew=true

Initially, when I had more than one form on a page, my first form request verification was failing.

Having read your answer to Kristin's original question, I now understand why.

I submit all my forms using JavaScript.

So, if I was to insert the hidden CSRF token hidden field with a CSRFGenerateToken(key="foo",forceNew=true) value, using jQuery just before the document.someform.submit statement, then only a form, about to submit will receive a token.

Theoretically, the request should always pass verification, because only one token per page will ever be created.

However, what would be the implications of a back button or second browser tab, with this approach?





On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrote:


ad...@establishmindfulness.com

unread,
Aug 31, 2017, 6:13:27 PM8/31/17
to Central New York ColdFusion Users Group

I am kind of attempting to answer my own question here, but it might be of some use, to anyone, who is searching for answers on this topic, in future.

But, feel free to correct me on any of the following thoughts.

I am using a 'js.cfm' file, so that I can use CFML inside my JavaScript statements.

After the token field has been inserted into the DOM, the form is guaranteed to submit. Because the submission is guaranteed to request a new page, when the new page returns to the client, the token field will have been destroyed. This guarantees that only one token field per page will ever be created.

Theoretically the back button should not cause an issue, because even if the token field is retained (although I am not even sure whether DOM elements created by JavaScript are cached), if form 1 is resubmitted, my JavaScript checks to see if the token field exists. So, it will never duplicate the token field in the same form. If form 2 is submitted, then a new field with the same token will be inserted into form 2, so the token should still pass verification.

As for a second tab. This poses a problem, because tab 2 will overwrite tab 1's token, and thus tab 1's form request will no longer verify. One possible solution is to use AJAX to create a new token, just before inserting the value returned into the token field, and inserting the token field into the DOM. In this way, multiple tabs will share the same token.

The drawback of this approach, is the performance loss incurred waiting for the AJAX response. And it would also require a callback to insert the token field and then submit the form.

To be honest, it all gets a bit complicated, and vulnerable to bugs developing.

So, I have decided not to use 'forceNew=true'.




On Tuesday, October 14, 2014 at 3:03:41 PM UTC+1, Kristin wrote:

qtap...@gmail.com

unread,
Feb 5, 2019, 8:34:19 PM2/5/19
to Central New York ColdFusion Users Group
Hello,

I know this is an old thread.  But I am facing similar issues working with CF's csrfGenerateToken() and csrfVerifyToken() .  I have raised a question in StackOverflow https://stackoverflow.com/questions/54498116/coldfusion-csrf-xss-attack-prevention  I think maybe someone here maybe able to help me out :)

Basically, I signed in the site already and then copy the form.  The form has a unique token every time the form is displayed.  I copy the form as an HTML and save it somewhere, changed the values etc. then open it.  The only thing that is displayed is the submit button, I simply click on it and whatever change I made outside the site gets updated to the site!  Is this supposed to happen?  I then navigate away from the form then went back in.  At that time a different token should have been created.  Surprisingly, I was able to still use the same form and submitted a different value with the same old token I got the first time and updated the fields!  Any insight is appreciated.
Reply all
Reply to author
Forward
0 new messages