Besides the operating system and Web server security services, administrators also have ColdFusion's security framework available to them. The migration to the Java platform means a change in ColdFusion's security infrastructure. ColdFusion now leverages the JAAS (Java Authentication and Authorization Service). NOTE ColdFusion MX introduced a new security framework that completely replaced the Advanced Security system of previous versions. As a result, the following tags and functions are obsolete and will not work in ColdFusion MX: <cfauthenticate>, <cfimpersonate>, AuthenticatedContext(), AuthenticatedUser(), IsAuthenticated(), IsAuthorized(), and IsProtected().However, ColdFusion MX 7 will allow you to create user-defined functions (UDFs) with the same name as these obsolete security functions. ColdFusion offers security in the following areas:
This portion of the chapter focuses on development and user security. CFML security enhancements are discussed in Chapter 7, and resource security (the server sandbox) is covered in Chapter 9, "Creating Server Sandboxes." Development SecurityColdFusion's security implementation begins with the ColdFusion Administrator. The Security section of the Administrator allows you to configure the following: the ColdFusion Administrator password, the RDS password, and Resource/Sandbox security. By default, ColdFusion protects Administrator and RDS access with the passwords you enter during installation. The ColdFusion Administrator Password page (Figure 8.24) allows you to disable the ColdFusion Administrator password, or enter and confirm a new one. Figure 8.24. Use the ColdFusion Administrator Password page to change the Administrator password. To completely disable the password, clear the check box.Figure 8.25 shows the RDS Password page, which is used to control access to ColdFusion from visual tools (Dreamweaver, HomeSite+, and ColdFusion Studio 5). Disabling RDS security means relying on the Web server and individual database servers for file and data source security. Figure 8.25. Use the RDS Password page to control the password for Dreamweaver, HomeSite+, or ColdFusion Studio access, or to completely disable the password.CAUTION Disabling either the Administrator or RDS password can open devastating holes in your application. Disable the Administrator password only if you are using Web server ACLs for access control to the CFIDE directory and its children. Although Macromedia recommends completely disabling RDS on production systems, you should always enable the RDS password on all systems. For steps to disable RDS, see the "Disabling RDS on Production Servers" section of Chapter 10, "Security in Shared and Hosted Environments." User SecurityUser security gives you granular control over your application. This control goes beyond that of the operating system and Web server. The operating system controls local and share access to files and directoriesin other words, NTFS permissions. The Web server's ACLs grant access to files and directories containing code, based upon an authenticated user's credentials. However, neither the Web server nor the operating system allows you to natively extend the system's security framework beyond the page level to the elements within your code (HTML, CFML, images, Web services, and so on). ColdFusion's user security provides the authentication and authorization features that extend access control to the element level, allowing you to programmatically decide what functionality displays in a page. About AuthenticationYou should already be familiar with authentication and authorization. Authentication is the process of validating a user's identity. The typical paradigm is a user name/login ID and password stored in a user table of a back-end relational database (RDBMS). A user submits a user name and password via the proverbial login form, and its action page fires a SQL query that matches the user's input with the entries in a user table. Some enterprise solutions replace the RDBMS with an LDAP (Lightweight Directory Access Protocol) user directory such as Microsoft's Active Directory, the Sun ONE/iPlanet Directory Server, or Novell's NDS. Some high-security sites even utilize LDAP and X.509 client certificates, leveraging the Web server's SSL capabilities. ColdFusion provides tags and functions for easy integration with all these solutions. ColdFusion recognizes two methods of authorization: Web server authentication and application (programmatic) authentication.
About AuthorizationIn ColdFusion security, authorization ensures that the authenticated user has the appropriate credentials to access resources. Rolesgroup memberships defined in a user directorydictate which users have access to what resources. This is undoubtedly similar to some of the models with which you are already familiar. Consider the personnel structure of a typical Web department:
Each individual in the department falls into one of these roles. Each role has access to particular sections of the network infrastructure, database, Web server, and so forth. Indeed, each role has specific responsibilities and duties. Similarly, applications define roles and assign them to users. These roles control what a user can do or can access within the application. Applications then acquire the authenticated user's ID and roles from the user directory at login, storing them for the duration of the user's session. In ColdFusion, the <cflogin> and <cfloginuser> tags provide the authentication functionality, and the GetAuthUser() and IsUserInRole() functions perform authorization. Security Tags and FunctionsAs previously mentioned, ColdFusion includes tags and functions with which to implement security and access control. Table 8.4 describes the new ColdFusion security tags and functions.
Authenticating with <cflogin>Code all of your authentication logic between <cflogin> tagsincluding database user name/password lookups, Windows authentication, LDAP logins, and so forth. The <cflogin> tag creates a container for storing user security informationthe CFLOGIN scope. This scope contains two variables: CFLOGIN.name and CFLOGIN.password. These two variables are populated with a user's login ID and password when any of the following occurs:
CAUTION User name and password are sent in cleartext using a simple login form. Flash Remoting sends the user name/password over the binary-encoded AMF (Action Message Format) protocol. HTTP Basic authentication sends the user name and password in a Base64-encoded string with each request. Consider using SSL (HTTPS) to secure the user name and password when authenticating with these methods. The <cflogin> tag accepts three optional attributes:
If authentication is successful, specify the authenticated user's user ID, password, and roles to the <cfloginuser> tag to log the user into the ColdFusion application. The GetAuthUser() function returns the user ID specified in <cfloginuser>.The IsUserInRole() function checks the specified role against the list of roles specified in <cfloginuser>. TIP If Web server security is used instead of ColdFusion security (CFLOGIN), GetAuthUser() returns the value of CGI.Remote_User, which is set by the Web server. If using both Web server security and CFLOGIN, pass the web server's authenticated user ID (for instance, CGI.Remote_User or CGI.Auth_User) to the <cfloginuser> name attribute to keep Web server security and ColdFusion security in synch. The IsUserInRoles() function requires <cfloginuser>. Storing Login InformationLogin credentials are stored either in a cookie or in the Session scope, as determined by the LoginStorage value as either an Application.cfc initialization variable or in the <cfapplication> attribute. By default, <cflogin> sets a nonpersistent cookie in the user's browser, called cfauthorization_applicationName. The cookie value is a Base64-encoded string containing the user ID, password, and application name. This in-memory cookie is not written to disk (for example, the cookies.txt file) and is destroyed when the browser closes. Because ColdFusion sends this cookie with every request, users must allow in-memory cookies in their browsers. If the browser disables cookies, then the effect of the <cfloginuser> tag exists only for the current page request. In this scenario, ColdFusion allows you to code the <cfloginuser> outside of the <cflogin> tag in every template you want to secure, in order to persist the login information across page requests. When storing login information in the Session scope, ColdFusion stores the Base64-encoded user ID, password, and application name in the SESSION.cfauthorization variable. ColdFusion stores this variable in its internal memory space and uses the browser's session cookies (CFID and CFTOKEN, or JSESSIONID) for user identification. This is more secure than using cookies for login storage because ColdFusion does not pass SESSION.cfauthorization with every page request. The user's login and session share the same timeout valueand ColdFusion ignores the IdleTimeout <cflogin> attribute. To use the Session scope to store login information, ensure the following:
TIP With SetDomainCookies enabled in Application.cfc or <cfapplication>, the SESSION.cfauthorization login variable is available to all members in a server cluster. Best Practice: ColdFusion Sessions and CFLOGINSession scope variables are held in ColdFusion memory space. Storing the CFLOGIN authorization variable (SESSION.cfauthenticate) in the Session scope (LoginStorage=Session) will persist the value for the duration of the user's session. If the user closes the browser without logging out with CFLOGOUT, the authorization value will still persist in ColdFusion memory until the Session scope is cleared when the session times out or the server is restarted. If another user logs in with the same Session ID (CFID and CFTOKEN or JSESSIONID) as the authenticated user, that user can impersonate the previously authenticated user. ColdFusion identifies browser sessions by cookies and URL parameters. A ColdFusion Session ID comprises the CFID, CFTOKEN, and Application name (applicationname_CFID_CFTOKEN). When J2EE Sessions are enabled, the Session ID is the JSESSIONID value. A ColdFusion session can be impersonated by passing existing Session ID values on the URL or in cookies. The following are best-practice steps to prevent session impersonation:
Logging OutThere are several ways to log out a user and remove their login information from ColdFusion. The primary method is to use the <cflogout> tag. ColdFusion MX did a poor job of destroying session information after a user logged out. Table 8.5 lists the circumstances in which ColdFusion logs out the current user and destroys the <cfloginuser> authentication credentials.
CAUTION <cflogout> does not clear the login information if you authenticate users via Web server security or the HTTP Authorization header. These security paradigms continue to send authentication information to ColdFusion until all browser windows are closed. Therefore, until all browser windows are closed, the CFLOGIN scope may persist beyond user logout, providing a window of opportunity for another user to impersonate the first user. Executing <cflogout> when using LoginStorage=Session removes the SESSION.cfauthorization variable from the Session scope, but does not end the current user's session. Therefore, if you want to log the user out and completely clear their session, you must code logic that calls <cflogout> and StructClear(Session): <cfif IsDefined('URL.Logout') and URL.Logout> <cflogout> <cfset StructClear(Session)> </cfif> TIP Calling StructClear(Session) will not generate a new session id but it will completely empty the SESSION structure. Basic ColdFusion Login ExampleThe following code demonstrates a single-page login mechanism. Three templates are involved: Application.cfm, loginForm.cfm, and index.cfm. The loginForm.cfm template contains a simple login formuser name and password fieldsthat passes the special j_username and j_password to the <cfloginuser> tag in the Application.cfm. The user must authenticate with the login form in order to access the index.cfm. The Application.cfm contains all the authentication and authorization logic in the body of <cflogin>. Listings 8.1 through 8.3 display this basic login code example (using "admin" and "password" for the username and password, respectively). Listing 8.1. Application.cfm[View full width] <cfsilent> <!---#### File name: Application.cfm Description: Demonstrates coldfusion user security with <cflogin>, <cfloginuser>, and <cflogout> Tags. Assumptions: None Author name and e-mail: Sarge (ssargent@macromedia.com) Date Created: July 24, 2002 Change Log: Updated February 20, 2005 ####---> <cfapplication name="OWS" sessionmanagement="yes" sessiontimeout="#createTimeSpan(0,0,0 ,30)#" loginstorage="cookie"> <!---#### Display a nice title in the browser title bar ####---> <cfhtmlhead text="<TITLE>ColdFusion: User Security Test</title>"> <!---#### Set a REQUEST variable to check the login status. ####---> <cfparam name="REQUEST.loggedin" default="true" type="boolean"> <!---#### If the logout URL variable is passed, log off the current user, clear the session, and set the login status to false, then return the user to the login screen. ####---> <cfif IsDefined('URL.logout') and URL.logout> <cflogout> <cfset StructClear(Session)> <cfset REQUEST.loggedin = "false"> <cfinclude template="loginform.cfm"><cfabort> </cfif> <!---#### Call <cflogin> to create the CFLOGIN scope/container. Idle timeout is set to 30 minutes or 1800 seconds. ####---> <cflogin idletimeout="1800"> <!---#### CFLOGIN.name and CFLOGIN.password automatically assume the j_username and j_password values from the login form. If you use some other field naming conventions, you will have to manually set CFLOGIN.name and CFLOGIN.password equal to the corresponding values. ####---> <cfif IsDefined("CFLOGIN.name") and Len(Trim(CFLOGIN.name)) and Len(Trim(CFLOGIN.password))> <!---#### Authenticate the user. For this example, the only valid user is "admin," whose password is "password." CompareNoCase will return a zero (0) if the two strings are identical. ####---> <cfif not CompareNoCase('admin', Trim(CFLOGIN.name)) and not CompareNoCase(Trim (CFLOGIN.password), "password")> <!---#### Pass the authenticated user's name, password, and role to <cfloginuser>, and set login status to true ####---> <cfloginuser name="#CFLOGIN.name#" password="#CFLOGIN.password#" roles="admin"> <cfset REQUEST.loggedin = "true"> <cfelse> <!---#### if the login fails, set bad login notice, set login status to false, and return to the login form. ####---> <cfset REQUEST.badlogin = "true"> <cfset REQUEST.loggedin = "false"> <cfinclude template="loginform.cfm"><cfabort> </cfif> <cfelse> <!---#### If no login is provided, set login status to false and return to the login form. ####---> <cfset REQUEST.loggedin = "false"> <cfinclude template="loginform.cfm"><cfabort> </cfif> </cflogin> </cfsilent> The Application.cfm checks to see if a user is logged in, and redirects the request to the login page (loginform.cfm) if needed. Listing 8.2. loginForm.cfm[View full width] <cfsetting enablecfoutputonly="yes"> <!---#### File name: loginForm.cfm Description: Login form for the ColdFusion user security example. Demonstrates how to use the special j_username and j_password field names for the <cflogin> tag. Assumptions: None Author name and e-mail: Sarge (ssargent@macromedia.com) Date Created: July 24, 2002 Change Log: Updated February 20, 2005 ####---> <cfsetting enablecfoutputonly="no"> <!doctype html public "-//w3c//dtd html 4.01 transitional//en"> <html> <body> <p>Please enter your login information:</p> <!---#### If the user submits a bad login, display a friendly message ####---> <cfif IsDefined('REQUEST.badlogin')><span style="color: red">Your login information was invalid!</span></cfif> <!---#### Use cfform to provide client-side javascript validation on the user name form field. ####---> <cfform action="index.cfm" method="post"> <table border="0"> <tr> <td>User Name:</td> <td><cfinput type="text" name="j_username" message="You must enter a user name!" required="yes"></td> </tr> <tr> <td>Password:</td> <td><cfinput type="password" name="j_password" message="You must enter a password!" required="yes"></td> </tr> <tr> <td> </td> <td><input type="reset"> | <input type="submit" name="logon" value="Login"></td> </tr> </table> </cfform> </body> </html> The loginForm.cfm template contains the actual form that submits the user name and password. These are processed by the Application.cfm when the form is submitted. The code in Application.cfm will allow processing to continue with index.cfm if authentication is successful; otherwise, the login form will redisplay. Listing 8.3. index.cfm[View full width] <cfsetting enablecfoutputonly="yes"> <!---#### File Name: index.cfm Description: Index page secured by <cflogin> in the Application.cfm. This page also shows how to use the ColdFusion security functions: GetAuthUser() and IsUserInRole. Assumptions: None Author name and e-mail: Sarge (ssargent@macromedia.com) Date Created: July 24, 2002 Change Log: Updated February 20, 2005 ####---> <cfsetting enablecfoutputonly="no"> <!doctype html public "-//w3c//dtd html 4.01 transitional//en"> <html> <body> <!---#### Use the GetAuthUser function to display the authenticated id ####---> <p><b>Welcome, <span style="color: green"><cfoutput>#GetAuthUser()#</span> </cfoutput>!< /b></p> <!---#### Use the IsUserInRole function to add conditional logic based on the user's group membership (role). ####---> <cfif IsUserInRole("admin")> <p>Based on your login ID, you are permitted to access this section of the site.</p> Please proceed to the <a href="index.cfm" title="This link is for demonstration purposes only">Administrator's section</a>. <!---#### Display the log out link. ####---> <p><a href="index.cfm?logout=yes">Log Out</on></p> </cfif> </body> </html> |