Hacking the Code ASP.NET Web Application Security [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Hacking the Code ASP.NET Web Application Security [Electronic resources] - نسخه متنی

James C. Foster, Mark M. Burnett

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






Handling Malicious Input

Before an attacker can exploit your application with malicious input, the attacker has to get the input to your code. And that is where Web developers have the advantage. By carefully identifying and controlling input, you can prevent the attacks before they ever get to the sensitive code.

The input handling strategies we will address here are:



Identifying Input Sources



Programming Defensively




Identifying Input Sources












Summary:


Sometimes the hidden sources of user input are the most dangerous.


Threats:


Malicious input


Half the challenge of stopping malicious input is identifying the numerous ways your application accepts input. All attacks on the application itself are based on some form of manipulating the permitted user input. If you handle form input properly, you can eliminate most, if not all, of these vulnerabilities. Not only should you handle form input and query strings, but you must also consider any other data that an attacker can modify. Often overlooked are indirect sources of input and data that you might think an attacker cannot access.

With an ASP.NET application the most obvious place to look for input is any place you use the Request object. Classic ASP provided the Request object as a property of the Server object. To maintain compatibility with existing ASP code, ASP.NET provides the HttpRequest class through the Request property of the Page class. The HttpRequest class exposes elements of the HTTP request through its various properties. Table 5.1 shows some of these properties and how they relate to different elements of the HTTP request.
















































Table 5.1: HttpRequest Class and HTTP Elements

Property


Source


Browser


Guessed based on the client-provided User-Agent header


ClientCertificate


Based on client certificate headers


Cookies


Set-Cookie header from client


Form


Post data from the client


Headers


All HTTP headers


Path


Parsed from the URL


PathInfo


Parsed from the URL


QueryString


Parsed from the URL


ServerVariables


Combination of client and server data


UrlReferrer


Referer header from the client


UserAgent


User-Agent header from the client


UserHostName


May be controlled by user if user controls the DNS server


An easy way to identify potential flaws in your code is to search for all references to the Request object to make sure that you handle user input properly. As described later in this chapter, you should always filter data coming from the Request object, and you should never concatenate the Request object directly to a string. For example, consider this code:

[C#]
Response.Write("Welcome " + Request.QueryString["Username"]);
[VB.NET]
Response.Write("Welcome " & Request("UserName"))

In this example, the code directly outputs the contents of the UserName variable without validating or filtering its contents. This is a common, but definitely not a safe, practice. A better solution is to filter the input before acting on the data, as shown here:

[C#]
string userNameSafe=FilterInput(Request.QueryString["UserName"]);
Response.Write("Welcome " + userNameSafe);
[VB.NET]
userNameSafe=FilterInput(Request("UserName"))
Response.Write("Welcome " & userNameSafe)

In this example, the code calls a custom function FilterInput that you create to perform whatever filtering is necessary for the type of data.

Other Sources of Input


The Request object is the most common source of input, but it is possible for a user to inject input indirectly or through less obvious sources. A user may input data directly into your database or manipulate HTTP headers sent to your server. For example, consider the ASP code shown in Figure 5.1. This code is from an ASP error handler page (found by default at C:\WINNT\Help\iisHelp\common\500-100.asp) that IIS 5 uses to handle server-side errors. The intended behavior is that if an error occurs it will show the details only if the request is going to “LocalHost” based on checking the contents of the SERVER_NAME server variable.







' Only show the Source if it is available and the request is from the same _' machine as IIS
If objASPError.Source > " Then
strServername = LCase(Request.ServerVariables("SERVER_NAME"))
strServerIP = Request.ServerVariables("LOCAL_ADDR")
strRemoteIP = Request.ServerVariables("REMOTE_ADDR")
If (strServername = "localhost" Or strServerIP = strRemoteIP) And_
objASPError.File <> "?" Then
Response.Write Server.HTMLEncode(objASPError.File)
If objASPError.Line > 0 Then Response.Write ", line " &_ objASPError.Line
If objASPError.Column > 0 Then Response.Write ", column " & objASPError.Column
Response.Write "<br>"
Response.Write "<font style="COLOR:000000;_
FONT: 8pt/11pt courier new"><b>"
Response.Write Server.HTMLEncode(objASPError.Source) & "<br>"
If objASPError.Column > 0 Then Response.Write_ String((objASPError.Column
- 1), "-") & "^<br>"
Response.Write "</b></font>"
blnErrorWritten = True
End If
End If






Figure 5.1: ASP Source From 500-100.ASP

The problem is that the server derives the SERVER_NAME variable from the Host header provided by the client. This Host header comes from the client based on what IP address resolves to the name “LocalHost.” Normally, this IP address always points to loopback, 127.0.0.1, but anyone can edit their HOSTS file to point it to any IP address. So if an attacker were to enter your Web site IP address as the LocalHost IP address in their HOSTS file, they could browse to LocalHost and hit your Web site instead. Furthermore, the client’s Host header will now return LocalHost and the server will therefore show all the error details. With IIS 6, Microsoft fixed this by removing the check for LocalHost, and showing error details only if the server IP address matches the remote IP address.





Note

Editing the HOSTS file is just one way to accomplish this type of attack. An attacker could also use a tool or a script to build a custom HTTP request with any of the headers he wants.


Another example of unexpected user input is the DNS host name for the client. If an attacker controls the reverse DNS entries for his IP address, he could potentially use that hostname to inject malicious input or fool access control restrictions that rely merely upon the hostname.

It is important to consider all sources of input, including input that comes from your internal network or from internal users. Rather than attack your server directly, an attacker may find it easier to go in the back way and inject malicious code where you’d least expect it—from a trusted resource.

Security Policy




Identify all instances of the Request object to be sure you properly filtered this input.



Search for and filter other forms of indirect input, including input from the application itself.




Programming Defensively












Summary:


Preventing application vulnerabilities requires smart coding practices.


Threats:


Malicious input


Of all the different types of application attacks brought to the world’s attention in recent years, none required anything more than reasonable and foreseeable defensive coding practices. Unfortunately, until recently, few Web developers had the time, motivation, or training to build this extra code into their applications. But the never-ending discovery of application vulnerabilities painfully emphasizes the need for defensive coding practices. Although defensive coding may not always be your highest priority, it doesn’t take much effort to follow some simple best practices for improving code security.

Controlling Variables


Because all user input at some point is connected to a variable (or a property or method result of an object variable), if you control variables, then you control user input. The Perl programming language has a feature called Taint Mode that treats all user-supplied input as tainted, and therefore unsafe. Furthermore, any variable derived from a tainted variable also becomes tainted. You cannot perform certain operations with tainted variables until you untaint or filter them with a regular expression.

ASP.NET has no equivalent taint feature, but Web developers can write code using the same approach. To do this, simply make sure you always assign user input to a variable, first running the input through a filtering function to make sure the input is safe to use. Next, make sure you work only with the untainted variables, and never raw user input.





Tip

To help you keep track of variable taintedness, it might be helpful to append a suffix to variable names after you check the data for safety, for example: userName_safe=FilterString(Request.Form("UserName").Now, just make sure you never act on a variable unless it contains the _safe suffix.


Classic ASP allows Web developers to use variables without first defining them. There is an Option Explicit directive you can use to enforce variable declaration, but this is not enabled by default. Explicit variable declaration is always a good practice, but it also has some security benefits. By declaring variables in your code you have a list of all data that you will use in your code—an excellent way to identify sources of user input. By declaring your variables, you are controlling them. Fortunately, VB.NET enables Option Explicit by default; just make sure it always stays that way. It’s critical to note that C# requires variable declaration, so this does not apply.

Classic ASP has another weakness: there is no way to define a variable type; all variables are Variants. Fortunately, the CLR provides a strong type system, but VB.NET does not enforce this by default. It is important to strongly type your variables because this helps to enforce data validity and limit exposure to attacks. By defining the variable type you are limiting the type of data that variable can hold. For example, if you are passing numeric input into an SQL query, you can prevent SQL injection because a numeric variable will not accept the string data required to inject SQL commands. With classic ASP, the variant type would automatically adjust to accommodate the string data.

Because VB.NET does not enforce strict data typing, you must enable this option. In Visual Studio .NET 2003 you can enable this by selecting Tools | Options to bring up the dialog box shown in Figure 5.2. Select the Projects folder, and then click on VB Defaults. From there, set Option Strict to On. As with variable declaration, C# automatically requires variable typing so this issue does not apply.


Figure 5.2: Enabling Option Strict for VB.NET

Centralizing Code


Controlling variables at some point involves filtering or sanitizing the data in those variables. Rather than writing code for each time you accept user input, it is a good practice to centralize your filtering code. As you build your ASP.NET application, use centralized filtering functions on every source of user input. Centralizing your code has several security benefits:



It organizes your code and reduces complexity.



It reduces the attack surface by reducing the amount of code.



It allows you to make quick fixes to deal with future attacks as they surface.



Complexity is the enemy of security. By keeping your code organized and under control, you reduce the likelihood of application vulnerabilities. In general, reducing the code volume reduces bugs, while keeping your code simple and reusing code decreases the number of attack vectors in your code. Most of all, having centralized code allows you to easily adjust your filtering functions to address new attacks as security knowledge and research evolves.

Another benefit of using centralized code is that it is easy to identify user input that you have not properly filtered because it is not wrapped in a filtering function. For example, if you have a filtering function named FilterInput, you should never refer to the Request Object without running it through the FilterInput function this way: safeInput = FilterInput(Request.QueryString_("Username")). If you follow this practice, you can easily search for all references to the Request object not inside a FilteringInput function using a tool such as Grep.





Tip

You can download a native Win32 port of Grep and other Unix utilities from http://unxutils.sourceforge.net/


Testing and Auditing


Due to the complexity and variety of application-level attacks, it is easy to overlook simple mistakes. You should always test your security code to verify that it in fact does what you expect. For example, one commercial Web application uses a regular expression to restrict access to certain administration pages so that only users on the local system could browse those pages. To do this, it checked the client’s IP address against the regular expression “127.*.” Since any IP address that begins with 127 refers to the local host, the programmer expected that this expression would properly restrict access. However, because the programmer did not use the ^ anchor to force matching from the beginning of the string, and because the .* portion of the expression means zero or more occurrences of any character, the regular expression in fact matches any IP address that contains 127 in any position, such as 192.168.1.127. It would not be difficult for an attacker to find an IP address with a 127 and completely bypass this restriction.

By building a proper audit plan and testing with different IP addresses, the programmer could have prevented this flaw.

Using Explicit References


Many programming languages allow programmers to take shortcuts to save typing by allowing certain implicit defaults. For example, if you do not provide a fully qualified path when accessing a file, the system assumes that the file is in the current working directory.

This is important when it comes to filtering user input because ASP.NET allows you to reference items in the Request object without explicitly naming a specific collection. For example, Request(“Password”) is the same as Request.Form(“Password”). When you refer to the generic Request object, ASP.NET searches the QueryString, Form, Cookies, ClientCertificate, and ServerVariables collections, in that order, to find a match. Therefore, by not explicitly stating the collection, you could inadvertently take input from the wrong source. The problem here is that QueryString is the first collection searched.

Now consider the code in Figures 5.3 (C#) and 5.4 (VB.NET). This is a simple ASP.NET page that restricts access to LocalHost by checking the IP address of the client using the REMOTE_ADDR variable. The server itself provides this value, so it is a reliable method for checking the IP address as shown in Figure 5.5.







<html>
<body>
<%
if (Request.QueryString["REMOTE_ADDR"]== "127.0.0.1")
Response.Write("Access is <b>allowed</b>");
else
Response.Write("Access is <b>not allowed</b> from " +
Request.QueryString["REMOTE_ADDR"]);
%>
</body>
</html>






Figure 5.3: Using Generic Request References [C#]







<html>
<body>
<%
If Request("REMOTE_ADDR")="127.0.0.1" Then
Response.Write("Access is <b>allowed</b>")
Else
Response.Write("Access is <b>not allowed</b> from " & _
Request("REMOTE_ADDR"))
End If
%>
</body>
</html>






Figure 5.4: Using Generic Request References [VB.NET]



Figure 5.5: IP Address Blocked

The problem with this code is that the programmer failed to specify the specific collection to use so ASP.NET will search the QueryString, Form, Cookies, and ClientCertificate collections before it tries the ServerVariables collection. Knowing this, an attacker could populate any of these collections with an item matching the server variable name and bypass the protection. For example, adding a query string variable named REMOTE_ADDR using the IP address 127.0.0.1 will fool the application’s IP restriction as shown in Figure 5.6.


Figure 5.6: IP Address Allowed with REMOTE_ADDR in QueryString


In a similar manner, an attacker could trick another user by passing variables on the URL that override form, cookie, or certificate values. The solution for this is simple: always explicitly name the collection from which you expect to pull the variable. This is illustrated in Figures 5.7 (C#) and 5.8 (VB.NET) as explicitly referring to the Request.ServerVariables object. By avoiding implied references, you can prevent attackers from exploiting ambiguities in your code.







<html>
<body>
<%
if (Request.ServerVariables["REMOTE_ADDR"]== "127.0.0.1")
Response.Write("Access is <b>allowed</b>");
else
Response.Write("Access is <b>not allowed</b> from " +
Request.ServerVariables["REMOTE_ADDR"]);
%>
</body>
</html>






Figure 5.7: Using Generic Request References [C#]







<html>
<body>
<%
If Request.ServerVariables("REMOTE_ADDR")="127.0.0.1" Then
Response.Write("Access is <b>allowed</b>")
Else
Response.Write("Access is <b>not allowed</b> from " & _
Request("REMOTE_ADDR"))
End If
%>
</body>
</html>






Figure 5.8: Using Generic Request References [VB.NET]


Security Policy




Always assign filtered user input to variables to distinguish it from the raw data.



When using VB.NET, always use Option Explicit and Option Strict.



Use centralized filtering functions on all user input.



Never use the generic Request collection.



/ 96