Input validation is a security issue if an attacker discovers that your application makes unfounded assumptions about the type, length, format, or range of input data. The attacker can then supply carefully crafted input that compromises your application. When network and host level entry points are fully secured; the public interfaces exposed by your application become the only source of attack. The input to your application is a means to both test your system and a way to execute code on an attacker’s behalf. Does your application blindly trust input? If it does, your application may be susceptible to the following:
1. Buffer Overflows
Buffer overflow vulnerabilities can lead to denial of service attacks or code injection. A denial of service attack causes a process crash;. code injection alters the program execution address to run an attacker’s injected code. The following code fragment illustrates a common example of a buffer overflow vulnerability.
void SomeFunction( char *pszInput )
// Input is copied straight into the buffer when no type checking is performed
. . .
Countermeasures to help prevent buffer overflows include:
● Perform thorough input validation. This is the first line of defense against buffer overflows. Although a bug may exist in your application that permits expected input to reach beyond the bounds of a container, unexpected input will be the primary cause of this vulnerability. Constrain input by validating it for type,
length, format and range.
● When possible, limit your application’s use of unmanaged code, and thoroughly inspect the unmanaged APIs to ensure that input is properly validated.
● Inspect the managed code that calls the unmanaged API to ensure that only appropriate values can be passed as parameters to the unmanaged API.
● Use the /GS flag to compile code developed with the Microsoft Visual C++ development system. The /GS flag causes the compiler to inject security checks into the compiled code. This is not a fail-proof solution or a replacement for your specific validation code; it does, however, protect your code from commonly known buffer overflow attacks.
Example of Code Injection Through Buffer Overflows
An attacker can exploit a buffer overflow vulnerability to inject code. With this attack, a malicious user exploits an unchecked buffer in a processby supplying a carefully constructed input value that overwrites the program’s stack and alters a function’s return address. This causes execution to jump to the attacker’s injected code. The attacker’s code usually ends up running under the process security context. This emphasizes the importance of using least privileged process accounts. If the current thread is impersonating, the attacker’s code ends up running under the security context defined by the thread impersonation token. The first thing an attacker usually does is call the RevertToSelf API to revert to the process level security context that the attacker hopes has higher privileges. Make sure you validate input for type and length, especially before you call unmanaged code because unmanaged code is particularly susceptible to buffer overflows.
2. Cross-Site Scripting
An XSS attack can cause arbitrary code to run in a user’s browser while the browser is connected to a trusted Web site. The attack targets your application’s users and not the application itself, but it uses your application as the vehicle for the attack. Because the script code is downloaded by the browser from a trusted site, the browser has no way of knowing that the code is not legitimate. Internet Explorer security zones provide no defense. Since the attacker’s code has access to the cookies associated with the trusted site and are stored on the user’s local computer, a user’s authentication cookies are typically the target of attack.
Example of Cross-Site Scripting:
To initiate the attack, the attacker must convince the user to click on a carefully crafted hyperlink, for example, by embedding a link in an email sent to the user or by adding a malicious link to a newsgroup posting. The link points to a vulnerable page in your application that echoes the unvalidated input back to the browser in the HTML output stream. For example, consider the following two links.
Here is a legitimate link:
Here is a malicious link:
If the Web application takes the query string, fails to properly validate it, and then returns it to the browser, the script code executes in the browser. The preceding example displays a harmless pop-up message. With the appropriate script, the attacker can easily extract the user’s authentication cookie, post it to his site, and subsequently make a request to the target Web site as the authenticated user.
Countermeasures to prevent XSS include:
● Perform thorough input validation. Your applications must ensure that input from query strings, form fields, and cookies are valid for the application. Consider all user input as possibly malicious, and filter or sanitize for the context of the downstream code. Validate all input for known valid values and then reject all other input. Use regular expressions to validate input data received via HTML form fields, cookies, and query strings.
● Use HTMLEncode and URLEncode functions to encode any output that includes user input. This converts executable script into harmless HTML.
3. SQL Injection
A SQL injection attack exploits vulnerabilities in input validation to run arbitrary commands in the database. It can occur when your application uses input to construct dynamic SQL statements to access the database. It can also occur if your code uses stored procedures that are passed strings that contain unfiltered user input. Using the SQL injection attack, the attacker can execute arbitrary commands in the database. The issue is magnified if the application uses an over-privileged account to connect to the database. In this instance it is possible to use the database server to run operating system commands and potentially compromise other servers, in addition to being able to retrieve, manipulate, and destroy data.
Example of SQL Injection
Your application may be susceptible to SQL injection attacks when you incorporate unvalidated user input into database queries. Particularly susceptible is code that constructs dynamic SQL statements with unfiltered user input. Consider the following code:
SqlDataAdapter myCommand = new SqlDataAdapter(“SELECT * FROM Users WHERE UserName ='” + txtuid.Text + “‘”, conn);
Attackers can inject SQL by terminating the intended SQL statement with the single quote character followed by a semicolon character to begin a new command, and then executing the command of their choice. Consider the following character string entered into the txtuid field.
‘; DROP TABLE Customers –
This results in the following statement being submitted to the database for execution. SELECT * FROM Users WHERE UserName=”; DROP TABLE Customers –‘
This deletes the Customers table, assuming that the application’s login has sufficient permissions in the database (another reason to use a least privileged login in the database). The double dash (–) denotes a SQL comment and is used to comment out any other characters added by the programmer, such as the trailing quote.Note
Other more subtle tricks can be performed. Supplying this input to the txtuid field:
‘ OR 1=1 –
builds this command:
SELECT * FROM Users WHERE UserName=” OR 1=1 –
Because 1=1 is always true, the attacker retrieves every row of data from the Users table.
Countermeasures to prevent SQL injection include:
● Perform thorough input validation. Your application should validate its input prior to sending a request to the database.
● Use parameterized stored procedures for database access to ensure that input strings are not treated as executable statements. If you cannot use stored procedures, use SQL parameters when you build SQL commands.
● Use least privileged accounts to connect to the database.
: The semicolon is not actually required. SQL Server will execute two commands separated by spaces.
Different forms of input that resolve to the same standard name (the canonical name), is referred to as canonicalization. Code is particularly susceptible to canonicalization issues if it makes security decisions based on the name of a resource that is passed to the program as input. Files, paths, and URLs are resource types that are vulnerable to canonicalization because in each case there are many different ways to represent the same name. File names are also problematic.
For example, a single file could be represented as:
c:\ temp\ somefile.dat
Ideally, your code does not accept input file names. If it does, the name should be converted to its canonical form prior to making security decisions, such as whether access should be granted or denied to the specified file.
Countermeasures to address canonicalization issues include:
● Avoid input file names where possible and instead use absolute file paths that cannot be changed by the end user.
● Make sure that file names are well formed (if you must accept file names as input) and validate them within the context of your application. For example, check that they are within your application’s directory hierarchy.