ColdFusion CSRF XSS attack prevention - coldfusion

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="test#test.com">
<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 test2#test.com instead of test#test.com 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.

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

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.

Related

Variable session passes from page to page coldFusion

CFBuilder admin storage
15cdb5dcb6.jpg
Application.cfm
34ed7586e1.jpg
Login.cfm
<cfif not isDefined('FORM.submitButton')>
<cfform name="loginForm" method="post" action="#CGI.SCRIPT_NAME#">
Login:
<cfinput type="text" name="login" required="yes">
Password:
<cfinput type="password" name="password" required="yes">
<br>
<cfinput type="submit" name='submitButton' value="Sign">
<br>
<cfinput type="button" name='registerButton' value="Register">
</cfform>
<cfelse>
<cfquery name='getUser' datasource="dbfortest">
SELECT * FROM usertable WHERE login="#FORM.login#" ;
</cfquery>
<cfif getUser.RecordCount NEQ 0>
<cfif FORM.password eq getUser.password>
<cflock scope="Session" timeout="60" type="exclusive" >
<cfset Session.loggedIn = "yes">
<cfset Session.user = "#FORM.login#">
</cflock>
<cfoutput>#StructKeyList(Session)#</cfoutput>
<cfelse>
Your pass isn't correct.
</cfif>
<cfelse>
There is no user with this name.
</cfif>
</cfif>
part of page when i want to use login including.
<cfif Session.loggedIn eq "no">
<cfinclude template="login.cfm">
</cfif>
<cfif structKeyExists(session, "user")>
<cfoutput>Welcome, #Session.user#.</cfoutput>
</cfif>
<cfoutput>#StructKeyList(Session)#</cfoutput>
Hello everyone, please help me understand these sessions' behavior.
The whole problem consists in attempting to pass variables from one page to another.
So after login i don't see the session.user in session struct.
How can i pass this?
Have already tried different browsers.
#Aquitaine has given you some good information. I just wanted to also point out that another part of your problem is likely that you have set a 10 second life span for your sessions. That's probably not long enough.
In the Application.cfm example that you posted you have this line:
sessiontimeout="#createTimespan(0,0,0,10)#"
The arguments for the CreateTimeSpan function are as follows:
createTimespan(days, hours, minutes, seconds)
As such you are assigning a 10 second lifespan for sessions. Perhaps you meant to set 10 minutes instead of 10 seconds.
To figure out what's going on with the session variables, try putting in some debug code right after your cfset session statements to make sure that they're happening. Maybe <cfdump var="#session#">.
You do not need to cflock your session scope (and have not needed to since CFMX). See Adam Cameron's 2013 post on when to lock scopes
If your debug code runs and you see the session variables, but then they're gone on the next page, that may be an issue with your session storage (which is a different part of cfadmin) or else whatever front-end webserver you're using. Try <cfdump var="#session#"> in onRequestStart in Application.cfc and make sure that JSESSIONID is the same on every request. (or try disabling J2EE session variables in CFADMIN and see if the same problem persists with CFID/CFTOKEN).
If your debug code doesn't run, then you should be seeing one of your error conditions.
For ease-of-reading, be consistent in your casing when refering to scopes, e.g. session not Session. While this kind of thing may not matter functionally, it can get you into trouble with portability when referencing paths or components.
Some other issues:
If you are going to use a boolean value for loggedIn then use a boolean value: true or false or 1 or 0 or (if you must) yes or no but not "yes" which is a string; instead of being able to do if (session.loggedIn) if you will have to do if (session.loggedIn == 'yes') and nobody will be happy.
If this is meant to be working, production site code, at a minimum you need to be using cfqueryparam as you do not ever want to pass unescaped user input directly to a database query.
You might also head over to the CFML slack at cfml.slack.com and ask on #cfml-beginners for some pointers on writing login forms.

ColdFusion Dropbox - How to get the token from the response URI

I am trying to implement the OAuth with Dropbox from a ColdFusion application, and I managed how to call the Dropbox method to generate the access token, but... I don't know how to get the generated TOKEN from the response URI. I am getting something like this from Dropbox:
http://localhost/dropbox/generate_token.cfm#access_token=AAAAAAAAYVM_XdCYlbTz0gQOwQkWlg6TDXf84_5h4giikg6J-7Man&token_type=bearer&uid=267693&account_id=dbid%3AAABeDMm-BN0n1DofLZz9kPZAipnQ
How to I retrieve the URL variables in this case? I mean if I do a
<cfdump var="#URL#">
I am getting an empty struct. If I do a
<cfdump var="#CGI#">
I still don't see any of the URL retrieved parameters in the structure. How do I get the variables and their values from the Dropbox response?
UPDATED
At some point I thought I found a way to read the URL but now - for no reason - this doesn't work anymore! I didn't change anything but the solution below doesn't work anymore.
I can read the full URL with JavaScript using document.location but this means to do an extra submit to a ColdFusion page and I don't want to do this. I want to get the Dropbox token from the URL and save it to the database directly in this page...
Any new ideas please?
SOLUTION THAT SEEMED TO WORK AT SOME POINT ...
I found a way to get the URI string using this:
<cfset objRequest = GetPageContext().GetRequest().getParameterMap() />
<cfdump var="#objRequest#">
<cfoutput>
<cfloop collection="#objRequest#" item="i">
<p>
#i# - #objRequest[i][1]#
</p>
</cfloop>
</cfoutput>
From now on, I know how to get the values returned by Dropbox.
I found a way to get the returned parameters by reading the browser URL with JavaScript, so in two steps: first, parse and extract the full URL including the part after the # sign (I found this has a name and it is called the "URL fragment") and second, create a JavaScript form with parsed parameters and resubmitted to the server. Here is the code:
<cfparam name="FORM.action" default="">
<cfif FORM.action IS "save_token">
<cfdump var="#FORM#">
<cfelse>
<form name="main" id="main" method="post">
<input type="hidden" name="action" id="action" value="save_token">
</form>
<script type="text/javascript" language="javascript">
<!--
var parameters = window.location.hash.substr(1).split("&");
function addHidden(theForm, key, value) {
// Create a hidden input element, and append it to the form:
var input = document.createElement("input");
input.type = "hidden";
input.name = key;
input.value = value;
theForm.appendChild(input);
}
// Form reference:
var theForm = document.forms["main"];
for (var i=0; i<parameters.length; i++) {
// Add data:
addHidden(theForm, parameters[i].split("=")[0], parameters[i].split("=")[1]);
}
theForm.submit();
//-->
</script>
</cfif>

How to perform submit button function in ColdFusion?

I am new to coldfusion ,
please check my code below
<cfif isDefined("form.submit")>
<cfoutput>
<h3>hi</h3>
</cfoutput>
</cfif>
<cfform action="#CGI.SCRIPT_NAME#">
User Name:<cfinput type="Text" name="usr_nm"><br>
<cfinput type="Radio" name="access_flg" value="0">Admin
<cfinput type="Radio" name="access_flg" value="1">User</br>
<cfinput type="submit" name="submit" value="submit"><br>
</cfform>
But ,When I am clicking submit button ,I am expecting result as hi
I haven't see hi message, Is there any thing wrong in my code ,Any one please help me
Since you're new to ColdFusion, I'll give you some advice straight away:
1. Do not submit a form to the same page.
Submit the form to a separate page for processing. Reason being, as you get into more advanced applications, you'll need to restrict pages/URLs to only respond to an appropriate HTML Verb.
Your form page should respond to HTTP GET.
Your form processing page should only respond to HTTP POST.
2. Do not use CFFORM.
The function of CFFORM is to create JavaScript validation and server-side interactions. This can easily be done with modern JavaScript libraries like
https://jquery.com/
http://jqueryvalidation.org/
3. Give your form elements an ID, as well as a NAME.
This allows easier reference to the form elements when using JavaScript.
4. Do not name your submit button "submit".
If you ever want to use JavaScript to submit a form, the function is submit().
For example: $('#myForm').submit();
Having a form element named the same as a function will cause errors.
Here's my_form.cfm:
<form id="myForm" name="myForm" action="my_form_action.cfm" method="post">
User Name:<input type="Text" id="usr_nm" name="usr_nm"><br>
<input type="Radio" id="access_flg_0" name="access_flg" value="0">Admin
<input type="Radio" id="access_flg_1" name="access_flg" value="1">User</br>
<input type="submit" id="my_form_submit" name="my_form_submit" value="Submit"><br>
</form>
5. You don't need to use CFOUTPUT unless you are rendering data from the server.
Here's my_form_action.cfm:
<cfif structKeyExists(form, "my_form_submit")>
<h3>Hi!<lt>
</cfif>
Even better:
<cfif (cgi.request_method IS "post") AND (structKeyExists(form, "my_form_submit"))>
<h3>Hi!<lt>
</cfif>
This is an elaboration of this part of Adrian's answer:
<cfif (cgi.request_method IS "post") AND (structKeyExists form, "my_form_submit"))>
<h3>Hi!</h3>
</cfif>
This is a candidate for code re-use. In one of our applications, I wrote a custom tag that does something like this:
if (StructKeyExists(attributes, 'ScopeToCheck') is false)
attributes.ScopeToCheck = "form";
if (StructKeyExists(caller, attributes.ScopeToCheck) is false)
Redirect = true;
else if (StructIsEmpty(caller[attributes.ScopeToCheck]) is true)
Redirect = true;
else
Redirect = false;
if (Redirect == true)
location(somewhere, false);
The custom tag approach was appropriate for my situation. For other situations, the same logic can be put into a udf that returns either true or false. Then the calling page can decide what to do with that information.

How can I include CSRFProvider in my existing site?

\edited: added creation of instance
I want to protect a site against CSRF. But because we're on CF9, I cannot use CSRFGenerateToken(). So I've done a little bit of research and found this: CSRFProvider.
It's a cfc providing protection against CSRF attacks.
My question is, how can I include it in my site? I want to use the 'hidden-forms' method, which is explained as followed:
// Writes a hidden form field to your view, you must pass an 'intention' which should be unique per form, per application
#csrf.renderToken(intention="my_unique_form_name")#
// On form submission, the application must verify the token using the same 'intention'
validSubmission = csrf.verifyToken(intention="my_unique_form_name", token=form._token);
I have copied the cfc in the apllications directory and created a form inside test.cfm with a hidden field:
<cfset csrf = new CSRFProvider()>
<cfform method="post" action="test2.cfm" name="qwertz">
<cfinput name="csrftoken" type="hidden" value="#csrf.renderToken(intention="qwertz")#">
<cfinput name="whatever" type="text" > <br/>
<cfinput name="Submit" type="submit" value="blah"> </cfform>
And in test2.cfm:
<cfif validSubmission = csrf.verifyToken(intention="qwertz", token=form._token); >
<p>valid</P>
<cfelse>
<p>nope</P
</cfif>
When I'm trying to open the site, it always tells me:
The method renderToken was not found in component [actual_path_to_my_applycation]/CSRFProvider.cfc.
Thanks in advance!

Login System in ColdFusion 9

I am working with a very old login system that my company used before on a website that used frames.
Before, when someone tried a wrong user/pass combination the frame would load a simple cfinclude file with the login form and an error message on top of it.
Now I am using a form in a popup window that calls the application.cfc but instead of getting the error message back on my popup window the page load the cfinclude file from the application component to a new page.
So I need a few things to happen for this application. First, I need the initial popup window to stay up and the page should not submit if the combination of user/pass is wrong, and finally I need the error message to appear somewhere on the popup.
If anyone did something like this before I would really appreciate your feedback.
This is a partial of my code:
Login Form:
<!--- loginErrMsg display - to tell why login is denied --->
<cfif isdefined("loginErrMsg")><span style="color:red">#loginErrMsg#</span><br /></cfif>
<form name="LoginForm" id="LoginForm" action="<cfif test is false>https://secure.example.com</cfif>#loginFormAction#" method="post" target="_top">
</cfoutput>
<input type="hidden" name="loginPost" value="true">
<p>
Login below to take advantage of the great services we offer:
</p>
E-mail:<input name="j_username" class="loginform" type="text" size="30" maxlength="50" id="j_username">
Password: <input name="j_password" type="password" size="30" maxlength="16" class="loginform">
<br />
<input type="submit" name="btn" value="Submit" class="bluebuttonnormal">
</form>
Application.cfc Code:
<cflogin applicationtoken="swmadmin">
<cfif NOT IsDefined("cflogin")>
<cfinclude template="login.cfm">
<cfabort>
<cfelse>
<cfquery name="userlookup" datasource="#ds#">
SELECT clientadminID, roles, isFaxOnly, acctEnabled FROM clientadmin
WHERE
username=<cfqueryparam value="#cflogin.name#" CFSQLTYPE="CF_SQL_VARCHAR" maxlength="50">
and password=<cfqueryparam value="#cflogin.password#" CFSQLTYPE="CF_SQL_VARCHAR" maxlength="16">
</cfquery>
<cfif userlookup.recordcount eq 0>
<cfset loginErrMsg = "Invalid login.">
<cfinclude template="login.cfm">
<cfabort>
</cflogin>
I am working with a very old login system that my company used before
on a website that used frames.
If this is a new website, don't use it. Login forms are a dime a dozen and can be done in your sleep. Start fresh and do it right.
So I need a few things to happen for this application. First, I need
the initial popup window to stay up and the page should not submit if
the combination of user/pass is wrong, and finally I need the error
message to appear somewhere on the popup.
You're going to want to use an AJAX solution here, either write your own or use a good library like jQuery. Once you check the login values you can use jQuery or simple javascript to unhide or update the innerHTML of an empty element to display your error message.
<cflogin ...>
...
</cflogin>
CFLogin makes me sad. Another one of ColdFusion's tags meant to simplify something commonly done that doesn't really help much and sacrifices flexibility. You can get far more control over your application without it. instead of CFLogin, try something like this pseudo code
<cfcomponent>
<cffunction name = "onRequest" ...>
<cfargument name="targetPage" type="String" required = "true" />
<cfif !structKeyExists(session, "roles") and !findNoCase("loginHandler.cfm",cgi.script_name)>
<!--- notice I prevent the redirect on the form handler, otherwise the ajax will get redirected to the login.cfm page --->
<cfinclude template = "login.cfm">
<cfelse>
<cfinclude template = "#arguments.targetPage#">
</cfif>
</cffunction>
</cfcomponent>
Your login.cfm would then contain your form but your button would fire something like jQuery.post() to "loginHandler.cfm", then depending on the result of the login, your callback function may use jQuery.html() to display the error or window.location.replace / window.location.href if the login was successful. Of course, in the event of a successful login, your ColdFusion page would have to create their session variables and do whatever else you want it to do before sending the result back to your AJAX call.