Secure ASP.NET coding practice for three most critical vulnerabilities in Web Application
Secure ASP.NET coding practice for 3 most critical vulnerabilities in Web Application
Somnath Guha Neogi (OSCP,CNSM)
Introduction:ASP.NET provides several exciting security controls, but these need to be understood properly and used wisely. Failing to use the ASP.NET functions properly results in an insecure web application. We see therefore that ASP.NET does not exempt the programmer from following coding standards and procedures in order to write safe and secure application code.
In this paper we will discuss about the code level mitigation for three most frequently found vulnerabilities:
Cross Site Scripting
SQL Injection
Information Leakage
Cross Site Scripting:An application is vulnerable to Cross Site Scripting if malicious user input is embedded in the HTML response without passing through any particular validation process. Let’s take a look on a vulnerable chunk of code
<%@ Page ValidateRequest="false" %>
<html>
<script runat="server">
void buttonsubmit_Click(Object sender, EventArgs e)
{
Response.Write(comment.Text);
}
</script>
<body>
<form runat="server">
<asp:TextBox runat="server" />
<asp:Button runat="server"
Text="SubmitComment" />
</form>
</body>
</html>
Now an attacker can send malicious request with embedded JavaScript through the comment textbox which will be executed at the client’s browser. To see that this is possible, the above vulnerable script can be fed with the following input:
<script>alert([removed])</script>
Now this type of script injection attack can be mitigated by adopting a two tire security approach. User input validation will form the first tire of security while HTML-encoding on outgoing user data will form a second layer of security. So we can start assuming that all user input is malicious and to safely allow restricted HTML input developers/testers should adopt three security approaches as follows:
a) Add the ValidateRequest="false" attribute to the @ Page directive to disable the ASP.NET request validation.
b) Encode the string input with HtmlEncode function.
c) White listing approach can be adopted by using a String Builder and calling its Replace method to selectively remove the encoding on the HTML elements that you want to permit.
The following .aspx code depicts this as an example.
<%@ Page ValidateRequest="false"%>
<script runat="server">
void submitbutton_Click(object sender, EventArgs e)
{
StringBuilder stringbuilder1 = new StringBuilder(
HttpUtility.HtmlEncode(Txt1.Text));
// Selectively allow <b> and <i>
stringbuilder1.Replace("<b>", "<b>");
stringbuilder1.Replace("</b>", "");
stringbuilder1.Replace("<i>", "<i>");
stringbuilder1.Replace("</i>", "");
Response.Write(stringbuilder1.ToString());
}
</script>
<html>
<body>
<form runat="server">
<div>
<asp:TextBox Runat="server"
TextMode="MultiLine" Width="318px"
Height="168px"></asp:TextBox>
<asp:Button Runat="server"
Text="Submit" OnClick="submitbutton_Click" />
</div>
</form>
</body>
The above .aspx page code shows this approach. The page disables ASP.NET request validation by setting ValidateRequest="false". It HTML-encodes the input and then selectively allows the <b> and <i> HTML elements to support simple text formatting.
Now the second tire of security can be brought into the frame by encoding the output to know that the text contains HTML special characters or not.
Response.Write(HttpUtility.HtmlEncode(Request.Form["text"])); Or in case of URL strings that contain input to the client.Response.Write(HttpUtility.UrlEncode(urlString));
As a result, the HTML response stream of the malicious input <script>alert([removed])</script> will look like this
<script>alert([removed])</script>
This will ultimately restrict the browser to execute the Javascript code because no HTL <script> tag is present any more in the response.The greater-than and less-than symbols are replaced by their HTML-encoded output,< and > respectively.
In addition to this two tire security approach discussed above, we can also use the following countermeasures to prevent cross site scripting as further safe guards.
Setting the correct character encoding:
Character encoding can be done in page level or in configuration level. To set the Character encoding at the page level we can use <meta> element or the ResponseEncoding page-level attribute as follows:
<% @ Page ResponseEncoding="iso-8859-1" %> R <meta http-equiv="Content Type" content="text/html; charset=ISO-8859-1" />To set the Character encoding at the configuration level we have to bring certain changes in Web.config file as follows:
<configuration> <system.web> <globalization requestEncoding="iso-8859-1" responseEncoding="iso-8859-1"/> </system.web> </configuration>Use white listing approach rather than black listing:
Sanitizing user input by filtering out known malicious characters is a common practice. But we should not rely on this approach because an attacker can usually find an alternative means of bypassing your validation. Instead, your code should check for known secure, safe input. There are other safe ways of representing these malicious characters. For example < (less than) and > (greater than) can be represented as < and > respectively.
Using the HttpOnly Cookie Option:
HttpOnly cookie attribute is supported by Internet Explorer 6 Service Pack 1 and later, which prevents client-side scripts from accessing a cookie from the [removed] property. Instead, the script returns an empty string. The cookie is still sent to the server whenever the user browses to a Web site in the current domain.
Secure coding practice in ASP.NET against SQL injection vulnerability should focus on the following countermeasures:
Constrain user supplied input
Before applying any countermeasure at the code level we should be concerned about the potential risk associated with denying a list of unacceptable characters (blacklisting) because it is always possible to overlook an unacceptable character when defining the list. Also this kind of validation approach can be easily bypassed by representing an unacceptable character in an alternate format.
ASP.NET server side validator controls, such as the RegularExpressionValidator and RangeValidator controls can be used to constrain input. Alternatively we can also the Regex class in our server-side code to constrain input.
When user input is captured by an ASP.NET TextBox control, we can constrain its input by using a RegularExpressionValidator control as shown in the following aspx code..
<%@ %> <form runat="server"> <asp:TextBox runat="server"/> <asp:RegularExpressionValidator runat="server" ErrorMessage="Incorrect data" ControlToValidate="text1" ValidationExpression="^\d{3}-\d{2}-\d{4}$" /> </form> If the user input is from another source, such as an HTML control, a query string parameter, or a cookie, you can constrain it by using the Regex class from the System.Text.RegularExpressions namespace. The following example assumes that the input is obtained from a cookie. if (Regex.IsMatch(Request.Cookies["SSN"], "^\d{3}-\d{2}-\d{4}$")) { // perform the database task } else { // handle exception }User supplied input parameters need to be validated before being used in SQL statements. The following data access routine can be taken as an example of how validate user input parameters.
using System; using System.Text.RegularExpressions; public void useraccount(string username, string password) { // check username contains only lower case or upper case letters, // the apostrophe, a dot, or white space. Also check it is // between 1 and 40 characters long if ( !Regex.IsMatch(userIDTxt.Text, @"^[a-zA-Z'./s]{1,40}$")) throw new FormatException("Invalid username format"); // Check password contains at least one digit, one lower case // letter, one uppercase letter, and is between 8 and 10 // characters long if ( !Regex.IsMatch(passwordTxt.Text, @"^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$" )) throw new FormatException("Invalid password format"); // Perform data access operation (using type safe parameters) ... }Use parameterized stored procedures:
The following code shows how to use parameters with stored procedures.
using System.Data; using System.Data.SqlClient; using (SqlConnection connection = new SqlConnection(connectionString)) { DataSet userDataset = new DataSet(); SqlDataAdapter myCommand = new SqlDataAdapter( "LoginStoredProcedure", connection); myCommand.SelectCommand.CommandType = CommandType.StoredProcedure; myCommand.SelectCommand.Parameters.Add("@au_id", SqlDbType.VarChar, 12); myCommand.SelectCommand.Parameters["@au_id"].Value = SSN.Text; myCommand.Fill(userDataset); } In the above example the @au_id parameter is treated as a literal value and not as executable code. Also, the parameter is checked for type and length. In the preceding code example, the input value cannot be longer than 12 characters. If the data does not conform to the type or length defined by the parameter, the SqlParameter class throws an exception.Note: Using stored procedure with parameters does not necessarily prevent SQL injection.Take a look at the following stored procedure:
CREATE PROCEDURE dbo.RunQuery @var ntext AS exec sp_executesql @var GONow despite being a parameterized stored procedure , this one executes whatever is passed to it.Consider the @var variable being set to:
DROP TABLE USERS;
Use parameterized dynamic sql:
Now if you are not using stored procedure, you still should use parameters when constructing dynamic SQL statements. The following code shows how to use parameters with dynamic SQL statement.
using System.Data; using System.Data.SqlClient; using (SqlConnection connection = new SqlConnection(connectionString)) { DataSet userDataset = new DataSet(); SqlDataAdapter myDataAdapter = new SqlDataAdapter( "SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id", connection); myCommand.SelectCommand.Parameters.Add("@au_id", SqlDbType.VarChar, 11); myCommand.SelectCommand.Parameters["@au_id"].Value = SSN.Text; myDataAdapter.Fill(userDataset); }Using a least privileged database account:
Your application should connect to the database by using a least-privileged account. If you use Windows authentication to connect, the Windows account should be least-privileged from an operating system perspective and should have limited privileges and limited ability to access Windows resources. Additionally, whether or not you use Windows authentication or SQL authentication, the corresponding SQL Server login should be restricted by permissions in the database.
If your ASP.NET application only performs database lookups and does not update any data, you only need to grant read access to the tables. This limits the damage that an attacker can cause if the attacker succeeds in a SQL injection attack.
Avoid Disclosing Error Information
Use structured exception handling to catch errors and prevent them from propagating back to the client. Log detailed error information locally, but return limited error details to the client.
If errors occur while the user is connecting to the database, be sure that you provide only limited information about the nature of the error to the user. If you disclose information related to data access and database errors, you could provide a malicious user with useful information that he or she can use to compromise your database security. Attackers use the information in detailed error messages to help deconstruct a SQL query that they are trying to inject with malicious code. A detailed error message may reveal valuable information such as the connection string, SQL server name, or table and database naming conventions.
Information leakage: Remember that __VIEWSTATE data can be viewed
The __VIEWSTATE’s Base64 encoding can be easily decoded, and the __VIEWSTATE data can be exposed with minimal effort. Now the attacker can see the information that may be sensitive, such as internal state data of the application.To encrypt the __VIEWSTATE data we have to add the machineKey attribute in web.config file as follows:
<configuration>
<system.web>
<machineKey validation="3DES"/>
</system.web>
</configuration>
About the Author:Somnath has been working as an Information Security Consultant iViZ Techno Solutions,India and have successfully carried out countless assignments on vulnerability assessment, penetration testing, web application security, Threat modeling,PCI DSS Compliance for various Banking sector firms, financial institutions, Govt. organizations, Defense, Software development Companies, leading BPOs and various small-mid-large industries.He holds security certifications like OSCP and CNSM.
Article Source: ArticlesBase.com - Secure ASP.NET coding practice for three most critical vulnerabilities in Web Application