Friday, October 28, 2005

Input Validation in ASP.Net

I was reading the SecureMe blog the other day (check out their hilarious avatars) and I came across a number of references to “input validation.” I concur with the assessment that failure to use proper input validations is the source of quite a few software flaws, and the number of Cross Site Scripting and SQL Injection vulnerabilities could be minimized if proper input validation were used. The author mentioned how ASP.Net makes input validation easier, so I will demonstrate how to do basic validation in ASP.Net using the Regular Expression Validation Component. For more information about using .Net validation components, refer to this MSDN Library Article. I also came across a short but interesting academic paper placing blame on the failure of the academic community to instill proper fundamentals in developers. I believe this to be true. Incidentally, Slashdot has an article about individuals who learn to program in Visual Studio not getting proper programming fundamentals.

Input Validation is the process of confirming that the input into an application is valid, and handling cases where the input is not valid. The textbook CS example is checking for type errors. For example, a prompt asking for an integer value and the user providing a text string such as a name would result in an invalid type error. There are other types of validation errors, such as the dreaded buffer overflow. Receiving input that is passed directly to a backend system like a database can lead to unexpected results and subsequently to a SQL Injection vulnerability. Web Applications provide a larger layer of complexity than traditional applications due to the separation of the client interface and the backend servers. For Web applications, input validation has to be done on the client side as well as the server side, and also ensure that the inputs sync to prevent client side modification in the absence of server side checking. This added complexity is one of the many reasons I detest using Web interfaces for applications unless absolutely necessary.

In this example I will build a simple C# ASP.Net page in Visual Studio .Net 2002 that will query the employee table of my local database for an employee record with a matching ID. Results will be put into a listbox. The Employee ID is 10 characters, and can only contain numeric characters. There are no alpha characters or special characters.

Before I start, I want to demonstrate the completed application without input validation. You can see where I used a specially crafted input string to return more than just the one employee (the string I used is “ or nm_emp_last like ‘Wa’ –”), in this case all employees whose last names start with “WA”. Had this been a authentication form, I could log in with invalid credentials, or just create havoc with the database.

Lets start building the application to include the validation from the ground up. I will start by going up to File, New, Project, and creating a new project as illustrated below.



I will create a form with a Label, a Textbox, a Listbox, and a Submit button just like in the picture to the below:



I will then add a Regular Expression Validation component. I have a love/hate relationship with regular expressions. I love how powerful regular expressions are, but I hate how difficult they are to read, especially for someone who is not familiar with them; and in my opinion they violate quite a few rules of proper programming techniques. So I will keep my regular expression simple and avoid using any fancy script-fu. The input validation component is indicated in red in the picture above.

I will change the text to read “Invalid Input”, and use the validation expression of \d{10}, which will allow only 10 numeric characters. See the picture below:




Under the ControlToValidate property, I select the txtEmployeeID control



I will add the code depicted below to the cmdSubmit_Click function. This code will create an ADO.Net connection to an Oracle database, query the database with our filled in value, and add all the results to the results listbox. Exception handling is omitted for brevity:

private void cmdSubmit_Click(object sender, System.EventArgs e)
{
     if (Page.IsValid)
     {
          //Local Variables for working with Oracle to retrieve my data.
          System.Data.OleDb.OleDbConnection oraConnect = new System.Data.OleDb.OleDbConnection();
          System.Data.OleDb.OleDbCommand oraCommand = new System.Data.OleDb.OleDbCommand();;
          System.Data.OleDb.OleDbParameter oraParam = new System.Data.OleDb.OleDbParameter("empId", System.Data.OleDb.OleDbType.VarChar, 10);
          System.Data.OleDb.OleDbDataReader oraResults;

          //Set the database connection string and open up the connection
          oraConnect.ConnectionString = "Provider=OraOLEDB.Oracle.1;Password=test1234;Persist Security Info=True;User ID=test;Data Source=test";
          oraConnect.Open();

          //Setup up the database command. First set the conneciton object for
          //the command, the set the command type to test. Then, set the query
          //for retrieving the employee from the database, finally, add the
          //parameter we will be using
          oraCommand.Connection = oraConnect;
          oraCommand.CommandType = CommandType.Text;
          oraCommand.CommandText = "select nm_emp_last ', ' nm_emp_first Name from employees where no_emp = ?";
          oraCommand.Parameters.Add(oraParam);

          //Set the parameters value then execute the query, closing the connection
          //behind it
          oraCommand.Parameters["EmpID"].Value = txtEmployeeID.Text;
          oraResults = oraCommand.ExecuteReader(CommandBehavior.CloseConnection);

          //Traverse through the results, adding to the listbox all matches
          while (oraResults.Read())
               lstResults.Items.Add(oraResults["Name"].ToString());

          //close the connections
          oraResults.Close();
          oraConnect.Close();

          Response.Write("<script>alert(\"SQL Command: " + oraCommand.CommandText + " \");</script>");
     }
     else
          Response.Write("<script>alert(\"Invalid input found, ignoring request\");</script>");
     }
}


The program is complete. I would like to test my Input Validation control, so I save and run the project. I can see that the value of txtEmployeeID is already filled in the Textbox, so I click cmdSubmit. Once I do, I can see that the Invalid Input message appears due to the alpha characters.



Is this a client side validation at this point, or a server side validation? In order to test this, I will capture a session in Ethereal to try and investigate if is being done client side or server side. Although I can get the WebUIValidaton.js file, I feel it is too much work to try and kludge through it, and instead I instead decide to test by saving the POST session and modifying the parameter for txtEmployeeID with something that should fail validation. In order to do this, I find the POST session header as indicated under the Info column, then I right mouse click on it and select Follow TCP Stream. I make sure that ASCII is selected as the format, and click on save as, and save the session as bypassValidation.txt. I then edit the saved text file, remove all the server response information, and modify the txtEmployeeID to read “’ or 1=1 --”, which if successful, will attempt to pull all data from the table. (Thanks to this site for the SQL Injection example. I put the link to the print version to spare you the advertising). The modified example is below.

POST /SearchForEmployees/WebForm1.aspx HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel, application/msword, */*
Referer: http://john/SearchForEmployees/WebForm1.aspx
Accept-Language: en-us
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Host: john
Content-Length: 273
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: ASP.NET_SessionId=gmyrcwf1hhw0porjguqsnqeo

__VIEWSTATE=dDwxOTg4MTczMDcyO3Q8O2w8aTwxPjs%2BO2w8dDw7bDxpPDQ%2BOz47bDx0PHQ8O3A8bDxpPDE%2BO2k8Mj47PjtsPHA8V2FyZCwgSm9objtXYXJkLCBKb2huPjtwPFdhcmQsIEpvaG47V2FyZCwgSm9obj47Pj47Pjs7Pjs%2BPjs%2BPjs%2BJlE8BGSK6UaTCuPYd%2Bdr1fm5whg%3D&txtEmployeeID=' or 1=1 --&cmdSubmit=cmdSubmit

What I am going to do is connect to the Web server via Netcat and pipe in the above text file. Below is the DOS session for that attempt

C:\tmp>type bypassValidation.txt c:\nc localhost 80
HTTP/1.1 100 Continue
Server: Microsoft-IIS/5.0
Date: Thu, 27 Oct 2005 20:50:59 GMT
X-Powered-By: ASP.NET

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 27 Oct 2005 20:50:59 GMT
X-Powered-By: ASP.NET
Connection: close
X-AspNet-Version: 1.1.4322
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 3100

<script>alert("Invalid input found, ignoring request");</script>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>WebForm1</title>
<meta content="Microsoft Visual Studio 7.0" name="GENERATOR">
<meta content="C#" name="CODE_LANGUAGE">
<meta content="JavaScript" name="vs_defaultClientScript">
<meta content="http://schemas.microsoft.com/intellisense/ie5" na
me="vs_targetSchema">
</HEAD>
<body MS_POSITIONING="GridLayout">
<form name="Form1" method="post" action="WebForm1.aspx" language
="javascript" onsubmit="if (!ValidatorOnSubmit()) return false;" id="Form1">
<input type="hidden" name="__VIEWSTATE" value="dDwxOTg4MTczMDcyO3Q8O2w8aTwxPjs+O
2w8dDw7bDxpPDQ+Oz47bDx0PHQ8O3A8bDxpPDE+O2k8Mj47PjtsPHA8V2FyZCwgSm9objtXYXJkLCBKb
2huPjtwPFdhcmQsIEpvaG47V2FyZCwgSm9obj47Pj47Pjs7Pjs+Pjs+Pjs+JlE8BGSK6UaTCuPYd+dr1
fm5whg=" />

<script language="javascript" src="/aspnet_client/system_web/1_1_4322/WebUIValid
ation.js"></script>


<span id="Label1" style="Z-INDEX: 101; LEFT: 17px; POSIT
ION: absolute; TOP: 16px">Enter the Employee ID:</span><input name="txtEmployeeI
D" type="text" value="' or 1=1 --" id="txtEmployeeID" style="Z-INDEX: 102; LEFT:
165px; POSITION: absolute; TOP: 17px" /><input type="submit" name="cmdSubmit" v
alue="cmdSubmit" onclick="if (typeof(Page_ClientValidate) == 'function') Page_Cl
ientValidate(); " language="javascript" id="cmdSubmit" style="Z-INDEX: 103; LEFT
: 21px; POSITION: absolute; TOP: 51px" /><select name="lstResults" size="4" id="
lstResults" style="height:75px;width:325px;Z-INDEX: 104; LEFT: 345px; POSITION:
absolute; TOP: 16px">
<option value="lstResults">lstResults</option>
<option value="Ward, John">Ward, John</option>
<option value="Ward, John">Ward, John</option>

</select>
<span id="RegularExpressionValidator1" controltovalidate
="txtEmployeeID" errormessage="Invalid Input" isvalid="False" evaluationfunction
="RegularExpressionValidatorEvaluateIsValid" validationexpression="\d{10}" style
="color:Red;Z-INDEX: 105; LEFT: 165px; POSITION: absolute; TOP: 47px">Invalid In
put</span>
<script language="javascript">
<!--
var Page_Validators = new Array(document.all["RegularExpressionValidato
r1"]);
// -->
</script>


<script language="javascript">
<!--
var Page_ValidationActive = false;
if (typeof(clientInformation) != "undefined" && clientInformation.appName.indexO
f("Explorer") != -1) {
if (typeof(Page_ValidationVer) == "undefined")
alert("Unable to find script library '/aspnet_client/system_web/1_1_4322
/WebUIValidation.js'. Try placing this file manually, or reinstall by running 'a
spnet_regiis -c'.");
else if (Page_ValidationVer != "125")
alert("This page uses an incorrect version of WebUIValidation.js. The pa
ge expects version 125. The script library is " + Page_ValidationVer + ".");
else
ValidatorOnLoad();
}

function ValidatorOnSubmit() {
if (Page_ValidationActive) {
return ValidatorCommonOnSubmit();
}
return true;
}
// -->
</script>


</form>
</body>
</HTML>

I can see that the script command to alert that an invalid input occurred is there, highlighted in red. I redirect the output to a file and open in IE.

By the pop-up message that was returned, I can tell that the processing is done server-side and the error message is returned to the client via the resulting HTML page. This is good because it helps to prevent an outsider from manipulating the client page to circumvent my validation routine. Had this been done client side, I would still have the alert message with the SQL that I used for debugging. While there are more in-depth tests, I am fairly confident that the Regular Expression Validation control is working. However, if you are rolling out an application, you should never assume that you are completely secure. Remember that there is always someone out there smarter than you. In time someone is bound to find a flaw in the .Net validation components, but these components are a good start toward securing your web applications from input validation attacks if you are using the .Net platform.

No comments: