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).
ColdFusion offers security in the following areas:
-
Development
. ColdFusion provides password protection for the administrator, and Remote Development Services (RDS) access via Macromedia Dreamweaver, HomeSite+, or ColdFusion Studio 5.
-
CFML
. The ColdFusion Markup Language provides several features to augment application security and deployment.
-
Resource
. ColdFusion controls access to a subset of tags and functions, data sources, files and directories, and host IP addresses.
-
User
. ColdFusion provides user authentication, allowing you to secure application functionality based on a user's role (or
group
membership).
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 Security
ColdFusion'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.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.
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 Security
User 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 Authentication
You 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.
-
Web server authentication
. Most Web servers support basic HTTP authentication, requiring a valid user name and password to access directories containing application files. When a user
requests
a page in a secured directory, the Web server
presents
a login form. If the user's login is successful, the Web server grants access to the directory and caches the authenticated user ID and password to
employ
on the user's
subsequent
page requests. We have already described how to configure directory-based authentication methods in IIS, Apache and Sun ONE.
-
Application authentication
. Application authentication relies on application code and logic to perform roles-based authentication. In this method, it's the application that displays the login form and authenticates the user against the application's own user directory (usually a database or LDAP). Upon a successful login, the application checks the user's credentials and grants access to the appropriate ColdFusion resources.
About Authorization
In 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:
-
Network administrators
-
Systems administrators
-
Database administrators
-
Web developers
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 Functions
As 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.
Table 8.4. ColdFusion Security Tags and Functions
|
TAG\FUNCTION
|
DESCRIPTION
|
|
<cflogin>
|
Provides a container for user authentication code. Used with the
<cfloginuser>
tag to validate a user login against an LDAP, database, or other user repository.
|
|
<cfloginuser>
|
Identifies the authenticated user to ColdFusion by specifying the user's ID, password, and
roles
. Requires the
name
,
password
, and
roles
attributes. Specify a comma-delimited list to the
roles
attribute. ColdFusion
evaluates
whitespace in this attribute, so be careful not to add spaces after commas.
|
|
<cflogout>
|
Logs the current authenticated user out of ColdFusion by completely removing the user's authenticated ID (session) and roles. When this tag is not used, ColdFusion automatically logs users out when their sessions time out.
|
|
<cffunction>
|
Used only in ColdFusion Components (CFCs). The
roles
attribute restricts function execution to authenticated users in the specified roles.
|
|
<cfntauthenticate
>
|
Authenticates a user name against the
Windows
domain in which the ColdFusion server is running. This tag can
optionally
retrieve the authenticated user's group memberships as well. This tag is new in ColdFusion MX 7.
|
|
GetAuthUser()
|
Returns the authenticated user's ID. By default it returns the
username
value specified in
<cfloginuser>
; if this is blank, it returns the value of
CGI.Remote_User
.
|
|
IsUserInRole()
|
Returns
true
if the authenticated user is a member of the specified roles. Use a comma-delimited list to check multiple role assignments.
|
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:
-
A form is submitted containing input fields with the special j_username and j_password
names
. For example:
<input type="text" name="j_username">
<input type="password" name="j_password">
-
A Macromedia Flash Remoting
gatewayConnection
object is sent containing the
setCredentials()
method.
TIP
For an example of how to authenticate via Flash Remoting, see Macromedia TechNote 18684, "How to pass login credentials to
cflogin
via Flash Remoting" at http://www.macromedia.com/go/tn_18684.
-
A request contains an Authorization Header with a user name and password sent via HTTP Basic authentication.
-
A request contains an Authorization Header with a
hashed
user name and password sent via Digest or NTLM authentication. In this case,
CFLOGIN.name
contains the username sent by the Web server, but
CFLOGIN.password
is set to an empty string.
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:
-
IdleTimeout
. Specifies a maximum time interval for inactivity (the period between page requests) before logging out the user. The default value is 1800 seconds (30 minutes). This attribute is ignored if the
loginStorage
attribute is set to
Session
in the
<cfapplication>
tag or
Application.cfc
.
-
ApplicationToken
. An application-specific identifier used to restrict the
CFLOGIN
scope to the current application. This defaults to the current application namespecified in the
<cfapplication>
tag or with the
THIS.name
variable in
Application.cfc
and
prevents
cross-application
logins.
NOTE
Normally you won't need to specify
ApplicationToken
; however, ColdFusion allows unnamed applications for J2EE compatibility. ColdFusion uses the
ApplicationToken
value to help keep the user's login valid for only the current directory and its subdirectories. To secure code in other directories, the application name must be identical to the
ApplicationToken
value.
-
CookieDomain
. Specifies the domain for which the login cookies are set. This prevents cross-site cookie attacks and is useful in clustered environments.
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 Information
Login 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:
-
Session Management is enabled in the ColdFusion Administrator
-
Session Management is enabled in the
Application.cfc
or
<cfapplication>
-
LoginStorage=Session
is specified in either the
Application.cfc
or
<cfapplication>
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
CFLOGIN
Session 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 Out
There 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.
Table 8.5. ColdFusion User Logout
|
DESCRIPTION
|
LoginStorage=Cookie
|
LoginStorage=Session
|
|
Application fires
<cflogout>
|
X
|
X
|
|
The
<cflogin> IdleTimeout
value is reached
|
X
|
N/A
|
|
The user closes all browser windows
|
X
|
N/A
|
|
The ColdFusion session ends
|
N/A
|
X
|
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 Example
The 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>