Wednesday, February 01, 2006

Coldfusion Development Part 1

I see a lot of negative press towards Coldfusion, especially on web forums such as Slashdot. Truth of the matter is it really doesn’t matter which web platform you use, if you do not practice secure coding techniques in PHP, JSP, or ASP, they will all cause you security headaches just as well. Things such as input validation, leaving default passwords, and leaving default installations with sample applications can cause a world of hurt in Coldfusion and other web platforms, as stated in this Macromedia article about the top 5 Coldfusion securit. My personal feelings is that application servers shouldn’t be exposed to customer facing networks, but implementing solutions where these systems hide in a protected environment is costly and difficult to set up. But despite all this, Coldfusion is a great platform for putting out web applications very quickly, as I will demonstrate in the next few articles.

The application will take a users input for a Nomination Form, which is a request to take a class, and insert it into a table. There will be a separate Coldfusion script that will check for the presence of data in that table, pull it, email it to a predetermined email address, log it, and clear the table. The setup I am developing for is Coldfusion MX 6, using Macromedia Dreamweaver MX 2004 for development. This is in a totally trusted environment (if there is such a thing), so minimum of input validation will be used. The web forms used in this example were put together by a graphic artist, so I will have to modify her form, and based off this form, the fields for the table have already been defined. I will also use a tool called SecureCFM to check the source code afterwards, doing a small, cheap security audit of the source code.



Figure 1: The web form (modified)

Based on the fields in the form, I created this simple table (its flat, but that’s OK, not everything needs to be normalized, especially for something as simple as this).

create table nom_form
(
participant_f_name varchar2(50),
participant_m_name varchar2(50),
participant_l_name varchar2(50),
participant_soeid varchar2(10),
participant_igeid varchar2(15),
participant_e_mail varchar2(100),
participant_home_phone varchar2(30),
participant_job_title varchar2(50),
participant_start_date date,
participant_hire_date date,
participant_department varchar2(100),
participant_cost_number_checked number(1),
participant_cost_number varchar2(100),
participant_address varchar2(150),
participant_city varchar2(150),
participant_state varchar2(50),
participant_zip varchar2(20),
participant_interoffice varchar2(100),
participant_office_phone varchar2(20),
participant_office_Fax varchar2(20),
manager_name varchar2(100),
manager_id1 varchar2(15),
manager_phone varchar2(20),
manager_email varchar2(100),
class1_event_name varchar2(150),
class1_course_code varchar2(20),
class1_class_id number,
class1_class_date date,
class2_event_name varchar2(150),
class2_course_code varchar2(20),
class2_class_id number,
class2_class_date date,
class3_event_name varchar2(150),
class3_course_code varchar2(20),
class3_class_id number,
class3_class_date date,
class4_event_name varchar2(150),
class4_course_code varchar2(20),
class4_class_id number,
class4_class_date date,
date_processed date
);

With the table create, submitting the information into a form is simply a matter of building the appropriate insert statement, and setting the forms submit action. I will set this form to submit to itself, with a small URL variable indicating that the form has been filled out. Alternatively, I can check for the existence of one of the form field using the isdefined() function. A rough skeleton of the code will look like this:

<cfif not IsDefined("URL.Submit")>
<!--- Code to display form page would go here - - ->
<cfelse>
<cftry>
<!--- Data input validation code goes here -- ->
<!--- Insert statement will go here - - ->
<cfcatch type=”any”>
<!--- Error handeling code goes here - - ->
<cfabort>
</cfcatch>
</cftry>
</cfif>

The form page contains all the fields, named something meaningful (thankfully, my graphic artist did all this for me, and I didn’t even have to tell her). The actual cfquery tag with the insert statement will looks like this:

<cfquery name="insertEvent" datasource="datasource">
insert into nom_form
(
participant_f_name,
participant_m_name,
participant_l_name,
participant_soeid,
participant_geid,
participant_e_mail,
participant_home_phone,
participant_job_title,
participant_start_date,
participant_hire_date,
participant_department,
participant_fc_number_checked,
participant_fc_cost_number,
participant_address,
participant_city,
participant_state,
participant_zip,
participant_interoffice,
participant_office_phone,
participant_office_Fax,
manager_name,
manager_soeid,
manager_phone,
manager_email
<!--- Conditional for class, this will be changed later to be mandatory --->
<cfif form.classid1 neq "">
,
class1_event_name,
class1_course_code,
class1_class_id,
class1_class_date
</cfif>
<!--- Conditional for clas, these will be optional --->
<cfif form.classid2 neq "">
,
class2_event_name,
class2_course_code,
class2_class_id,
class2_class_date
</cfif>
<!--- Conditional for class --->
<cfif form.classid3 neq "">
,
class3_event_name,
class3_course_code,
class3_class_id,
class3_class_date
</cfif>
<!--- Conditional for class --->
<cfif form.classid4 neq "">
,
class4_event_name,
class4_course_code,
class4_class_id,
class4_class_date
</cfif>
)
values
(
<cfqueryparam value="#form.firstname#" cfsqltype="CF_SQL_VARCHAR" maxlength="50">,
<cfqueryparam value="#form.mi#" cfsqltype="CF_SQL_VARCHAR" maxlength="50">,
<cfqueryparam value="#form.lastname#" cfsqltype="CF_SQL_VARCHAR" maxlength="50">,
<cfqueryparam value="#form.soeid#" cfsqltype="CF_SQL_VARCHAR" maxlength="10">,
<cfqueryparam value="#form.geid#" cfsqltype="CF_SQL_VARCHAR" maxlength="15">,
<cfqueryparam value="#form.emailaddress#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">,
<cfqueryparam value="#form.hmphone#" cfsqltype="CF_SQL_VARCHAR" maxlength="30">,
<cfqueryparam value="#form.positiontitle#" cfsqltype="CF_SQL_VARCHAR" maxlength="50">,
<cfqueryparam value="#form.positionstart#" cfsqltype="cf_sql_date">,
<cfqueryparam value="#form.hiredate#" cfsqltype="cf_sql_date">,
<cfqueryparam value="#form.department#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">,
<!--- Need to determine if we are using a branch number or a cost center number --->
<cfif form.costfcselect eq "Cost Center Number">
0,
<cfelse>
1,
</cfif>
<cfqueryparam value="#form.costcenter#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">,
<cfqueryparam value="#form.workstreet#" cfsqltype="CF_SQL_VARCHAR" maxlength="150">,
<cfqueryparam value="#form.workcity#" cfsqltype="CF_SQL_VARCHAR" maxlength="150">,
<cfqueryparam value="#form.workstate#" cfsqltype="CF_SQL_VARCHAR" maxlength="50">,
<cfqueryparam value="#form.workzipcode#" cfsqltype="CF_SQL_VARCHAR" maxlength="20">,
<cfqueryparam value="#form.interofc#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">,
<cfqueryparam value="#form.fcphone#" cfsqltype="CF_SQL_VARCHAR" maxlength="20">,
<cfqueryparam value="#form.fcfax#" cfsqltype="CF_SQL_VARCHAR" maxlength="20">,
<cfqueryparam value="#form.mngrname#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">,
<cfqueryparam value="#form.mngrsoeid#" cfsqltype="CF_SQL_VARCHAR" maxlength="15">,
<cfqueryparam value="#form.mngrphone#" cfsqltype="CF_SQL_VARCHAR" maxlength="20">,
<cfqueryparam value="#form.mngremail#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">

<!--- Conditional for class --->
<cfif form.classid1 neq "">
,
<cfqueryparam value="#form.event1name#" cfsqltype="CF_SQL_VARCHAR" maxlength="150">,
<cfqueryparam value="#form.coursecode1#" cfsqltype="CF_SQL_VARCHAR" maxlength="20">,
<cfqueryparam value="#form.classid1#" cfsqltype="cf_sql_numeric">,
<cfqueryparam value="#form.classdate1#" cfsqltype="cf_sql_date">
</cfif>

<!--- Conditional for class --->
<cfif form.classid2 neq "">
,
<cfqueryparam value="#form.event2name#" cfsqltype="CF_SQL_VARCHAR" maxlength="150">,
<cfqueryparam value="#form.coursecode2#" cfsqltype="CF_SQL_VARCHAR" maxlength="20">,
<cfqueryparam value="#form.classid2#" cfsqltype="cf_sql_numeric">,
<cfqueryparam value="#form.classdate2#" cfsqltype="cf_sql_date">
</cfif>

<!--- Conditional for class --->
<cfif form.classid3 neq "">
,
<cfqueryparam value="#form.event3name#" cfsqltype="CF_SQL_VARCHAR" maxlength="150">,
<cfqueryparam value="#form.coursecode3#" cfsqltype="CF_SQL_VARCHAR" maxlength="20">,
<cfqueryparam value="#form.classid3#" cfsqltype="cf_sql_numeric">,
<cfqueryparam value="#form.classdate3#" cfsqltype="cf_sql_date">
</cfif>
<!--- Conditional for class --->
<cfif form.classid3 neq "">
,
<cfqueryparam value="#form.event4name#" cfsqltype="CF_SQL_VARCHAR" maxlength="150">,
<cfqueryparam value="#form.coursecode4#" cfsqltype="CF_SQL_VARCHAR" maxlength="20">,
<cfqueryparam value="#form.classid4#" cfsqltype="cf_sql_numeric">,
<cfqueryparam value="#form.classdate4#" cfsqltype="cf_sql_date">
</cfif>
)
</cfquery>

And that’s basically it for the form. I save this page as “nomination_form.cfm” and with the additional CFML tags inserted as indicated above, set the form action to point to “nomination_form.cfm?Submit=1”, and the main form page is ready to roll. Notice I use cfqueryparam tags instead of in-lining the values to be passed into the database. The main reason for this is that it is a little faster, maybe not in this case, but definitely in cases where the query will be run repetitively. It also forces the correct datatype into the database. So if a user tries to pass alphanumeric data into a numeric field, it will fail. This will provide some basic input validation to help prevent against SQL Injection attacks since parameters are bound as bind variables in Oracle, not parsed as string literals.

Next, since this page is complete, I run SecureCFM to see if there are any vulnerabilities in it (at least that this tool can detect, something like Nessus would probably be appropriate to use here as well). According to it, the page contains no detectable vulnerabilities.



Getting this up and going was incredible simple compared to a language like ASP or and of the .Net languages. I didn’t have to worry about things like object instantiation, creating ADO objects, commands, or recordsets. Coldfusion is great for getting applications up and running in a short amount of time. That is not to say that it is appropriate for all scenarios. Gauge the appropriate platform based on the requirements of the project.

The remainder of tasks for the project are add-ons, with the exception of the scheduled job that will email data to a pre-determined email address. Next article I will add buttons to lookup the employee information from a pop-up window and pass back the results to the main form. The same method will be used to lookup class information. This allows for the automation of as much information as possible, but allows the user to change information that may be incorrect in the employee database, such as phone numbers, or to allow them to use an alternative cost center or FC number.

No comments: