form data into database: preventing SQL injection - coldfusion

It has recently been mentioned to be that our method of inserting data into our SQL database via form submission is subject to SQL injection attacks, and want some advice to harden our security.
Here's the code that inserts form data into the DB:
<cfquery name="InsRegistrant" datasource="#application.Datasource#" dbtype="odbc">
INSERT INTO Schedule_Registrations(
schedule_id,
first_name,
last_name,
phone_number,
email,
guest,
list_type,
datetime_registered
)
VALUES(
#url.schedule_id#,
'#FORM.first_name#',
'#FORM.last_name#',
'#CleanPhoneNumber#',
'#FORM.email#',
#attendee.guest#,
<!--- Values for list types
0 = NEVER USE Will cause many many problems
1 = Main List
2 = Waiting List --->
#attendee.list_type#,
#createodbcdatetime(now())#
)
</cfquery>
CleanPhoneNumber is set this way:
<cfset CleanPhoneNumber = REReplace(form.phone_number, "[^0-9]", "", "ALL") />
I've been told to use, for instance,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.phone_number#" />
but I'm not sure what to replace and where. When I replace the values with such I get an error.
Any direction would be helpful..

You should wrap all form and url variables in cfqueryparam
Your query would look like this:
<cfquery name="InsRegistrant" datasource="#application.Datasource#" dbtype="odbc">
INSERT INTO Schedule_Registrations(
schedule_id,
first_name,
last_name,
phone_number,
email,
guest,
list_type,
datetime_registered
)
VALUES(
<cfqueryparam cfsqltype="cf_sql_integer" value="#url.schedule_id#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.first_name#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.last_name#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#CleanPhoneNumber#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.email#">,
<cfqueryparam cfsqltype="cf_sql_integer" value="#attendee.guest#">,
<!--- Values for list types
0 = NEVER USE Will cause many many problems
1 = Main List
2 = Waiting List --->
<cfqueryparam cfsqltype="cf_sql_integer" value="#attendee.list_type#">,
#createodbcdatetime(now())#
)
</cfquery>
I'm not sure I got all the data types correct, see the full documentation of cfqueryparam for all the data types.

There are several good practices you can do.
For the insert code you have provided one of the things you can do is explicitly check the input of the Form Fields before inserting the data.Check for things like spaces and "'". You also want to ensure that the user does not see your error messages from bad data entered. This is useful to somebody wanting to know your table structure.
Otherwise place the insert in a stored procedure and validate the input parameters before calling the stored procedure for the insert or update.
Here is a good list of things you can do to prevent SQL injection attacks. It is related to asp.net but the concepts still apply no matter what language you are using.
How To: Protect From Injection Attacks in ASP.NET

Related

Processing Incoming Mail from Mailgun with Coldfusion

I've just started using Mailgun for 2-way emails within a web app I'm developing for in-house use. I have a route set up to forward messages to a URL on my server - which is working fine.
However, the code at my end to process these messages is throwing errors. Malign sends the data as a http post, which I can then reference with a form variable. Initially I was getting 'not defined' errors on some fields (those with hyphens in the field names), however that now appears to be resolved. The code is now stumbling on the CFQUERY insert, with the error stating I have an error in the SQL syntax - but I can't see anything wrong with it!
This is the code I have for the page mailgun sends the post to;
<cfset thebody = form["body-plain"]>
<cfset thesender = form["sender"]>
<cfset therecipient = form["sender"]>
<cfset thesubject = form["subject"]>
<cfquery name="addmail">
INSERT INTO mailmessages(from,sender,recipient,subject,body,msgdate)
VALUES('#thesender#','#thesender#','#therecipient#','#thesubject#','#thebody#',#CreateODBCDateTime(Now())#)
</cfquery>
The error message states;
Error Executing Database Query. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'from,sender,recipient,subject,body,msgdate) VALUES('lee#mydomain.com','l' at line 1
A cfdump of the form can be seen here (this is sent to be via cfmail in a cfcatch statement - the only way I'm able to see any errors);
The from column name is a reserved word, so this is what mySQL is complaining about.
If you escape the column name, it should fix this.
Along with cfqueryparam your code should be something like:
<cfset thebody = form["body-plain"]>
<cfset thesender = form["sender"]>
<cfset therecipient = form["sender"]>
<cfset thesubject = form["subject"]>
<cfquery name="addmail">
INSERT INTO mailmessages (
"from",
sender,
recipient,
subject,
body,
msgdate
)
VALUES (
<cfqueryparam value="#thesender#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#thesender#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#therecipient#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#thesubject#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#thebody#" cfsqltype="cf_sql_varchar">,
<cfqueryparam value="#CreateODBCDateTime(Now())#" cfsqltype="cf_sql_timestamp">
)
</cfquery>
As the error message suggests, there is a syntax error in the insert query.
Seems like you are missing single quotes (') around the date value you are passing. It should be written like this:
<cfquery name="addmail">
INSERT INTO mailmessages(from,sender,recipient,subject,body,msgdate)
VALUES('#thesender#','#thesender#','#therecipient#','#thesubject#','#thebody#','#CreateODBCDateTime(Now())#')
</cfquery>
To get rid of such syntax errors and prevent SQL injection always use cfqueryparams like this:
<cfquery name="addmail">
INSERT INTO mailmessages (from,sender,recipient,subject,body,msgdate)
VALUES (
<cfqueryparam value="#thesender#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#thesender#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#therecipient#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#thesubject#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#thebody#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#CreateODBCDateTime(Now())#" cfsqltype="CF_SQL_TIMESTAMP">
)
</cfquery>
Instead of CF_SQL_TIMESTAMP you can also use CF_SQL_DATE as per your DB or data type you have specified for the column.
I can't select Jedihomer Townend's comment above as the answer, but he was correct to point out that my fieldnames contained reserved words, and that was causing the problem. Changing those fixed it.

How to loop form values depending on form column name?

I have a form containing 6 check_num fields, 1 account number and an amount. I would like to insert those values into a database table:
<cfquery datasource="test" name="test">
insert into test (ACCOUNT_NUMBER,check_num,amount)
values ('#ACCOUNT_NUMBER#','#check_num#','#amount#')
</cfquery>
However, I don't want to insert all of the values into 1 row. If any of the check_num fields contain a number, I want to insert that value, along with with the acccount_number and amount values.
For example with the data below, I would like to insert 5 rows. The field "check_num6" would be skipped because it is empty.
ACCOUNT_NUMBER 123456789
CHECK_NUM1 56623
CHECK_NUM2 5512
CHECK_NUM3 6562
CHECK_NUM4 653
CHECK_NUM5 6623
CHECK_NUM6
AMOUNT 32.31
FIELDNAMES ACCOUNT_NUMBER,CHECK_NUM1,CHECK_NUM2,CHECK_NUM3,CHECK_NUM4,CHECK_NUM5,CHECK_NUM6,AMOUNT,SUBMIT
SUBMIT Submit
Not perfect but you can start/get some inspiration from here:
<cfloop list="#StructKeyList(form)#" index="i">
<cfif (left('#i#', 9) is "CHECK_NUM") and (trim(form['#i#']) is not "")>
<cfquery datasource="test" name="test">
insert into test (ACCOUNT_NUMBER,check_num,amount)
values ('#ACCOUNT_NUMBER#','#form['#i#']#','#amount#')
</cfquery>
</cfif>
</cfloop>
Also you should validate, see if your DB supports multiple inserts, change your query to use cfqueryparam, research about it. So, a better cfquery code would be:
<cfquery datasource="test" name="test">
insert into test (
ACCOUNT_NUMBER,
check_num,
amount
)
values (
<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.ACCOUNT_NUMBER#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#form['#i#']#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#form.amount#">
)
</cfquery>

Cfquery causing XSS and SQL Injection issues

I ran an application scan (IBM) recently and now I'm trying to fix the issues that came up. A good majority of my issues stem from a cfquery (see below). I'm trying to get it so that Stored XSS and SQL injections issues don't show up on my scans. Any help would be greatly appreciated, as this is my first time doing something of the sort.
Thanks!
<cfquery name="enter_question" datasource="#dsn#">
INSERT INTO xx_questions(q_id,
q_name,
q_narrative,
q_used,
q_type)
VALUES( #variables.new_q_id#,
'#form.q_name#',
'#form.q_narrative#',
'n',
#form.q_type#)
</cfquery>
Each query that contains user inputs should have cfqueryparam like so:
<cfquery name="enter_question" datasource="#dsn#">
INSERT INTO xx_questions(q_id,
q_name,
q_narrative,
q_used,
q_type)
VALUES(
<cfqueryparam cfsqltype="CF_SQL_INTEGER" value="#variables.new_q_id#"/>,
<cfqueryparam cfsqltype="CF_SQL_CHAR" value="#form.q_name#"/>,
<cfqueryparam cfsqltype="CF_SQL_CHAR" value="#form.q_narrative#"/>,
<cfqueryparam cfsqltype="CF_SQL_CHAR" value="n"/>,
<cfqueryparam cfsqltype="CF_SQL_INTEGER" value="#form.q_type#"/>)
</cfquery>
The cfsqltype will need to reflect the data type in the xx_questions table for the cooresponding column - but you get the idea.
Binding the variable to a type in this manner automatically protects you against SQLi injection. XSS injection is something else. That's when user input is output onto the page. If a user pushes something (say a script tag) into a column in the DB and then it is output onto the page you haven't really suffered from injection (they gave you a character column, you put in a character column, they did not change the nature of the query). But in a forum app or any other area where user input from one user is shown to another you have the potential for malicious code infections.
For that each of your character types needs to be vetted for malicious code. Using something like Foundeo as a Web application Firewall can do this. You can also protect your page at a server level using the CF admin or at an application level using the scriptprotect attribute of the cfapplication tag or props in the application.cfc.
You can also manually protect within your DB inserts using HTMLEditformat like so:
<cfqueryparam cfsqltype="CF_SQL_CHAR" value="#htmleditformat(form.q_narrative)#"/>,
... or you can check each user input against a list of known allowed inputs (if the data is predictable). These techniques should be part of your "best practice" list if possible. :)
You need to use <cfqueryparam>. Check the documentation at: https://wikidocs.adobe.com/wiki/display/coldfusionen/cfqueryparam
Try something like this (you should change the CFSQLType to match whatever your DB columns are):
<cfquery name="enter_question" datasource="#dsn#">
INSERT INTO xx_questions(q_id,
q_name,
q_narrative,
q_used,
q_type)
VALUES(
<cfqueryparam value="#variables.new_q_id#" CFSQLType="CF_SQL_INTEGER">,
<cfqueryparam value="#form.q_name#" CFSQLType="CF_SQL_VARCHAR">,
<cfqueryparam value="#form.q_narrative#" CFSQLType="CF_SQL_VARCHAR">,
<cfqueryparam value="n" CFSQLType="CF_SQL_CHAR">,
<cfqueryparam value="#form.q_type#" CFSQLType="CF_SQL_INTEGER">
)
</cfquery>

Tackling Null Values while Inserting data in data base

I have the following cfquery:
<cfquery name="CQuery" datasource="XX.X.X.XXX">
INSERT INTO DatabaseName
(PhoneNumber_vch,
Company_vch,
date_dt)
VALUES(#PhoneNumber#,
#Company#,
#Date# )
</cfquery>
There are null values in case Company name doesnt' exist and I believe becasue of that Iam getting the following error:
Error Executing Database Query.
[Macromedia][SQLServer JDBC Driver][SQLServer]Incorrect syntax near ','.
The comma , pointed in the error message is after #Company# field. Please let me know is it's because of null values and best approach to fix it?
The values in the PhoneNumber, company and Date are extracted from a XML SOAP response with proper usage of trim function as discussed in my previous post.
Using cfif in coldfusion
Thanks
If you use CFQueryParam like you should on any database SQL that accepts dynamic parameters you can kill two birds with one stone. First and most important, prevent SQL Injection Attacks and second you can use the attribute of the null="" to insert a NULL value into your record.
<cfquery name="CQuery" datasource="XX.X.X.XXX">
INSERT INTO DatabaseName (PhoneNumber_vch, Company_vch, date_dt)
VALUES(
<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(PhoneNumber)#" null="#NOT len(trim(PhoneNumber))#" />
,<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(Company)#" null="#NOT len(trim(Company))#" />
,<cfqueryparam cfsqltype="cf_sql_timestamp" value="#Date#" null="#NOT len(trim(Date))#" />
)
</cfquery>
You will want to use <cfqueryparam> to take care of nulls (and injection attacks)
Try
<cfquery name="CQuery" datasource="XX.X.X.XXX">
INSERT INTO DatabaseName
(PhoneNumber_vch,
Company_vch,
date_dt)
VALUES(
<cfqueryparam value = "#PhoneNumber#" cfsqltype = "CF_SQL_VARCHAR">,
<cfqueryparam value = "#Company#" cfsqltype = "CF_SQL_VARCHAR"
null = "#IIF(Company EQ "", 1, 0)#">,
<cfqueryparam value = "#Date#" cfsqltype = "CF_SQL_TimeStamp"
null = "#IIF(Date EQ "", 1, 0)#" >
)
</cfquery>
Also see: cfqueryparam
You either need to qualify your varchar entries (surround all varchar entries with single quotes, or, better would be change them to cfqueryparams;
<cfquery name="CQuery" datasource="XX.X.X.XXX">
INSERT INTO DatabaseName
(PhoneNumber_vch,
Company_vch,
date_dt)
VALUES(<cfqueryparam value="#PhoneNumber#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#Company#" cfsqltype="CF_SQL_VARCHAR">,
<cfqueryparam value="#Date#" cfsqltype="CF_SQL_TIMESTAMP"> )
</cfquery>

Check form submission against data in database?

We have a registration system that we're using where people fill out a form, and their info is put into our SQL database. However, we don't have a system in place to check for duplicate registrations, and I'm hoping to get some guidance on how to check maybe name or email address and set an error.
Here's our error setting code (one example, we have several checks):
<cfif not len(trim(form.last_name)) or form.last_name eq "Last Name">
<cfset errors = errors & "<li>You must include your complete last name.</li>">
</cfif>
If no errors are found, here is the query code to insert the data into the database:
<cfquery name="InsRegistrant" datasource="#application.Datasource#" dbtype="odbc">
INSERT INTO Schedule_Registrations(
schedule_id,
first_name,
last_name,
phone_number,
email,
guest,
list_type,
datetime_registered
)
VALUES(
#url.schedule_id#,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.first_name#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.last_name#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#CleanPhoneNumber#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#FORM.email#">,
#attendee.guest#,
<!--- Values for list types
0 = NEVER USE Will cause many many problems
1 = Main List
2 = Waiting List --->
#attendee.list_type#,
#createodbcdatetime(now())#
)
</cfquery>
There's no "unique" identifier tied to a specific individual, so I'm thinking the best way is to prevent registrations that match someone with an identical first/last name or even just the email address. Any ideas on how to implement such a function using the above system would be helpful.
This is simplified but.. you get the idea.
<cfquery name="RegistrantEmailExists" datasource="#application.Datasource#" dbtype="odbc">
SELECT COUNT(email) as EmailExists FROM Schedule_Registrations WHERE email = <cfqueryparam value="#form.email#" cfsqltype="cf_sql_varchar" >
</cfquery>
<cfif RegistrantEmailExists.EmailExists gt 0>
<cfset errors = errors & "<li>That Email Address already exists.</li>">
</cfif>
You could also set the email column in the database as a primary key, which would definitely prevent duplicate registrations. You would of course have to gracefully handle the duplicate primary key which would be thrown from the DB!
Placing a unique key on the mail-column would be the easier way than changing the primary key. But you've also to handle the error thrown by the db
<cfquery name="checkdup" datasource="whatever">
Select 1 from Schedule_Registrations where email=#form.email#
</cfquery>
<cfif #checkdup.recordcount# is 0>
<cfquery name="doyourquery" datasource="whatever">
insert into Schedule_Registrations
(email,etc1,etc2) values ('#form.email#','#etc1#','#etc2#')
</cfquery>
Thanks for registering
<cfelse>
You have already registered an account
</cfif>
And yes, all the cfqueryparam stuff you'd need would go in there. This is just the basics of it.
Good Luck :)
WHeis