Click here to monitor SSC
  • Av rating:
  • Total votes: 22
  • Total comments: 4
Jeremy Jarrell

Two Steps Forward to More Secure Applications

09 September 2010

Are you certain that you know enough about cross-site scripting attacks, and SQL Injection to avoid either of them happening to that site you're responsible for? Even though these exploits are easy to avoid, many developers still aren't taking the necessary precautions, so we asked Jeremy Jarrell to come up with simple guidelines.

Introduction

The web has woven itself into nearly every aspect of your life.  You do your banking online, you track your investments online, and you keep in touch with friends and family online.  Every day you trust your most important financial information and most intimate personal communications to millions of lines of code written by programmers just like you.

If you’re like most programmers that last sentence is enough to make you leap out of your seat, your hair stand on end, and seriously question ever buying anything online again.  Why?  Well, because despite the fact that some of the most important parts of our society have moved completely online, only a handful of programmers have even a passing familiarity with basic security.

Earlier this year the SANS Institute released its 2010 edition of the Top 25 Most Dangerous Software Errors…sort of a ‘Greatest Hits’ of epic failures in software security.  What’s so disturbing about this list is that even though it’s updated every year, the vast majority of errors can easily be avoided with just a bit of forethought.  Many developers, including myself, often view software security as a highly advanced black art that is understood only by those few security professionals who have dedicated their career to demystifying its muddy waters.  While I am sure that there are only a handful of professionals on this earth who can truly understand the arcane differences between AES encryption and DES encryption, would it surprise you to know that the #11 programming error of 2010 was “Use of Hard-Coded Credentials”? 

What was that?  People are still hard-coding unencrypted usernames and passwords directly in their code, or even worse, in configuration files?  That’s right, and enough people are doing it that it was the 11th highest ranking programming error by the SANS Institute in 2010.  Do you still think that security is a black art reserved for an elite few?

In addition to the fact that many of the most common security exploits can be easily avoided, many highly dangerous exploits from days gone by have been greatly reduced if not prevented completely by the advent of managed runtimes such as .NET.  For example, Buffer Overflows and Integer Wraparounds, once the bane of security experts everywhere, are either prevented by the .NET runtime or their effects are strongly reduced.

In this article, we’ll discuss two simple steps that you can take to significantly improve the security of your software.  We’ll learn about a few common exploits in the wild today, and how we can defend against them using some basic coding techniques.

SQL Injection Attack

Whether you write applications fo­­r the web, desktop, or mobile devices…if your application talks to a database then you’re at risk for this attack.

A SQL Injection Attack consists of a user passing SQL code into your application, which it naively processes, resulting in the user gaining unauthorized access to pieces of your data or completely destroying the data in your database.

Most often this occurs when a developer constructs a SQL query by simply concatenating unchecked user input with strings of pre-written SQL, like in the example below…

SELECT * FROM Users WHERE LastName=’ + lastName + ‘;

Assuming your user is searching for ‘Joseph Wilson’then web entry form might look like this...

 ...and the resulting SQL would look as follows…

SELECT * FROM Users WHERE LastName=’Wilson‘;

As long as your users are trustworthy people, which we all hope they are, the code above can exist happily for a quite some time with no concerns.  However, what happens when you can encounter a user with less than altruistic intentions?  Imagine that your user has passed in the text “Fake' OR '1'='1”.  The resulting SQL would be….

 

SELECT * FROM Users WHERE LastName=’Fake' OR '1'='1 ’;

What’s with the Quotes?

There’s another interesting point about the SQL above.  Note that the user has added uneven quotes around the ‘1’=1.  This allows the attacker to smoothly insert his own SQL directly into the string concatenation without causing a syntax error.  As we’ll see later, a clever attacker can also use this technique to append entire SQL statements on the end of your query.

In this case the fact that there is no user in your user’s table with the last name of ‘Fake’ is irrelevant, the fact that the attacker has OR’d ‘Fake’ with the expression of ‘1=1--an expression which will always be true--has ensured that the WHERE clause will return true for each row in the table, resulting in every row in the table being returned.  Hope you’re hashing your passwords!

What’s that you say?  You never store your passwords in clear text and even if you did you wouldn’t bind the results directly to a grid?  Don’t get too comfortable yet, maybe our attacker will just decide to add himself to the database then.

SELECT * FROM Users WHERE LastName=’Fake’; INSERT INTO Users (FirstName,LastName,Password) VALUES (‘Joe’,’TheHacker’,’joesPassword’)--

By inserting ; the attacker can cause their own SQL statement to be executed immediately after yours.  Also, note the - - appended to the end.  This comment disables the trailing quote that would normally be inserted after the attacker’s input and prevents a syntax error.

Or, maybe your user isn’t even interested in gaining access to your system.  Maybe, instead, all he’s interested in is taking it down.

SELECT * FROM Users WHERE LastName=’Fake’; TRUNCATE TABLE Users--

The statement above will definitely take your application offline for a few hours and will give you a great chance to test out that backup strategy you’re always thinking about implementing.

So how do you defend against this threat?  Actually, it’s quite simple.  By parameterizing your SQL queries or by using Stored Procedures, you can eliminate the possibility of an attacker passing malicious SQL into your application.

Let’s consider the SQL query from above.

SELECT FirstName,LastName,EmailAddress FROM Users WHERE LastName=’ + lastName + ‘;

What makes this query dangerous it that we allow the user to pass any value for LastName without first checking its validity.  However, by forcing this value into a parameter, we can trust our database engine to do a large amount of validation for us.

SELECT FirstName,LastName,EmailAddress FROM Users WHERE LastName=@LastName;

Here is the ADO.NET code responsible for executing the query above.

using (var connection = new SqlConnection())

{

using (var command = connection.CreateCommand())

      {

          command.CommandType = CommandType.Text;

          command.CommandText = “SELECT FirstName,LastName,EmailAddress FROM Users Where LastName=@LastName”;

          command.Parameters.Add(new SqlParameter(“@LastName”, lastName));

 

          connection.Open();

          using (var reader = command.ExecuteReader())

          {

            . . .

          }

      }

}

We can also achieve the same effect by using parameterized stored procedures.

CREATE PROCEDURE GetUserByLastName

(

            @LastName NVARCHAR(MAX)

)

AS

BEGIN

            SELECT FirstName,LastName,EmailAddress FROM Users WHERE LastName=@LastName

END

And here is the ADO.NET code responsible for executing the stored procedure above.

using (var connection = new SqlConnection())

{       

using (var command = connection.CreateCommand())

{

            command.CommandType = CommandType.StoredProcedure;

            command.CommandText = “dbo.GetUserByLastName”;

            command.Parameters.Add(new SqlParameter(“@LastName”, lastName));

         

            connection.Open();

            using (var reader = command.ExecuteReader())

            {

                          . . .

            }

        }

}

 

Both of these solutions allow us to leverage the power of the platform to prevent malicious code from being passed along to our database. The advantage of the solution that uses stored procedures is that you can then deny access to the base tables to the login used by the website whist allowing access to the stored procedure, so that, even if the attacker somehow gained access, there would be nothing further they could do. They would be unable to bypass the defined interface.  Another solution worth mentioning is the use of an OR/M such as NHibernate or Linq to SQL.  Since an OR/M generates most SQL automatically for the developer using parameterized SQL the opportunity for developer error is greatly reduced.

Cross-Site Scripting Attack

If you’ve done anything with the web in the past few years, the odds are that you’ve come up against this attack.  In fact, as of this year it has been elevated to the coveted Number 1 spot of the Top 25 Most Dangerous Software Errors report listed above.  Well…coveted if you’re a security bug, that is.

Cross-Site Scripting, abbreviated as XSS, is similar to the SQL Injection attack listed above in the sense that it often occurs by blindly trusting malicious input from your users.  However, unlike SQL Injection which is often used to gain access to, or possibly damage, the data in your database XSS is used to trick your website into serving harmful script data to your users.

An attacker can mount an XSS attack on your website when you allow users to input un-sanitized text directly into your web page that you later display.  For example, imagine that you have created a site that allows users to post comments about recent updates.  This type of freeform text entry is the perfect opportunity for an attacker to enter an attack script instead of a review.  The issue arises the next time an unsuspecting visitor browses that particular product on your site.  As your application retrieves all associated comments for a given product it will encounter the malicious scripting code and serve it to your user’s browser just as it would the other text in the database.  The browser, which doesn’t understand the context of a comment versus regular script code, will execute the script code as soon as it encounters it.

Let’s take a look at an example.

In the web page below, users are invited leave a review for the website in a free form text box.  This is the perfect opportunity for some of your less scrupulous users to mount a XSS attack against your site!

If you’re simply accepting your users’ reviews as trusted data then you could be leaving yourself open to just such an attack.

protected void SubmitButton_Click(object sender, EventArgs e)

{

SaveComment(CommentTextBox.Text);

}

The next time a user visits your site they’ll be the victim of the unscrupulous users attack.

How do you prevent such an atrocity from occurring?  By simply encoding your user’s input using the Microsoft AntiXSS Library.  The Microsoft AntiXSS Library compares your user’s input to a whitelist of known safe characters encoding anything that doesn’t appear on that whitelist.  This encoding neutralizes any harmful input that a user may throw at you.

protected void SubmitButton_Click(object sender, EventArgs e)

{

      SaveComment(Microsoft.Security.Application.AntiXss.HtmlEncode(CommentTextBox.Text));

}


It’s also worth mentioning that the ASP.NET platform also includes more out-of-the-box protection to deal with XSS attacks.  Every ASP.NET page contains a page-level directive called ValidateRequest which scans user input for known attack characters and rejects any post which contains them.  In fact, the examples above will only work if ValidateRequest has been turned off on the page (it’s on by default).  So why are additional libraries necessary if this page directive is in place?  Primarily it’s because the Anti-XSS library, and even HtmlEncode(), provide a much more in-depth defense than the ValidateRequest page directive does.  In addition, since ValidateRequest prevents a post of any data containing blacklisted input it’s not unheard of for ValidateRequest to be turned off for certain pages where blacklisted characters are expected.  In these cases it is imperative to allow field-level validation using one of the techniques that I’ve mentioned..

Conclusion

SQL Injection and XSS are the two of the most common attacks threatening Internet-based applications today.  However, by following the simple steps mentioned above, you can dramatically reduce your application’s exposure to malicious intent; This way you'll  make it safer for both your users and your stakeholders.

Jeremy Jarrell

Author profile:

Jeremy Jarrell is a software developer specializing in desktop application development with the .NET platform. He is heavily involved in the .NET developer community both as a regular contributor to open source and as a frequent presenter at user groups throughout both the Pittsburgh and Philadelphia areas. His interests include client application technologies, developer productivity, and leading edge developer techniques such as test-driven development. He can be reached through his website at www.JeremyJarrell.org.

Search for other articles by Jeremy Jarrell

Rate this article:   Avg rating: from a total of 22 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Just checking
Posted by: Anonymous (not signed in)
Posted on: Wednesday, September 15, 2010 at 3:45 AM
Message: <script type="text/javascript">
alert("sorry but I had to try! :)");
</script>

Thanks for the article. It's one of the simplest and clearest explanations I've read on the topic and I've passed it on to my colleagues.

Subject: Re: Just checking
Posted by: Jeremy Jarrell (not signed in)
Posted on: Wednesday, September 15, 2010 at 5:29 AM
Message: haha! Well, I'm glad we're encoding our input!

Thanks for the kind words about the article, I'm glad I could help.

Take care,
Jeremy

Subject: Security shouldn't need to be explicitly stated in requirements
Posted by: Phil Bolduc (not signed in)
Posted on: Wednesday, September 15, 2010 at 8:18 AM
Message: I recently worked on a project where login information was being looked up in an ldap directory. During testing, we noticed odd behavior when one of our testers was testing. This tester's first name starts with 't' and the login names are prefixed with the active directory domain, ie domain\tuser. This tester's credentials could not be found in the ldap directory. After validating the credentials were in directory, we guessed they were not escaping the '\t'. We escaped the backslash and the test worked! Hmm, ldap injection? We tested by passing a '*' as the username and guess what? The lookup was successful (there is no user with username equal to '*')! We reported this bug. To our surprise, the vendor wanted to close the issue with a 'Will not fix' because they stated it was not in the system requirements to escape input. By default, the system has very high security and privacy requirements. The debate between the client (us) and the vendor lasted for weeks. Crazy!

The vendor who developed the interface is a large well known company (not Microsoft). Unless you live under a rock, you would know this company by name.

Every project should have implied requirements:

- The system should not be vulnerable to known SQL injection attacks

- The system should not be vulnerable to known ldap injection attacks

- The system should not be vulnerable to known javascript injection attacks

- The system should not be vulnerable to known injection attacks

Information is readily available on these topics,

http://www.owasp.org/index.php/Category:Attack

Subject: RE: Security shouldn't need to be explicitly stated in requirements
Posted by: jeremyjarrell (view profile)
Posted on: Monday, September 20, 2010 at 6:07 AM
Message: Hi Phil,

Thanks for the story. You're entirely correct, LDAP injection is an often overlooked attack vector, especially for internal apps.

I often find that security takes a backseat in apps developed for internal use only. The justification, of course, is that the app isn't publicly facing and no-one within our enterprise would think of attacking our own apps. But your story highlights that not only are internal apps vulnerable to attack, but some attack vectors ONLY affect internal apps.

Keep in mind that when I say 'internal apps' I'm referring to not only apps developed by an enterprise's own internal IT department, but also to commercial apps purchased from a vendor but deployed behind an enterprise's firewall.

Thanks for sharing your story, Phil. It makes an excellent point!

Jeremy

 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

TortoiseSVN and Subversion Cookbook Part 10: Extending the reach of Subversion
 Subversion provides a good way of source-controlling a database, but many operations are best done from... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.