Here's another little thing that's come across my desk and I hope my solution can be of some help to others.
Continue reading →
I was asked to provide a solution to protect a site from Cross Site Request Forgery (CSRF or XSRF) attacks. Specifically, there were forms on the site that were considered at-risk for exploitation. I won't get into great detail on what CSRF is, but basically it's an attack vector that attempts to exploit the trust that a website has in the user's browser to allow malicious actions to take place on the server.
CF10 has methods for CSRF protection available built in, but the site I was asked to chime in on is on CF9.
So, the forms were vulnerable to a CSRF attack because a malicious user could cause a legitimate, authenticated user to unwittingly submit them. In order to protect the public facing forms, it's necessary to set up a condition where the server can trust that the form submission is from the legitimate user during their session. The method CF10 uses is to send a token with the form and check for it on submission. We can do the same thing by adding just a few lines of code to our forms.
Here's a typical self-posting form:
<cfif structKeyExists(form, 'sendInfo')>
<p>Your information has been received.</p>
<cfelse>
<cfform>
<label for="fName">First name:</label><cfinput name="fName" type="text"><br>
<label for="lName">Last name:</label><cfinput name="lName" type="text"><br>
<input type="submit" value="Submit" name="sendInfo">
</cfform>
</cfif>
Let's add a token to the form that the server can check for:
<cfif structKeyExists(form, 'sendInfo')>
<p>Your information has been received.</p>
<cfelse>
<cfset session.foobar = createUUID()>
<cfform>
<cfoutput><input type="hidden" name="foobar" value="#session.foobar#"></cfoutput>
<label for="fName">First name:</label><cfinput name="fName" type="text"><br>
<label for="lName">Last name:</label><cfinput name="lName" type="text"><br>
<input type="submit" value="Submit" name="sendInfo">
</cfform>
</cfif>
Next, we want the form processor to check for the token:
<cfif structKeyExists(form, 'sendInfo')>
<cfif structKeyExists(session, 'foobar') and form.foobar is session.foobar>
<p>Your information has been received.</p>
<cfelse>
<p>The form has a problem</p>
</cfif>
<cfelse>
<cfset session.foobar = createUUID()>
<cfform>
<cfoutput><input type="hidden" name="foobar" value="#session.foobar#"></cfoutput>
<label for="fName">First name:</label><cfinput name="fName" type="text"><br>
<label for="lName">Last name:</label><cfinput name="lName" type="text"><br>
<input type="submit" value="Submit" name="sendInfo">
</cfform>
</cfif>
And finally, we want to do a little housekeeping by dropping the token from the session when we're done with it:
<cfif structKeyExists(form, 'sendInfo')>
<cfif structKeyExists(session, 'foobar') and form.foobar is session.foobar>
<p>Your information has been received.</p>
<cfelse>
<p>The form has a problem</p>
</cfif>
<cfset structDelete(session, 'foobar')>
<cfelse>
<cfset session.foobar = createUUID()>
<cfform>
<cfoutput><input type="hidden" name="foobar" value="#session.foobar#"></cfoutput>
<label for="fName">First name:</label><cfinput name="fName" type="text"><br>
<label for="lName">Last name:</label><cfinput name="lName" type="text"><br>
<input type="submit" value="Submit" name="sendInfo">
</cfform>
</cfif>
So there ya' go. A handful of code and you've got your server recognizing your forms for a one-time post and bouncing them on any attempted re-posting.