devxlogo

Five Tips for Thwarting Data Input Attacks Against Your Web App

Five Tips for Thwarting Data Input Attacks Against Your Web App

n my previous “Best Defense” article, I discussed creating secure Win32 applications. Now I turn my attention to another important battlegroundthe Web, where data input attacks are a real danger.

Although many sysadmins have undying faith that their firewalls will protect their systems from all known and unknown attacks, data input attacks come straight in through port 80. Meanwhile, Web site developers often take input from users assuming it is clean, well formed data. Most of the time it is, but a small percentage of bad, often malicious data can cause serious problems.

The Attacker’s User Authentication Workaround
Imagine a simple login screen that takes a username and password from the user, posts the data to the server, and performs a database lookup to authenticate the user. This is a very common scenario. Many developers have code like the following ASP sample to validate the username and password:

<%@language=javascript%><%if (isPasswordOK(Request.form("name"),Request.form("pwd"))) {	Response.write("Authenicated!");	// Do stuff} else {	Response.write("Access Denied");}function isPasswordOK(strName, strPwd) {	var fAllowLogon = false;	try {		var oConn = new ActiveXObject("ADODB.Connection");		var strConnection="Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=c:\auth\auth.mdb;” oConn.Open(strConnection); var strSQL = “SELECT count(*) FROM client WHERE ” + “(name='” + strName + “‘) ” + ” and ” + “(pwd='” + strPwd + “‘)”; var oRS = new ActiveXObject(“ADODB.RecordSet”); oRS.Open(strSQL,oConn); fAllowLogon = (oRS(0).Value > 0) ? true : false; oRS.Close(); delete oRS; oConn.Close(); delete oConn; } catch(e) { fAllowLogon = false; } return fAllowLogon;}%>
Notice that the username and password are taken straight from the post and passed into the evaluation functionan unsecure procedure. The SQL string used to query the database is built directly from data passed from the user. Normally, a well formed SQL string where the account is “mikey” and the password is “&y-)4Hi=Qw8” would look like this:
SELECT count(*) FROM client WHERE (name=’mikey’) and 
(pwd=’&y-)4Hi=Qw8′)
For this account, the user is authenticated and allowed access to the resource when count(*) returns the number of rows that satisfy the query. If the result is zero, then no data was found that matched the query.

So how would an attacker get around the authentication phase? First, most attackers will assume you have a SQL statement like this, and they’ll try to send bogus names and passwords that adjust the logic of the SQL statement. For example, an account named ” x’ or 1) or (‘1″ with the password ” anyoldpassword” results in the following SQL statement:

SELECT count(*) FROM client WHERE (name=’x’ or 1) or (‘1’)
and (pwd=’anyoldpassword’)
If you understand SQL (and I hope you do if you’re writing security-related SQL statements), you know that this always returns at least one row, so the value of count(*) will always be one or greater. The attacker is authenticated because count(*) returned >0, even though they don’t know a valid username or password. Ouch!

Use the following five tips will help you to thwart unauthorized access to your Web apps.

Tip 1?Tell the Attacker Nothing
Don’t return errors that include the text of the failed SQL attempts. Internet Information Services returns a 500-100 error message by default when a scripting error occurs, but returning errors that do not include debugging text is sometimes better. For example, I entered some bogus names and passwords and received error messages like this:
Error Type:Microsoft JET Database Engine (0x80040E14)Syntax error (missing operator) in query expression 
‘(name=’x’ or 1) or (‘1′) and (pwd=”p’)’./login.asp, line 24
As you can see, part of the SQL syntax is returned, which can help an attacker determine how to correctly construct the name and password to create a bogus yet valid expression.

The simplest way to reduce the amount of information returned is to modify the %winnt%helpiisHelp500-100.asp page, or create a new file and configure IIS to use the new file for 500.100 errors. You can achieve this by performing these steps:

  1. Open the IIS admin tool.
  2. Right click the Web server in question.
  3. Select Properties.
  4. Click the Custom Errors tab.
  5. Enter the new filename for your custom 500.100 error page.
While we’re on the subject, never return error messages that include physical locations in the error. For example, presenting a message like this “Unable to find foo.doc at c:wwwrootsecretlocation” is a bad idea. A simple 404 will suffice.Tip 2?Determine What Is Valid Input
Using regular expressions in VBScript, Jscript, and Perl you can define rules for which user input is valid. Do not parse out invalid input because an attacker will find a way around your checks. For example, imagine you look for “<" and ">” symbols to prevent attempts to post HTML data to your site:
strInput = strInput.replace(/[<>]/,””);
This little code snippet replaces “<" and ">” with empty strings. So when an attacker posts some HTML it gets neutered and rendered ineffective, right? Nope! All an attacker needs to do is replace the “<" and ">” with the HTML tags for those symbols and your parsing code no longer catches the HTML. The lesson here is to determine what is legal, verify that input is legal, and toss out everything else. To this end, add the following line just before the try{} body in the isPasswordOK() function:
if (strName.search(/[^A-Za-z 0-9]/) != -1) return false;
This code will search strName and if it contains anything other than uppercase letters, lowercase letters, whitespace , or numbers (that’s what the ^ means), then the input is rejected.

Watch out for bogus filenames too. Attackers will try to post data to funny locations, or request filenames that may return source code and so on. The following regular expression allows only a highly restrictive filename. The rules for the name are:

  1. One or more 0-9a-zA-Z or _ followed by
  2. One or more 0-9a-zA-Z, -, , / and _ followed by
  3. A period followed by
  4. ”, ‘txt’, ‘jpg’, ‘jpeg’, ‘gif’, ‘htm’, ‘html’,
    ‘png’, ‘bmp’, ‘zip’
Everything else is invalid. It’s not perfect and it’s strict, but it works. You’ll notice that the filename cannot start with a back slash because this is the root of the disk drive. Why would anyoneother than an attackerwant to start from the root of your drive? Every attempt at file access should be relative to the root location of the Web site:
var strInput = Request.form(“filename”);var re = /^[w]{1,}[w-/\]{1,}.(txt|jpg|jpeg|gif|htm|html|png|bmp|zip)
{0,1}$/i;var fIsFilenameValid = (re.test(strInput)) ? true : false;
Tip 3?Beware of Quotes
Quotes can be difficult to handle because they can fool SQL strings. As I showed earlier, an attack can use quotes to create different SQL query logic and allow anyone to logon without a valid username and password. Another way to help mitigate quoting attacks is to escape the quote characters first. The following regular expression will double up all single and double quotes. This is perfectly valid SQL syntax, which can help make many attacks harder to execute:
strPwd = strPwd.replace(/([‘”])/g,”$1$1″);
You could use this in place of the regular expression just added. Personally, I use both?defense in depth!Tip 4?Check the Values Returned from the SQL Query
One way to mitigate these attacks completely is to stop using count(*) as the “thumbs up, thumbs down.” Instead, check the username and password against the username and password returned by the SQL query. The syntax looks like this:
var strSQL = “SELECT name, pwd FROM client WHERE ” +		“(name='” + strName + “‘) ” + 		” and ” +		“(pwd='” + strPwd + “‘)”;var oRS = new ActiveXObject(“ADODB.RecordSet”);oRS.Open(strSQL,oConn);fAllowLogon = (oRS(0).Value == strName && oRS(1).Value == strPwd)
? true : false;
If the SQL query returns no data, an exception is generated and then caught by the catch() body.Tip 5?Disable Parent Paths
Make sure that “..” is not allowed in a filename. Disable parent paths with the following steps:
  1. Right-click the root of the Web site and choose Properties from the context menu.
  2. Click the Home Directory tab.
  3. Click Configuration.
  4. Click the App Options tab.
  5. Uncheck the Enable Parent Paths checkbox.
You can also disable the paths from the command-line:
cscript adsutil.vbs set w3svc/1/root/AspEnableParentPaths false
All Input Is Bad
To truly be prepared for data input attacks you have to adopt the mindset that all input is bad. Check for valid input instead of looking for invalid data, because attackers will work around the rules quickly. Also learn regular expressions and use them wisely. Remember these rules and you will reduce the number of attack points for your Web application.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist