ColdFusion MX 7 Login Security (Page 5 of 13)

If you downloaded the source files, you can follow along in our scenario. There is only one entry in the Users table. The username is "admin" and the password is "admin". Because the password was encrypted using ColdFusion's Hash() function (more on this later), it will look like gibberish in the database; this is fine. As outlined on the previous page, when a user submits the login form, the onRequestStart method will recognize the form submittal and immediately redirect to authenticate.cfm. If you open up this template you'll see that the first thing we do is query the database in an attempt to match the user-supplied username with a username in the database.

30. <cfquery name="qryGetUserDetails" datasource="##request.datasource##">
31.   SELECT ID, Username, Password, FirstName, LastName, LastLogin_TS
32.   FROM Users
33.   WHERE Username = '##FORM.Username##'
34. </cfquery>

Next, we create an IF statement that handles the results of our query. There are two and only two scenarios here. Either the username supplied in the FORM exists in the database or it does not. Also, there should be only one instance of a particular username in the database. Of course, this doesn't happen by magic, and should be taken care of in your procedure to add/create users (not covered in this tutorial).

36. <cfif qryGetUserDetails.RecordCount eq 0>
41.    <cfset variables.errorMessage = "The Username you provided, <b>" &
                                       FORM.Username & "</b>, is an invalid Username.">
42. <cfelse>
44.    <cfset variables.hashedPassword = Hash(FORM.Password)>
45.    <cfif variables.hashedPassword neq qryGetUserDetails.Password>
47.       <cfset variables.errorMessage = "The Password you supplied for user <b>" &
                                          FORM.Username & "</b> was incorrect.">
48.    <cfelse>
52.       <cfquery name="qryUpdateLastLoginTS" datasource="##request.datasource##">
53.          UPDATE Users
54.          SET LastLogin_TS = ##CreateODBCDateTime(Now())##
55.          WHERE ID = ##qryGetUserDetails.ID##
56.       </cfquery>
63.       <cfset request.User.LoggedIn = "1">
64.       <cfset request.User.Username = FORM.Username>
65.       <cfset request.User.FirstName = qryGetUserDetails.FirstName>
66.       <cfset request.User.LastName = qryGetUserDetails.LastName>
67.       <cfset request.User.LastLogin = qryGetUserDetails.LastLogin_TS>
68.       <cfset tmpMessage = "You were last here on " &
                              DateFormat(qryGetUserDetails.LastLogin_TS, "mm.dd.yyyy")
                              & " at " &
                              TimeFormat(qryGetUserDetails.LastLogin_TS, "hh:mm tt")
                              & ".">
69.       <cfset request.User.LoginMessage = IIF(qryGetUserDetails.LastLogin_TS neq ""
                                        "tmpMessage", DE("This is your first visit!"))>
70.    </cfif>
71. </cfif>

Let's go over both scenarios and how our application will handle them. First, if the user supplies a username that does not exist in our database the conditional logic on line 36 will evaluate to TRUE. This will result in the creation of a local variable to hold an appropriate error message. Once this variable is set, page processing will return to the onRequestStart method - specifically, line 108. On this line, we test for the existence of a structure key called Request.User.LoggedIn. Obviously, since the user failed to supply a correct username, this key does not exist and the conditional statement will evaluate to TRUE. The login form will be shown again, and this time will display the error message we set in the authenticate.cfm template.