Creating a login system using Coldfusion - coldfusion

Im trying to create an admin area but cannot get a login system to work. I have three parts to this. The login.cfm page, login_action.cfm and the app.cfc.
When using this code, and try and login it only stays on the same page.
Login.cfm
<form name="fLogin" id="fLogin" action="<cfoutput>#FormAction#</cfoutput>" method="post">
<label>Username:</label>
<input type="text" name="username" required>
<label>Password:</label>
<input type="password" name="Password" required>
<br>
<input type="submit" id="sub" value="Login">
</form>
Login_action.cfm
<cflogin idletimeout="1800">
<!--- SETS the action page of the login form to whatever
page the user was trying to go to. Since the login
will actually be processed in the application.cfm file
(or a template included in it), then the FORM action
is the page that will be loaded after the login has
been completed. --->
<!--- IF there IS NOT a Query String passed in the URL,
only the requested page name is used --->
<cfif CGI.QUERY_STRING IS "">
<cfset FormAction = #CGI.SCRIPT_NAME#>
<cfelse>
<cfset FormAction = "#CGI.SCRIPT_NAME#?#CGI.QUERY_STRING#">
</cfif>
<cfif not (isDefined("cookie.XXXX.email"))>
<cfif NOT (IsDefined ("Form.username") AND IsDefined ("Form.Password"))>
<cfinclude template="login.cfm">
<cfabort>
<cfelse>
<cfif IsDefined("Form.username")>
<cfset username = #Form.username#>
</cfif>
<CFQUERY NAME="login">
select * from t_admin where username = '#username#'
</CFQUERY>
<cfif login.RecordCount gt 0>
<cfif #Form.password# eq "#login.password#">
<cfloginuser name="#username#" password="#login.password#" roles="admin">
<cfcookie name="XXXX.email" value="#login.email#" expires="never" >
<cfset session.userId = #login.id#>
<cfelse>
<cfset Invalid = "Yes">
<cfinclude template="login.cfm">
<cfabort>
</cfif>
</cfif>
</cfif>
<cfelse>
<CFQUERY NAME="cookielogin">
select * from t_admin where email = '#cookie.XXX.email#'
</CFQUERY>
<cfloginuser name="#cookielogin.email#" password="#cookielogin.password#" roles="admin">
<cfset session.userId = #cookielogin.id#>
<cfset session.email = #cookielogin.email#>
</cfif>
</cflogin>
and Application.cfc.
<cffunction name="OnRequestStart">
<cfinclude template="login_action.cfm">
<cfif isDefined ('cookie.XXXX.email')>
<cfset session.email = cookie.XXXX.email>
</cfif>
</cffunction>
If anyone could help that'll be great thanks

There are quite a lot of fundamental mistakes in this code.
Don't build an URL with the query string that came from the browser. You cannot trust anything that comes from the browser, therefore you must not use anything without scrubbing it down. Things like this are asking for trouble:
<cfset FormAction = "#CGI.SCRIPT_NAME#?#CGI.QUERY_STRING#">
Generally don't ever write anything to the HTML of your page that came from the user without properly sanitizing and HTML-encoding it. Use HTMLEditFormat() and URLEncodedFormat() extensively.
Don't ever use user-supplied values to build an SQL string. There is <cfqueryparam>, use it. This is bad and wrong:
select * from t_admin where username = '#username#'
While we're at it: Don't ever use select * in production code.
Don't ever store plain text passwords in a database. This is a big thing, you really must fix that before you do anything else. ColdFusion provides a number of hashing algorithms, use them (and read about salted hashes).
Is the login form sent through HTTPS? (Everything in that application should probably be HTTPS, but the login form absolutely must be.)
Login cookies should be marked as secure and httponly (see) to prevent session hijacking.
Login cookies that expire never might be not a good idea. Depends.

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.

If Else query is empty

I am using CFLDAP to have users get authenticated using active directory. I am trying to write an if statement in case the users information does not come back as authenticated. I thought I could check by using <cfif AuthenticateUser.RecordCount gt 0> which is working as long as the information is correct but if the wrong information is entered and nothing is authenticated it is not running the else statement. Any help with this would be greatly appreciated!
<cfldap action="query"
name="AuthenticateUser"
attributes="dn,mail,givenname,sn,samaccountname,memberof"
start="DC=domain,DC=net"
filter="(&(objectclass=user)(samAccountName=#trim(form.user_name)#))"
server="servername"
Port="389"
username="tc\#trim(form.user_name)#"
password="#trim(form.user_pass)#">
<cfoutput>#AuthenticateUser.RecordCount#</cfoutput>
<!--- Get all records from the database that match this users credentials --->
<cfquery name="userVerify" datasource="test">
SELECT *
FROM dbo.Users
WHERE user_name = <cfqueryparam value="#AuthenticateUser.samaccountname#" cfsqltype="cf_sql_varchar" />
</cfquery>
<cfif AuthenticateUser.RecordCount gt 0>
<!--- This user has logged in correctly, change the value of the session.allowin value --->
<cfset session.allowin = "True" />
<cfset session.employee_number = userVerify.employee_number />
<!--- Now welcome user and redirect to "index.html" --->
<script>
self.location="../dashboard/dashboard.cfm";
</script>
<cfelse>
<!--- this user did not log in correctly, alert and redirect to the login page --->
<script>
alert("Your credentials could not be verified, please try again!");
self.location="Javascript:history.go(-1)";
</script>
</cfif>
I have also tried: <cfif len(AuthenticateUser)>
This is how I do it. I try to run a query against our domain using the supplied username and password. If the supplied username and password are not valid, an error is generated.
<cftry>
<cfldap action="Query"
name="ADResult"
attributes="dn"
start="DC=domain,DC=net"
filter="sAMAccountName=administrator"
server="servername"
scope = "subtree"
username="#arguments.username#"
password="#arguments.password#" />
<cfset isAuthenticated = true />
<cfcatch type="any">
<cfset isAuthenticated = false />
</cfcatch>
</cftry>
<cfreturn isAuthenticated />
I wrap this up in a function called "authenticate" and expose it via a web service that I call from my apps. If I then need additional details about the user (mail, givenName, etc), I have another function in the same web service that I will call after I am sure the user has been authenticated. Note that in this other function I'm using my administrator username and password to run the query.
<cfldap action="Query"
name="ADResult"
attributes="mail,givenName"
start="DC=domain,DC=net"
filter="sAMAccountName=#arguments.username#"
server="servername"
scope = "subtree"
username="administrator"
password="myAdminPassword" />
I take the results of this, populate a query object or a structure, and return that to the calling function.
So the entire process sort of looks like this:
<cfset objAD = createobject("webservice", "http://mywebservice.com") />
<cfset isAuthenticated = objAD.authenticate(form.username, form.password) />
<cfif isAuthenticated>
<cfset userDetails = objAD.getUserDetails(form.username)>
</cfif>
Hope this helps.
This is a formatted comment. You are trying to do too much at once. Go one step at a time. Start with this:
<cfdump var="before cfldap tag<br />">
<cfldap action="query"
name="AuthenticateUser"
etc
>
<cfdump var="after cfldap tag<br />">
<cfdump var = "#AuthenticateUser#">
<cfdump var="after cfdump<br />">
Run this code with both valid and not valid credentials. Look at what you get. React accordingly.
I think it throws an error when the query fails. Try this:
<cftry>
<cfldap action="query"
name="AuthenticateUser"
attributes="dn,mail,givenname,sn,samaccountname,memberof"
start="DC=domain,DC=net"
filter="(&(objectclass=user)(samAccountName=#trim(form.user_name)#))"
server="servername"
Port="389"
username="tc\#trim(form.user_name)#"
password="#trim(form.user_pass)#">
<cfset LoginStatus = "Success">
<cfcatch type="any">
<cfset LoginStatus = "Failed">
</cfcatch>
</cftry>
Then your cfif would be something like this:
<cfif LoginStatus eq "Success">
<!--- This user has logged in correctly, change the value of the session.allowin value --->
<cfset session.allowin = "True" />
<cfset session.employee_number = userVerify.employee_number />
<!--- Now welcome user and redirect to "index.html" --->
<script>
self.location="../dashboard/dashboard.cfm";
</script>
<cfelse>
<!--- this user did not log in correctly, alert and redirect to the login page --->
<script>
alert("Your credentials could not be verified, please try again!");
self.location="Javascript:history.go(-1)";
</script>
</cfif>
I think this works on CF9.

ColdFusion Link to Previous Page Clears only that Page Session Variables

I have a page (form) set up like this:
<cfif not structKeyExists(session, "checkout")>
<cflocation url="ownerInfo.cfm" addToken="false">
</cfif>
<cfif not structKeyExists(session.checkout, "vehicle")>
<cfset session.checkout.vehicle = {ownership=""}
<cfparam name="form.ownership" default="#session.checkout.vehicle.ownership#">
<cfif structKeyExists(form, "submit")>
<cfset errors = []>
<cfif not arrayLen(errors)>
<cfset session.checkout.vehicle = {ownership=form.ownership}
<cflocation url="ownerCheck.cfm" addToken="false">
</cfif>
</cfif>
I am trying to figure out how I can reset this form by having a link on another page that when this page is linked back to it will reset all the session variables to null making the entire page needing to be filled out again.
This is what I have tried but am unsuccessful.
<cfif session.checkout.vehicle.ownership != null />
<cfset session.checkout.vehicle.ownership = null />
</cfif>
I cannot use <cfset StructClear(Session)> because I do not want all the session variables cleared from the previous pages only want this page to reset.(Not All Pages or All Session Variables). Any help with this would be greatly appreciated!
You can do using structDelete(structure,key)
<cfif session.checkout.vehicle.ownership != null />
<cfset structDelete(session.checkout.vehicle,'ownership ')>
</cfif>
If you want to clear the session scope variables if the ownership key already exists in the session scope then you can do:
<cfif structKeyExists(session.checkout.vehicle, "ownership")>
<!--- struct key exists so delete it --->
<cfset structDelete(session.checkout.vehicle, "ownership")>
</cfif>
You can actually just do:
<cfif structKeyExists(session.checkout, "vehicle")>
<!--- try and delete ownership key might not exist --->
<cfset structDelete(session.checkout.vehicle, "ownership")>
</cfif>
As long as the parent scope exists, you can try and delete the ownership key without first checking that it exists.
If you want to know if the key did exist then structDelete accepts a 3rd boolean parameter so it'll return true if it did exists and false if it didn't.
<cfset didExist = structDelete(session.checkout.vehicle, "ownership", true)>
An alternative approach to solving your problem would be to reset the form if it's not a form (POST) submission. So you'd do:
<cfif structKeyExists(form, "submit")>
<!--- form has been submitted store values in session and redirect... -->
<cfelse>
<!--- form not submitted so clear the session vars... --->
</cfif>

Is the CFDUMP tag modifiable?

With ColdFusion MX7 if we encounter an exception we send an email to the development team containing dumps of the various data scopes including the form structure.
This works great for debugging except in the case of an error when the user logs in. We end up getting the password printed out.
So, the question is, is there a way to modify the CFDUMP file so that it filters the password value out of the form object?
Naturally we could put it in the same code that sends the email, however it would be ideal to put it in the CFDUMP file so that we do not have to worry about it showing up in other spots.
I have located the CFDUMP file and it seems to be binary, so I'm guessing we can't do it.
You can copy the dump.cfm file to dumporiginal.cfm, and then make a new dump.cfm that calls dumporiginal.cfm.
<!---
So that it won't execute twice if you
have a closing slash (<cfdump ... />)
--->
<cfif thisTag.executionMode neq "start">
<cfexit method="exitTag" />
</cfif>
<!---
defaults for optional attributes, taken from the docs
http://livedocs.adobe.com/coldfusion/8/htmldocs/Tags_d-e_08.html
--->
<cfparam name="attributes.expand" default="yes" />
<cfparam name="attributes.format" default="html" />
<cfparam name="attributes.hide" default="all" />
<cfparam name="attributes.keys" default="9999" />
<cfparam name="attributes.label" default="" />
<cfparam name="attributes.metainfo" default="yes" />
<cfparam name="attributes.output" default="browser" />
<cfparam name="attributes.show" default="all" />
<cfparam name="attributes.showUDFs" default="yes" />
<cfparam name="attributes.top" default="9999" />
<!--- Hide the password, but store its value to put it back at the end --->
<cfif isStruct(attributes.var) and structKeyExists(attributes.var, 'password')>
<cfset originalPassword = attributes.var.password />
<cfset attributes.var.password = "{hidden by customized cfdump}"/>
</cfif>
<!---
Call the original cfdump.
Which attributes you pass depends on CF version.
--->
<cfswitch expression="#listFirst(server.coldfusion.productVersion)#">
<cfcase value="6">
<cfdumporiginal
var = "#attributes.var#"
expand = "#attributes.expand#"
hide = "#attributes.hide#"
label = "#attributes.label#"
>
</cfcase>
<cfcase value="7">
<cfdumporiginal
var = "#attributes.var#"
expand = "#attributes.expand#"
hide = "#attributes.hide#"
label = "#attributes.label#"
top = "#attributes.top#"
>
</cfcase>
<cfdefaultcase>
<cfdumporiginal
var = "#attributes.var#"
expand = "#attributes.expand#"
format = "#attributes.format#"
hide = "#attributes.hide#"
keys = "#attributes.keys#"
label = "#attributes.label#"
metainfo = "#attributes.metainfo#"
output = "#attributes.output#"
show = "#attributes.show#"
showUDFs = "#attributes.showUDFs#"
top = "#attributes.top#"
>
</cfdefaultcase>
</cfswitch>
<!--- Restore the password, in case it's read after cfdump call --->
<cfif isDefined("originalPassword")>
<cfset attributes.var.password = originalPassword />
</cfif>
No, I don't think there is a way to modify <cfdump>'s behavior. I can't be sure, obviously. It's thinkable that such a hack exists, though it's not necessarily recommendable.
Why not go with a simple:
<cftry>
<cfset DoSomethingThatFails()>
<cfcatch>
<cfif StructKeyExists(FORM, "Password")>
<cfset FORM.Password = "***">
</cfif>
<cfdump var="#FORM#">
</cfcatch>
</cftry>
CFDUMP began life as a custom tag (CF_DUMP) way back in the CF5 days. You could always get the code for that custom tag and modify it to your needs and use that instead of the built-in tag.
Is it only the password that is a problem of showing? If so, perhaps the solution is to salt/hash the password? That I think is good practice anyway.
http://blog.mxunit.org/2009/06/look-ma-no-password-secure-hashing-in.html

How do I redirect based on referral in ColdFusion

I have a coldfusion web site I need to change. Have no idea or experience with this environment (I do know ASP.NET). All I need to do is to write a condition based on the referral value (the URL) of the page, and redirect to another page in some cases.
Can anyone give me an example of the syntax that would perform this?
All of the other examples would work...also if you're looking to redirect based on a referral from an external site, you may want to check CGI.HTTP_REFERER. Check out the CGI scope for several other options.
<cfif reFindNoCase('[myRegex]',cgi.http_referer)>
<cflocation url="my_new_url">
</cfif>
...my example uses a regex search (reFind() or reFindNoCase()) to check the referring URL...but you could also check it as a list with / as a delimiter (using listContainsNoCase()) depending on what you're looking for.
Lets assume your the URL variable you are basing this on is called goOn (http://yoursite.com?goOn=yes) then the following code would work:
<cfif structKeyExists(url, "goOn") AND url.goOn eq "yes">
<cflocation url="the_new_url" addtoken="false">
</cfif>
Nothing will happen after the cflocation.
There is a CGI variable scope in ColdFusion that holds information on the incoming request. Try the following:
<cfif CGI.SCRIPT_NAME EQ 'index.cfm'>
<cflocation url="where you want it to redirect" />
</cfif>
To see what else is available within the CGI scope, check out the following:
http://livedocs.adobe.com/coldfusion/8/htmldocs/Expressions_8.html#2679705
Haven't done coldfusion in a little while but:
<cfif some_condition_based_on_your_url>
<cflocation url="http://where_your_referrals_go">
</cfif>
<!--- continue processing for non-redirects --->
A dynamic version.
<cfif isdefined("somecondition")>
<cfset urlDestination = "someurl">
<cfelseif isdefined("somecondition")>
<cfset urlDestination = "someurl">
.
.
.
<cfelse>
<cfset urlDestination = "someurl">
</cfif>
<cflocation url = urlDestination>