ASP.NET Development Secure Guide

SQL Injection Vulnerability

Vulnerability Details and Security Countermeasures

Websites interface with DBMS, and data transactions occur through user input values in web applications. If validity verification for user input is omitted, a malicious user can transmit manipulated inputs containing SQL query statements instead of normal values to alter the structure of the server-side query. Through this, they can bypass user authentication, leak database information, or even execute system commands on the server.

Security Countermeasures

Input Restriction

Validate all inputs for ASP.NET applications regarding type, length, format, and range. By restricting the input used in data access queries, you can protect the application from SQL injection.

Ref: To restrict input, use regular expressions that create a list of allowed characters and reject all characters not on that list.

    SQL Injection Vulnerability

    Vulnerability Details and Security Countermeasures Websites interface with DBMS, and data transactions occur through user input values in web applications. If validity verification for user input is omitted, a malicious user can transmit manipulated inputs containing SQL query statements instead of normal values to alter the structure of the server-side query. Through this, they can bypass user authentication, leak database information, or even execute system commands on the server.

    Security Countermeasures Input Restriction Validate all inputs for ASP.NET applications regarding type, length, format, and range. By restricting the input used in data access queries, you can protect the application from SQL injection.

    Ref: To restrict input, use regular expressions that create a list of allowed characters and reject all characters not on that list.

    A. Restricting ASP.NET Web Page Input Restrict server-side code input for ASP.NET web pages.

    Example: Capturing an SSN value and restricting text box input via ASP.NET

    <%@ language="C#" %>
    <form id="form1" runat="server">
        <asp:TextBox ID="SSN" runat="server"/>
        <asp:RegularExpressionValidator ID="regexpSSN" runat="server"         
                                        ErrorMessage="Incorrect SSN Number" 
                                        ControlToValidate="SSN"         
                                        ValidationExpression="^\d{3}-\d{2}-\d{4}$" />
    </form>

    Example: Restricting usage for other sources like HTML controls, query string parameters, or cookies for SSN input

    if (Regex.IsMatch(Request.Cookies["SSN"], "^\d{3}-\d{2}-\d{4}$"))
    {
        // access the database
    }
    else
    {
        // handle the bad input
    }

    B. Restricting Data Access Code Input

    Provide validation for data access code.

    • Untrusted Clients: Data may come from untrusted sources; therefore, add validation logic to restrict input for data access routines.
    • Library Code: If data access code is packaged as a library designed for use by multiple applications, the data access code should perform its own validation to ensure security.

    Example: Validating input parameters using regular expressions before data access routines using parameters in SQL statements

    using System;
    using System.Text.RegularExpressions;
    
    public void CreateNewUserAccount(string name, string password)
    {
        // Check name 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 name 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 logic (using type safe parameters)
        ...
    }

    Using Parameters in Stored Procedures

    Using stored procedures does not necessarily prevent SQL injection by itself. The critical point is to use stored procedure parameters. If parameters are not used and unfiltered input is utilized, the stored procedure remains vulnerable to SQL injection.

    Example: Calling a stored procedure

    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, 11);
      myCommand.SelectCommand.Parameters["@au_id"].Value = SSN.Text;
    
      myCommand.Fill(userDataset);
    }

    A. Reviewing Application Use of Parameterized Stored Procedures Review the application’s use of stored procedures, as simply using them does not guarantee prevention of SQL injection if they internally use dynamic SQL.

    Example: Application using a vulnerable stored procedure

    CREATE PROCEDURE dbo.RunQuery
    @var ntext
    AS
            exec sp_executesql @var
    GO

    Using Parameters in Dynamic SQL

    If stored procedures cannot be used, use parameters when generating dynamic SQL statements.

    Example: Using dynamic SQL parameters

    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);
    }

    A. Using Batch Processing Parameters When concatenating SQL text, ensure unique parameter names are used for operations.

    Example: Using batch processing parameters

    using System.Data;
    using System.Data.SqlClient;
    . . .
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
      SqlDataAdapter dataAdapter = new SqlDataAdapter(
           "SELECT CustomerID INTO #Temp1 FROM Customers " +
           "WHERE CustomerID > @custIDParm; SELECT CompanyName FROM Customers " +
           "WHERE Country = @countryParm and CustomerID IN " +
           "(SELECT CustomerID FROM #Temp1);",
           connection);
      SqlParameter custIDParm = dataAdapter.SelectCommand.Parameters.Add(
                                              "@custIDParm", SqlDbType.NChar, 5);
      custIDParm.Value = customerID.Text;
    
      SqlParameter countryParm = dataAdapter.SelectCommand.Parameters.Add(
                                          "@countryParm", SqlDbType.NVarChar, 15);
      countryParm.Value = country.Text;
    
      connection.Open();
      DataSet dataSet = new DataSet();
      dataAdapter.Fill(dataSet);
    }
    . . .

    XSS (Cross-Site Scripting) Vulnerability

    Vulnerability Details and Security Countermeasures

    XSS refers to a vulnerability where client-side scripts (such as JavaScript or VBScript) are executed in another user’s web browser, allowing an attacker to control the browser and attack the PC. These are categorized into two main types:

    (1) Reflected XSS
    This occurs when data sent from the client is immediately processed by the server and sent back to the user as a response. It typically happens in the following scenarios:

    • When a search term entered on a search page is displayed on the results page.
    • When a specific parameter in a URL is included as a hidden attribute in the response page.

    These types are essentially the same. Attackers use social engineering to trick victims into clicking a URL containing a malicious script (e.g., sending links via messenger or posting links on bulletin boards). While not always classified as “critical” because it requires user interaction, it must be removed in sectors where trust is paramount (public institutions, finance) and is generally recommended for removal in any public-facing website.

    (2) Stored XSS This is the most critical XSS vulnerability. It occurs in applications where user input is stored in a server-side database (like a bulletin board) and then displayed to other users when they view that information. This poses a severe threat because it can execute scripts on the browsers of an unspecified number of visitors. Malicious scripts can be inserted not just in the post body, but also in:

    • Bulletin Boards: Titles, author names, dates, attachment filenames, tags, and categories.
    • User Profiles: Names, nicknames, addresses, phone numbers, emails, occupations.
    • Any other data field where a user can provide input.

    Security Countermeasures

    Verify if ASP.NET Request Validation is Enabled

    By default, request validation is enabled in the Machine.config file. Ensure that this is active and not overridden in the application’s Web.config.

    A. Testing Procedure to Verify ASP.NET Validation

    1. Create an ASP.NET page with validation disabled by setting ValidateRequest="false".
    <%@ Page Language="C#" ValidateRequest="false" %>
    <html>
     <script runat="server">
      void btnSubmit_Click(Object sender, EventArgs e)
      {
        // If ValidateRequest is false, then 'hello' is displayed
        // If ValidateRequest is true, then ASP.NET returns an exception
        Response.Write(txtString.Text);
      }
     </script>
     <body>
      <form id="form1" runat="server">
        <asp:TextBox id="txtString" runat="server" 
                     Text="<script>alert('hello');</script>" />
        <asp:Button id="btnSubmit" runat="server"   
                    OnClick="btnSubmit_Click" 
                    Text="Submit" />
      </form>
     </body>
    </html>

    2. Run the page. If the script renders and a message box appears, the input was treated as a client-side script.

    3. Reset ValidateRequest="true" and verify that the following error message appears:

    A potentially dangerous Request.Form value was detected from the client (txtString="<script>alert('hello...").

    Review ASP.NET Code Generating HTML Output

    ASP.NET Code Examples for Reviewing HTML Output Generation

    Response.Write
    <% =

    Check Whether HTML Output Includes Input Parameters

    You must analyze your page design and code to determine if the output includes input parameters. These parameters can originate from a variety of sources.

    Examples of Common Input Sources:

    • Form Fields
    Response.Write(name.Text);
    Response.Write(Request.Form["name"]);
    Query Strings
    Response.Write(Request.QueryString["name"]);
    • Query Strings
    Response.Write(Request.QueryString["username"]);
    • Database and Data Access Methods
    SqlDataReader reader = cmd.ExecuteReader();
    Response.Write(reader.GetString(1));
    • Cookie Collections
    Response.Write(
    Request.Cookies["name"].Values["name"]);
    • Session and Application Variables
    Response.Write(Session["name"]);
    Response.Write(Application["name"]);

    Review Potentially Dangerous HTML Tags and Attributes

    When dynamically generating HTML, encode tag attributes if they use unsafe input. Example: Using Server.HtmlEncode to safely handle color input.

    <%@ Page Language="C#" AutoEventWireup="true"%>
    
    <html>
      <form id="form1" runat="server">
        <div>
          Color:&nbsp;<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
          <asp:Button ID="Button1" runat="server" Text="Show color" 
             OnClick="Button1_Click" /><br />
          <asp:Literal ID="Literal1" runat="server"></asp:Literal>
        </div>
      </form>
    </html>
    
    <script runat="server">
      private void Page_Load(Object Src, EventArgs e)
      {
        protected void Button1_Click(object sender, EventArgs e)
        {
          Literal1.Text = @"<span style=""color:" 
            + Server.HtmlEncode(TextBox1.Text)
            + @""">Color example</span>";
        }           
      }
    </Script>

    A. Potentially Dangerous HTML Tags

    Attackers often insert scripts using tags like:

    • <applet>
    • <body>
    • <embed>
    • <frame>
    • <script>
    • <frameset>
    • <html>
    • <iframe>
    • <img>
    • <style>
    • <layer>
    • <link>
    • <ilayer>
    • <meta>
    • <object>

    Attributes like src, lowsrc, style, and href can be used to inject XSS:

    <img src="javascript:alert('hello');">
    <img src="java&#010;script:alert('hello');">
    <img src="java&#X0A;script:alert('hello');">

    Tags that insert scripts by changing the MIME type of a style as follows:

    <style TYPE="text/javascript">
      alert('hello');
    </style>

    Evaluate Countermeasures

    A. Encode HTML Output

    Pre-process text output using HTML encoding to convert special characters (like <, >, &).

    Response.Write(HttpUtility.HtmlEncode(Request.Form["name"]));

    B. Encode URL Output

    When returning URL strings containing client input:

    Response.Write(HttpUtility.UrlEncode(urlString));

    C. Filter User Input

    If a page must accept a range of HTML elements, set ValidateRequest="false" and create a filter that allows only specific, safe HTML elements.

    • Safely Restricting HTML Input
    1. Disable ASP.NET Request Validation for specific requests by setting the @ Page directive attribute to ValidateRequest="false".
    2. Encode the input string using the HtmlEncode method.
    3. Selectively remove encoding for specific allowed HTML elements by utilizing StringBuilder to call and replace the encoded strings back to their original tags.