• No results found

ColdFusion makes handling form input very easy. Simply code an HTML form with the action set to a page that handles the input, pulling variables from the form scope, and you can gather and respond to user input.Too many developers, however, are careless in their form-handling code.The features offered in

ColdFusion that were intended to make the product more usable (such as the way the engine searches through all of the variable scopes to find a value for an unscoped variable) can be used by hackers to break your application or to cause unintended results. Consider the code shown in Figure 1.1.

Figure 1.1Improper Form Action Code

<cfparam name="myFieldName" default="fname"> <cfparam name="dsn" default="MyDSN">

<cfif IsDefined("form.fieldnames)">

<!---if we've found the variable form.fieldnames, ---> <!---a form post has been received--->

<cfquery name="getUsers" datasource="#dsn#"> select #myFieldName#

from users

where userID = #userID# </cfquery>

<cfoutput>

Your column, #myFieldName#, yielded the following result:<br> #valueList(getUsers.myFieldName)#

</cfoutput>

<cfelse>

<h3>An Extremely Simple, Poorly-Coded Form Action Page</h3> <form action="#cgi.script_name#" method="post">

<input type="text" name="myFieldName"><br>

<input type="submit" name="submit" value="submit"> </form>

This form action page does a few things well:

The form self-posts—by using the cgi.script_name value, you can create a

form that will run wherever it is located. Avoiding the use of a hard- coded directory or action template makes it easier to code a modular form that can be used in many places in your application. If you like, you can use this structure to embed forms in your custom tags.

The form checks for the existence of a variable scope by using the existence of a

known variable, form.fieldnames, to confirm that a form has been submitted and that form scope variables are available. In ColdFusion 5.0, you can use the <CFDUMP> tag to inspect the contents of a variable scope; for example, <CFDUMP var="#form#"> to check and output the contents of the form scope.This capability is available in ColdFusion 4.x, but must be coded manually as a custom tag.This capability is available in

ColdFusion 4.x, but must be coded manually as a custom tag, such as the

<cfa_dump> Spectra tag (precursor to the <CFDUMP> CF 5.0 tag).

The template sets default values for variables by using <CFPARAM> to set

the default value for the myFieldname value used on the page and for the datasource used in the <CFQUERY> call.

The code specifies scopes for output variables, setting the scope of

myFieldName variable used in the valuelist() call to the myFieldName

variable returned from the getUsers query, not from some other

myFieldName variable.

The code in Figure 1.1, however, does many things poorly:

The form doesn’t scope all of its variables and refers to unscoped variables

such as myFieldName, dsn, and UserID in the code. Because ColdFusion checks all of the variable scopes before throwing an error when it encounters an unscoped variable, an attacker could post a form to this action page.This action would satisfy the initial error handling that checks for the existence of form.fieldnames, and allow the hackers to sub- stitute arbitrary values for the myFieldName, dsn, and UserID variables. Depending upon what your code does, this hack could be an annoyance or a serious security breach.

This template fails to validate an integer field, passing the UserID field

unchanged to the <CFQUERY> tag.This is a very dangerous coding mistake, because it allows a hacker to insert arbitrary SQL commands

into your <CFQUERY> tag. Fortunately, this is an easy problem to fix: simply validate the UserID field with ColdFusion’s val() function, which returns a harmless 0 if the value in the tested variable is not an integer.

The code uses a dynamic SQL query, which although sometimes useful can

be a very dangerous item to include in your templates. Because the spe- cific field is not listed at the time the template is being called, the query can be manipulated in unpredictable ways. It is not recommended to allow users to return a specific column name from a query dynamically; instead, consider using stored procedures to return an entire record refer- enced by a key field (in this case, UserID).This method allows you to allow the database server to do more processing and extends your ability to add modular error-handling code inside of your stored procedures.

The code fails to set a specific scope in defined variables, setting myFieldName

in the <CFPARAM>, rather than variables.myFieldname, which would create a variable only for the local template, or form.myFieldName, which would expect only a value for the myFieldName variable from the form that you submitted. In addition, the DSN value is not scoped either, leaving it vulnerable to attack.You can use request.dsn to hold the value of your datasource, and set it in your Application.cfm file. Because the Application.cfm is run on every request, this action ensures that your variable will be available to every template in your application.

This template doesn’t check the cgi.http_referer value against a list of

known pages or of known domains to make sure the form is being called from an expected page. Use a simple version of this technique to check the cgi.http_referer value conditionally; for a more complicated and functional version, you might want to use a stored procedure or a custom tag to look up a list of known pages that correspond to this page.To implement this, you will need to identify each of your CFML templates in a data table, and load this information as a structure into memory in your Application.cfm (not recommended), or use a simple query lookup to find the information (recommended).

The listing in Figure 1.1 is intended to be an oversimplified example of poor coding, but it contains common mistakes to avoid and trap in your code.The same code, after fixing these bugs, might look like Figure 1.2.

Figure 1.2Improved Form Action Code

<cfif cgi.http_referer does not contain "myDomain.com"> <!---check to see where they are coming from --->

<!---this functionality could be encapsulated further ---> <cflocation url="/index.cfm">

</cfif>

<!---set variable scopes --->

<cfparam name="variables.myFieldName" default="fname"> <cfparam name="variables.dsn" default="#request.dsn#">

<cfif IsDefined("form.fieldnames)">

<cfif IsDefined("form.myFieldName")>

<cfset variables.myFieldName = form.myFieldName> <!---finding the variable form.fieldnames---> <!---shows that post has happened ---> <!---call stored procedure --->

<cfquery name="getUsers" datasource="#dsn#">

sp_getUserinfo (#val(form.userID)#,'#variables.myFieldName#') </cfquery>

<cfoutput>

Your column, #variables.myFieldName#, yielded the following result:<br> #valueList(getUsers.myFieldName)# </cfoutput>

<cfelse>

<h3>An Extremely Simple, Poorly-Coded Form Action Page</h3> <form action="#cgi.script_name#" method="post">

<input type="text" name="myFieldName"><br>

<input type="submit" name="submit" value="submit"> </form>