Jack
Jack

Reputation: 863

ColdFusion CSRF XSS attack prevention

I have implemented the csrfGenerateToken and csrfVerifyToken functions in trying the prevent a CSRF attack.

I have set up the webpage header with these:

set X-XSS-Protection "1; mode=block"
always set X-Frame-Options SAMEORIGIN
X-Content-Type-Options nosniff
Strict-Transport-Security "max-age=63072000" env=HTTPS

I have done some research and proceed as recommended by Adobe doing something like this:

<cfset tokenVar = 't' & lCase( hash( createUUID() & randRange( 1000, 100000 ), 'MD5', 'UTF-8', 250 ) )>
<form action="updateprofile.cfm" method="post">
    <input type="hidden" name="f#hash( 'tokenVar', 'SHA-256', 'UTF-8')#" value="#tokenVar#">
    <input type="hidden" name="f#hash( 'formToken', 'SHA-256', 'UTF-8')#" value="#csrfGenerateToken( tokenVar, true )#">
    <input type="text" name="emailAddress" value="#EmailAddress#">
    <input type="text" name="phoneNumber" value="#PhoneNumber#">
    <input type="submit" name="btnSubmit" value="Change Profile Info">
</form>

Updateprofile.cfm would have:

<cfparam name="FORM[ 'f' & hash( 'tokenVar', 'SHA-256', 'UTF-8')]" default="0">
<cfparam name="FORM[ 'f' & hash( 'formToken', 'SHA-256', 'UTF-8')]" default="0">
<cfif not csrfVerifyToken( FORM[ 'f' & hash( 'formToken', 'SHA-256', 'UTF-8')], FORM[ 'f' & hash( 'tokenVar', 'SHA-256', 'UTF-8')] )>
    <!--- CSRF attack. Clear cookies and kick user out --->
    <cfinclude template="clearcookies.cfm">
    <cflocation url="signin.htm" addToken = "no">
</cfif>

This will work if 2 accounts are signed in on the same browsers and if one tries to update the other. However, when I simply saved a copy of the resulting html from one of them and save it as csrf-attack.htm locally:

<html><body>
<script>history.pushState('', '', '/')</script>
<form action="http://www.exsample.com/updateprofile.cfm" method="post">
    <input type="hidden" name="f41BE6B4E09CBA69BDB76DBB69B493E8D49F5DD9ED230085913397B4C751D4E60" value="t93315a7c3ecb43d4d1b9422da97ffb09">
    <input type="hidden" name="f08DFC2607D4119D7B16B4C01DC5C00F54B044DC937257ABC411F9A7E55BB4191" value="A0EED67C55F5E17683E2E1B21FF3454FE690E0B1">
    <input type="text" name="emailAddress" value="[email protected]">
    <input type="text" name="phoneNumber" value="1-222-3333">
    <input type="submit" name="btnSubmit" value="Change Profile Info">
</form>
</body><html>

I processed the original form to update the phone number to 1-333-4444. Then I came back to the form again. At this time a new CSRFToken should have been created because ForceNew was set to true.

Then I go to the static HTML page that I have saved, and simply changed the value of the email address to [email protected] instead of [email protected] with the old token, then clicked the "Change Profile Info" button, I was able to update it to the site!!

Am I doing something wrong or is it how it works? It seems that the token is useless if I can simply copy the token values and manipulate the content, then post it. How can I mitigate issue like this on the receiving page?

Thanks in advance.

Upvotes: 4

Views: 1632

Answers (2)

charlie arehart
charlie arehart

Reputation: 6884

Jack, 2 points:

First, things will seem to "not protect anything" if your requests (including that html page) are made from the same browser, thus using the same session cookies for the site, thus using the same cf session.

The generated token is saved in the cf session for that user/browser (saved in a way not visible with a cfdump of the session). And the verify is then checking the passed-in token against that. But if you run the "other" code in another browser, it would NOT share the same session (unless you also copied in the user's cookies).

Second, even if you did duplicate the session cookies, the value in that csrf token field (simulating being grabbed by the bad guy who saw the form and "copied it off" to that other page) will be the value that was created when THEY did that...not the value that a real user would have in their session if they'd been to the page that did the csrfgeneratetoken. So the tokens won't match.

Make sense? It's easy to misconstrue what this is does and does not do, and just as easy to get tripped up setting up a demo prove if it "works", if you don't keep all this in mind.

And I may be lacking in my answer, so am open to comments and criticism.

Upvotes: 0

Pete Freitag
Pete Freitag

Reputation: 1031

The csrfVerifyToken result will still pass until you generate another CSRF token with the same key, at that point it will get invalidated. So if you are making single use tokens then you need to invalidate the old token by calling csrfGenerateToken with the same tokenVar after you call csrfVerifyToken

I wrote up a blog entry with a code example to illustrate this: https://www.petefreitag.com/item/856.cfm

Upvotes: 2

Related Questions