Accessibility

TechNote

Security Best Practice: Validating browser input

An underlying problem with many web applications is that they dynamically generate HTML pages containing non-validated browser input. If browser-submitted Cookie, URL and Form variables are not validated, malicious users can potentially embed web browser-executed scripts within the input. If a server-side script then re-displays this non-validated input, the script runs on the browser as though the trusted site generated it.

As outlined in Macromedia Security Bulletin ASB00-05, if you do not validate input to your dynamic web pages, you may encounter the following:

  • Compromised data integrity
  • Cookies set and read by unauthorized users
  • Intercepted user input
  • Malicious scripts executed by the client in the context of the trusted source

Typical examples include the following types of web pages:

  • Search engines that return results pages based on user input
  • Login pages that store user accounts in databases, cookies, and so forth, and later write the user name out to the client
  • Web forms that process credit card information

Presented here are a few approaches to preventing cross-site scripting security attacks. While these approaches can help you determine risk on your site, you must evaluate your specific situation to determine which techniques will work best for you. It is important to note that in all techniques, you are validating data that you receive from incoming browser input sources, such as Cookies, URL and Form variables. Prevention means following good coding practices by validating all client browser input to your server-side code.

Below are examples and methods to protect yourself and end-users from this vulnerability.

  • Review server-side JRun and ColdFusion code for possible areas of vulnerability
  • Define a Character Set in output HTML
  • Use built-in CFML tags and functions, such as:
    • HTMLCodeFormat
    • HTMLEditFormat
    • URLEncodedFormat
    • URLDecode
    • ReplaceList
    • REReplace
    • REReplaceNoCase
  • Escape and replace special characters and tags content in Java
  • Study the CF_INPUTFILTER ColdFusion CFML custom tag accompanying this article and consider using similar techniques in your Application.cfm files

Note: Macromedia has also released the CFML source code for this tag so that end-users may have an example of techniques used to remove special characters and tags from strings. As with the other solutions, there can be drawbacks. Please also note that this tag is provided "as is" and is provided for informational purposes only.

ColdFusion developers need to decide which solution best fit their needs; no foolproof quick fix exists to this problem. Each solution has certain drawbacks, whether it is a decrease in application performance, or inoperability in some areas. The following is a list of additional resources, which can provide additional information on the cross-site scripting issue:

Review server-side JRun and ColdFusion code for possible areas of vulnerability

To properly examine your code, you must examine the areas in which you accept data from end-users, in any form. This means you must check Form inputs, URL referrals, and Cookie data, among others. Below are listed some basic code auditing pointers.

Developers should strip, replace or modify input data from web browsers, since end-user can insert malicious code into these values.

JRun and ColdFusion developers should identify areas of code that use data from the client web browser. Typical areas of concern are code that act as common handlers, process Form, URL or Cookie data.

ColdFusion developers should also review code that makes use of the following short list of tags that are commonly used, but in which data is rarely validated by ColdFusion developers:

  • CFINPUT
  • CFOUTPUT
  • CFAPPLET
  • CFAPPLICATION
  • CFHTTPPARAM
  • CFINSERT
  • CFQUERY
  • CFTABLE

Furthermore, code should be reviewed that accepts browser input from the user, saves that data and dynamically generates pages based off of that saved data. For example, code that stores personalized data in the form of a cookie on the client's computer, or in a server-side database.

A malicious user may open the cookie itself, edit the content of that cookie to insert database queries, malicious scripts, or links/references to malicious code, with the intent that this malicious code be executed when the user returns to the administrator's web site.

Example:

The cfcookie tag generates a cookie that is then loaded onto the customer's hard drive, and the cookie's properties read as follows:

<cfcookie name="UserID" value="320054">

Later, some other code reads this cookie to generate a dynamic database query based on the values found in the UserID cookie:

 <cfquery name="q1" datasource="MySiteContent">     Select * from Users     Where UserID = #cookie.UserID#</cfquery> 

A hostile user could potentially modify the cookie parameters to contain malicious code:

 <cfcookie name="UserID"   value="320054+truncate+Table+Users"> 

An exploit such as this will not work if input from the UserID cookie is validated to contain only an integer. However, because input from the UserID cookie is not validated by the application, this sample exploit is successful.

This sample exploit can be foiled with simple validation in the call to cfquery:

 <cfquery name="q1" datasource="MySiteContent">     Select * from Users     Where UserID =<cfqueryparam cfsqltype="CF_SQL_INTEGER"        value="#cookie.UserID#"></cfquery> 

The parameter we expect to find is validated to be of data type INTEGER, disallowing malicious or garbage input.

In addition to input filtering, output values should also be considered for filtering, to ensure that any malicious existing browser script code is not output to the browser.

Define a Character Set in output HTML

Users have the ability to set the character set header attribute in the Application.cfm of their ColdFusion site. This attribute filters the entire site. This allows you to set a very specific set of characters for your site, banning any character that are not explicitly allowed. A list of proper ISO's and their specific character sets is available at:

www.w3.org/TR/html4/references.html

Since setting this attribute restricts data coming out of dynamically loaded pages to that specific character set, it may not be feasible for many companies as restricting the character set can cause functionality problems.

How to set the character set in your website's header:

The format for the attribute is:

 <meta http-equiv="Content-Type" content="text/html;     charset=ISO-8859-1"> 

This particular HTTP header announces that the character encoding is ISO-8859-1.

You can define character sets in the following areas of a document:

  • An HTTP "charset" parameter in a "Content-Type" field.
  • A META declaration with "http-equiv" set to "Content-Type" and a value set for "charset".
  • The charset attribute set on an element that designates an external resource.

The end-user's client translates these in a specific manner, the first being the highest priority. You should investigate exactly where you set your character set as some web servers strip out the header information of a page before it parses it and sends it tothe end-user.

More information on character sets and how to use them is available at:

www.w3.org/TR/html4/charset.html
www.w3.org/TR/html4/charset.html#spec-char-encoding

Use built-in CFML tags and functions

URLEncodedFormat

This function returns a URL encoded string, making it safe to pass strings through a URL.

It replaces spaces with + and all non-alphanumeric characters with equivalent hexadecimal escape sequences. This function enables you to pass arbitrary strings within a URL, because ColdFusion automatically decodes all URL parameters that are passed to the template.

Syntax: URLEncodedFormat( string)
String - String being URL encoded.

URL encoding refers to a data format where all high ASCII and non-alphanumeric characters are encoded using a percent sign followed by the two character hexadecimal representation of the character code. For example, a character with code 129 will be encoded as %81. In addition, spaces can be encoded using the plus sign (+).

Query strings in HTTP are always URL-encoded.

Example:

 <CFIF IsDefined("url.myExample")><P>The url variable url.myExample has been   passed from the previous link ... its value is:<BR>   "<CFOUTPUT>#url.myExample#</CFOUTPUT>"</CFIF><P>This function returns a URL encoded string, making it safe to pass strings through a URL.<CFSET s = "My url-encoded string has special characters & other stuff"><P><A HREF= "urlencodedformat.cfm?myExample= <CFOUTPUT>#URLEncodedFormat(s)# </CFOUTPUT>">Click me</A> 

URLDecode

Decodes a URL-encoded string.

Syntax: URLDecode(URLEncodedString)
UrlEncodedString - A string that has been URL-encoded

Here is an example of the URLDecode and URLEncodedFormat functions. In the example, a string containing all ASCII character codes in the range 1-255 is created. The string is then encoded and decoded. The decoded value is compared with the original string to demonstrate its equality.

 <CFSCRIPT> // Build string s = ""; for  (c = 1; c lte 256; c = c + 1) { s = s & chr(c); } // Encode string and display result enc = URLEncodedFormat(s); writeOutput("Encoded string is: '#enc#'.<BR>"); // Decode and compare result with original dec = URLDecode(enc); if (dec neq s) { writeOutput("Decoded is not the same as encoded."); } else { writeOutput("All's well on the Western front."); }</CFSCRIPT> 

Note that the encoded string is much longer than the actual string; this provides additional string and code obfuscation. Because the string must fit the coded format to be encoded or decoded, this characteristic makes it more difficult for individuals to execute scripts and code using the URL.

HTMLCodeFormat

Returns HTML escaped string enclosed in <PRE> and</PRE> tags. All carriage returns are removed from string, and all special characters (>< " &) are escaped.

Syntax: HTMLCodeFormat( string [, version ])
String - String being HTML escaped and preformatted
Version - The specific HTML version to use in replacing special characters with their entity references. Valid entries are:

  • 1 - The latest implementation of HTML
  • 2.0 - For HTML 2.0 (Default)
  • 3.2 - For HTML 3.2

HTMLEditFormat

Returns an HTML escaped string. All carriage returns are removed from string, and all special characters (>< " &) are escaped.

HTMLEditFormat allows a developer to take input from a Form/Cookie/URL string and "escape" all carriage returns. Special characters will not be stripped from the string, but simply ignored when the data is passed. This will prevent the user from passing any special/html functions and being able to activate those functions in the resultant URL.

Syntax: HTMLEditFormat( string [, version ])
String - String being HTML escaped
Version - The specific HTML version to use in replacing special characters with their entity references. Valid entries are:

  • 1 - The latest implementation of HTML
  • 2.0 - For HTML 2.0 (Default)
  • 3.2 - For HTML 3.2

Here is a functional example of HTMLCodeFormat and HTMLEditFormat in action:

 <FORM ACTION="HTMLeditformat.cfm" METHOD="POST"> Try entering a URL for the tag to return in HTMLCodeFormat and HTMLEditFormat:<INPUT TYPE="Text" size=25 NAME="urladdress"  VALUE="http://www.macromedia.com"><INPUT TYPE="Submit" NAME="" VALUE="get page"></FORM><!--- sets a default value for a url to retrieve ---><CFPARAM NAME="urladdress"  DEFAULT="http://localhost/cfdocs/index.htm"><!--- if we have passed a url address in  the FORM, we want to display the passed address ---><CFIF IsDefined("FORM.urladdress") is True><!--- do simple error check to avoid crashing the tag ---><CFIF Trim(Form.urladdress) is "" or Trim(Form.urladdress) is "<A href='http://"'>http://"</A>><!--- if error condition tripped, set alternative ---><CFSET urlAddress="http://localhost/cfdocs/index.htm"><H4>because you entered no url or an empty string, the tag will return the following address:<A href="http://localhost/cfdocs/index.htm"> http://localhost/cfdocs/index.htm</A></H4><CFELSE><!--- otherwise use address passed from form ---><CFSET urlAddress = "#FORM.urladdress#"></CFIF><!--- now use the CFHTTP tag to get the  file content represented by urladdress ---><CFHTTP URL="#urladdress#"  METHOD="GET"  RESOLVEURL=YES></CFHTTP><CFELSE><!--- the first time through, retrieve a URL that  we know exists ---><CFHTTP URL=http://localhost/cfdocs/index.htm METHOD="GET" RESOLVEURL=YES></CFHTTP></CFIF><!--- Now, output the file, including  the mimetype and content ---><H3>Show the file</H3><CFOUTPUT><P>Here is an example of 255 characters from your file output in HTMLCodeFormat:<P>#HTMLCodeFormat(Mid(CFHTTP.FileContent,1,255))#<P>Here is an example of 255 characters from your file output in HTMLEditFormat:<P>#HTMLEditFormat(Mid(CFHTTP.FileContent,1,255))#</CFOUTPUT> 

The primary difference between HTMLCodeFormat and HTMLEditFormat is that HTMLCodeFormat actually formats the parsed data, keeping line breaks; HTMLEditFormat does not. By escaping all special characters, the HTMLEditFormat function increases the length of the specified string. This can cause unpredictable results when performing certain string functions (Left, Right, and Mid, for example) against the expanded string.

ReplaceList

Function that returns string stripped of set of characters.

 ReplaceList(  my_string, comma_delimited_list_of_chars, "" ) 

REReplaceNoCase

Returns string with a regular expression being replaced with substring in the specified scope. The search is case-insensitive.

Syntax: REReplaceNoCase( string, reg_expression, substring [, scope ])
String - Any string
reg_expression - Regular expression to be replaced. This regular expression can include POSIX-specified character classes (for example, [:alpha:], [:digit:], [:upper:], and [:lower:])
Substring - String replacing reg_expression
Scope - Defines how to complete the replace operation:

  • ONE - Replace only the first occurrence (default)
  • ALL - Replace all occurrences

Example:

 <CFOUTPUT> #REReplaceNoCase("H2el%l>o,  t>%h2is l%i>ne2 co>n2ta>in%s i>l%%le%2gal  c>>>>ha%rac2ters","[>|%|2]", "", "ALL"  )#</CFOUTPUT> 

Note this function was wrapped in a cfoutput tag and given parameters to strip out all ">", "%" and 2's. In doing so, the function stripped out the defined character, and left us with a clean string. You can remove specific characters with this technique or, as this example format shows, you can take a given dynamic string and strip unwanted characters from the output:

 REReplaceNoCase( my_string, "</?(tag1|tag2|...|tagN)[^>]*>", "", "ALL" ) 

In this last case, a string that was created by filling in a form where the output is named "my_string". If the defined tags appear in the string of data supplied by my_string, they are stripped from the end result.

REReplace

 REReplace( my_string, "<[^>]*>", "" , "ALL" ) 

This is the same as ReReplaceNoCase, except that the variables passed to this function are case sensitive, therefore, if you tell the function to strip the <script> tag, it will ignore all<SCRIPT> tags.

See REReplaceNoCase examples below for more details. In the case of both of these tags, you must wrap the function itself around each area where you would be posting data to a dynamic page. This will filter the output efficiently, however.

Below are examples of REReplace Functions that would return strings stripped of all tags.

 REReplace( my_string, "<[^>]*(>|$)", "" , "ALL" ) REReplace( my_string,</?(tag1|tag2|...|tagN)[^>]*(>|$)", "", "ALL" ) 

More Information on these ColdFusion CFML functions can be found in the CFML Language Reference.

Escape and replace special characters and tags content in Java

You can test to see if input contains HTML using the indexOf function. For instance:

 if(request.getParameter("foo").indexOf('<') != -1 ||
		
request.getParameter("foo").indexOf('>')) { // return an error }

Another technique would be to use the String.replace to remove all < and > from input, for example:

 request.getParameter  ("foo").replace('<',' ').replace('>',' '); 

A more friendly technique would be to replace the < and > with their HTML entities, > and <. The Java class libraries do not have a String replacement function that will do this for you but you may use a function in JRun that does this if you wish. For example:

 import com.livesoftware.jsp.JSPParser; ... JSPParser.substitute  (JSPParser.substitute(request.getParameter("foo"), "<" , "<"), ">", ">"); 

Will return a String with < and > replaced by their entities.

Study the CF_INPUTFILTER ColdFusion CFML custom tag and consider using similar techniques in your Application.cfm files

The cf_inputFilter tag removes characters or tags from all fields coming from the specified scopes (form, cookie, or URL). This tag can be placed in the Application.cfm file to filter out any input coming through these scopes to any of the templates belonging to the Application.cfm file.

Download the Input Filter Tag

This tag can be executed only with ColdFusion 4.5 or higher.

Usage:

 <cf_inputFilter    scopes = "[FORM][,COOKIE][,URL]"    chars = "list_of_chars"    tags = "ALL|list_of_tags"> 

Attributes:

Scopes (string list, required) - comma-delimited list of input scopes to be filtered
Chars (string, optional) - string containing set of characters to be filtered out from the input scope
Tags (string list, optional) - comma-delimited list of tag names to be filtered out from the input scope

Usage Examples:

 <!------------------ example 1 ------------------><!--- search.cfm ---><form action="search_result" method="post">  criteria: <input type="text" name="criteria"><input type="submit"></form><cfparam name="form.criteria" default=""><!--- search_result.cfm ---><cfsearch   criteria="#form.criteria#" ... > ... render results  ...<p><!--- the original input should be used in a safe way to prefill the criteria form field for the new search ---> New Search:<form action="search_result.cfm" method="post"><cfoutput><!--- remove all double quotes from the criteria     string to prevent breaking the INPUT tag --->    criteria: <input type="text" name="criteria"  value="#Replace(form.criteria, '"' , '', 'ALL' )#"><!--- If there is a need to preserve the double quotes  in the criteria we have to escape them. Since ampersand  character is the escaping character we need to  need to escape it first. ---><cfset safeCrit =  Replace(form.criteria, "&", '&##38;', 'ALL')><cfset  safeCrit = Replace(safeCrit, """", '&##34;', 'ALL')>  criteria: <input type="text" name="criteria" value="#safeCrit#"></cfoutput><input type="submit"></form><!-------------------- example 2 -------------------><!--- form.cfm ---><form action="form_action.cfm" method="post">  name: <input type="text" name="author"><br>  message: <textarea name="message"></textarea><br><input type="submit"></form><!--- form_action.cfm ---><!--- Since parts of the input are passed back to the user in an HTML page - remove all 'dangerous' tags ---><cf_inputFilter scopes="form"  tags="script,embed,applet,object"><cfoutput><!--- now are the form fields safe --->  Your name is '#form.name#' and you posted the following message:<p>  #form.message#</cfoutput> 

Ensuring the security of your web site is important to Macromedia. If you have any questions related to possible security breaches, please bring them to our attention by sending mail to the Macromedia Security Response Team at secure@macromedia.com; please also visit our Security Zone at www.macromedia.com/devnet/security/ for the latest information about known security risks.

Additional Information


AlertThis content requires Flash

To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player.

Download the free Flash Player now!

Get Adobe Flash Player

Creative Commons License

Search Support


Document Details

ID:tn_17502
Browser:Chrome
Internet Explorer
Netscape
Opera
Safari
Firefox
Database:DB2
Informix
MySQL
Oracle
SQL Server
Sybase
MS Access

Products Affected:

coldfusion