Introduction There is no glamour in building secure ASP.NET web applications. The vast majority of developers I’ve met would much rather focus on building new & flashy features that can impress their managers and end-users. Even though security can usually take a backseat during the initial stages of development, it usually comes back to bite you when it’s least expected. This article covers some of the security aspects to be aware of when developing a new web application and what to do throughout the development process to protect applications and databases against common attacks.
Back to Reality Building web applications with ASP.NET has been getting easier as the technology and the development environment, Visual Studio, become more sophisticated. Many of the complexities are taken care of by the framework and are out of view from developers. This allows us to focus more on the business value and features of our web applications and less on technical aspects that very few really understand or appreciate. As a result, many developers believe that security is also taken care of for them and they need not to worry about it. Unfortunately the reality is quite different. Unless developers make security one of their priorities early on in the development cycle, project managers might end up with a great application but one that cannot go live without compromising the safety of their end-users and their data.
Attacks Are Easier Than You Might Think It might be surprising to many project & product managers that exploiting web applications does not necessarily require expert knowledge. Attacks are often perpetrated by hobbyists with very simple means. Here are two recent examples.
Attack I: SQL injection at Telegraph results in 700,000 emails stolen The Attack - On March 6th, 2009, a hacker used blind SQL injection to penetrate to the web site of The Telegraph, a leading English daily paper. Based on reports, he or she was able to gain access to about 700,000 email addresses on the site’s database. Telegraph’s response can be found on their blog. Some Background – SQL statements are generally used to retrieve, update and delete data against a web application’s database. This is normally done behind the scenes and the results are displayed to a user based on their authority level. This means that the data is protected and access is granted on a selective basis. Attack Explanation - Many web applications provide some form of search capabilities where users can provide their own filtering on the data the application might display. For example, a filter to see only the records posted in 2009. If the application is not secure, a hacker can potentially exploit this functionality. Rather than supplying a value to filter upon, he might provide another SQL statement that is then injected in to the SQL statement that the application uses to retrieve data. Attack Example – Let’s assume a user only has access to the records of his department and to filter through the records, he enters some criteria. He wants to see the latest records, so he enters 2009 in the year range. The application might attempt to execute the following statement against the database: SELECT * FROM … WHERE … AND Year = 2009 A hacker on the other hand, might try to trick the application and enter the following into that same year range field: 2009 OR 1=1
The application, if not careful, might then execute the following statement against the database: SELECT * FROM … WHERE … AND Year = 2009 OR 1=1 This would potentially provide the user with access to all the records in the system, even the ones to which they shouldn’t.
Attack II: Authentication Security Holes at Sage The Attack – Sage, a leading provider of accounting software in the UK, was about to launch Sage Live, a small business accounting product as SaaS (software as a service). However according to ZDnet, Sage Live went dead after serious security flaws were exposed in the product. The flaws were exposed by the founder of a tiny rival. Duane Jackson, CEO of UK-based KashFlow, described what she found on her blog: “A little bit of prodding around the site and I found myself looking at… pages that only authorized people should be seeing.” Attack Explanation - Users navigate to and though web applications by going from one URL address to another, much like postal addresses point to residential homes and businesses. As a result, users can easily change the URL and its parameters that can potentially lead to pages and information that they wouldn’t ordinarily be allowed to view.
Common ASP.NET Security Flaws There is a wide array of attacks that ASP.NET web applications need to protect against but most security holes are due to flaws in the following: - Authentication Making it easy for attackers to reveal users credentials, or worse to circumvent the application’s authentication altogether. Possible deficiencies: lack of password policy (strong passwords, expiration date etc), passing internal messages back to the browser, using dynamic SQL on the login page (SQL injection), using cookies and other insecure means to store users’ credentials, and passing user names and passwords in clear text . Possible attacks: network eavesdropping, brute force & dictionary attacks, SQL injection (on login page), Cookie replay attacks and credential theft. - Authorization Allowing logged-in users to perform actions without authorization verification (i.e. vertical & horizontal privilege escalation.) Possible deficiencies: inconsistent checks for user authorization for every user’s request and web page, lack of data validation and trusting data submitted by users (i.e. cookies, hidden fields, URL parameters, etc.) Possible attacks: privilege escalation attacks (horizontal and vertical), disclosure of confidential data and Data tampering attacks. - Data Validation Trusting data submitted by the user and acting upon it. Possible deficiencies: lack of consistent and strict data validation throughout the web, and failing to encode data sent to the browser. Common Attacks: cross-site scripting (XSS), SQL injection, data tampering (query string, form fields, cookies, and HTTP headers), embedded malicious characters and HTTP response splitting. - Application Configuration
Using default configuration on the application and hosted server. Possible deficiencies: granting the application more permissions than it actually needs, failing to properly secure resources (operating system, database, etc.) and passing internal application information back to the browser (internal messages, exceptions and trace information.) Common Attacks: unauthorized access to administrator functionality, unauthorized access to configuration information, retrieval of clear text configuration information and unauthorized access to data stores.
Securing Your ASP.NET Web Application Implementing security after the fact has a steep cost associated with it. Fixing software defects after the application goes into production might cost up to 15 times more than during development. Lax security also has some other indirect costs, such as damaging a company’s reputation and losing customers. While securing an ASP.NET web application can be complex, it needs to be done on a continuous basis. The .NET framework, and ASP.NET in particular, offers great features to make it easier to properly secure your web application.
Authentication Although ASP.NET supports several authentication methods, form authentication is the most commonly used by web applications. Unfortunately, this approach is not a particularly secure approach as it sends user’s credentials to the server in clear text. Depending on requirements, you might want to consider using SSL throughout the site or at least on the login page. But since SSL might be impractical for many commercial web applications, one new & unique approach that you can consider is using Silverlight. Silverlight can be embedded on any sensitive page and provide encryption of any submitted data. A web application’s authentication can be further enhanced with the following: - Password Policy Enforce a password policy including strong passwords, password expiration, and possibly locking user accounts after a few unsuccessful login attempts. - Guessing Credentials – Brute Force Attacks In addition to the above, you might want to introduce a random delay of a few seconds upon unsuccessful user login. This would make brute force attacks impractical to execute (use Thread.Sleep for this scenario.) - Password Hashing If you manage your authentication store, make sure to hash all user passwords.
Page Authorization & Data Validation Small web applications are often built as a series of fairly isolated web pages. Each page is designed to handle its own security and functionality, basically acting as a mini application. Although this approach might work when security & maintenance concerns are few, it very rarely works for a larger web application. One approach that is commonly implemented, which can offer a solid data validation and authorization infrastructure, is the development of a web framework to be used by all ASP.NET web pages in a consistent manner. This web framework should be designed to employ common authorization and data validation security check points upon every user
request, allowing the application to streamline and tighten many security aspects throughout the application. Building such a web framework in ASP.NET is surprisingly easy. Using .NET inheritance, we can design one or more ASP.NET base classes to inherit from the System.Web.UI.Page object. These base classes would run during each page lifecycle and would verify each user request before the results are served to the user. The following page lifecycle events are commonly used to perform security checks before the page begins processing the request: Init, InitComplete and PreLoad.
Constructing the ASP.NET Web Framework Structuring base web pages is easy. Let’s assume that our ASP.NET web application supports three kinds of users: Unregistered users – allowed to browse anything publicly available. Logged-in users – allowed to browse anything that’s public plus their own private information. Administrators - same as a logged-in user plus some extra admin functionality. Diagram 1: Base class to serve unregistered users and the foundation for other base classes Collapse
public abstract class BaseWebPage : System.Web.UI.Page { ... }
Note: This class should verify general information submitted by users, such as URL parameters, form data to some extent, cookies and other info that is not tied to any particular user. Diagram 2: User base class to be inherited by all pages used by logged-in users Collapse
public abstract class UserWebPage : BaseWebPage { ... }
Note: This class should ensure that the user is currently logged-in and has access to the page functionality and to the date they want to view. Diagram 3: Administrator base class to be inherited by all pages used by administrators Collapse
public abstract class AdminWebPage : UserWebPage { ... }
Note: This class should ensure that in the current user is indeed an administrator.
Linking the Page to Our Web Framework Utilizing the ASP.NET web framework above requires very minor adjustments to a web page. Rather than using the default ASP.NET page base class (System.Web.UI.Page) use the appropriate base class defined earlier.
For instance, if we defined a new ASPX page that is supposed to serve logged-in users we would define it as follows: Diagram 4: Sample ASPX code-behind page serving a particular user Collapse
public partial class ShowUserSettingsPage : UserWebPage { ... }
Performing Page Authorization & Data Validation Now that we have built our web framework, all we have left is to verify user authorization per page and to perform general data validation for each request. To perform page authorization, we would define one virtual method on the BaseWebPage class to authorize the current user. Both the UserWebPage and the AdminWebPage would override this method and provide their own implementation. We would call this method on Page_Init from the BaseWebPage before any page processing is taking place. This might be implemented as follows: Diagram 5: Page authorization & data validation on BaseWebPage base class Collapse
public abstract class BaseWebPage : System.Web.UI.Page { protected void Page_Init(object sender, EventArgs e) { AuthorizeUser(); ValidateSubmittedData(); } protected virtual void AuthorizeUser() { //no need to do anything for unregistered users } ... }
Note: Class calls a virtual method AuthorizeUser that inheriting classes can implement to force specific user access rights, and once the user is authorized, the class verifies all data submitted to the web page. Diagram 6: Page authorization on UserWebPage for ASP.NET web pages designated for logged-in users Collapse
public abstract class UserWebPage : BaseWebPage { protected override void AuthorizeUser() { if(!User.IsInRole("User")) { //user cannot see the page - redirect to appropriate page } }
}
Note: Class overrides the AuthorizeUser to make sure current user is logged-in.
More on Data Validation A centralized web framework can offer many security benefits to web applications, but it often needs to be complemented with page-specific security measures to ensure proper data validation. Web pages should employ data validation based on the following guidelines:
•
Data validation – web pages should enforce strict data validation on any piece of data not strictly covered by the web framework. This might include additional type casting (for instance ensures that a piece of data is an integer) range and length. Regular expressions and Regex class are a great a great way to constrain input in ASP.NET.
•
Encoding – any web page displaying data on the browser that cannot be guaranteed to be safe should be encoded using Server.HtmlEncode to prevent any malicious cross-site scripting (XSS) attacks.
•
URL write actions– avoid performing database write actions against URL parameters even if data is valid and the user has authorization to perform such actions. Also ensure that every POST action is actually done from an internal application page to prevent cross-site request forgeries (CSRF) attacks.
•
Exceptions – make sure to catch any possible exceptions and only send user-friendly messages to the browser to avoid revealing any sensitive information that might reveal an application’s vulnerabilities.
Data Authorization – Data Access Framework N-tier infrastructure is not a foreign concept to most ASP.NET developers and can provide substantial benefits in terms of performance and scalability. Among the many benefits of ntier architecture, it also can be used to centralize and enforce strict authorization rules. To do this effectively, a data access framework should be built as an isolated component and should be designed to do the following for each piece of functionality exposed to the ASP.NET web application: •
Parameter Validation - validate all parameters supplied by the user for type, range, length, etc.
•
Authentication - ensure that the calling user is a valid user.
•
Authorization – verify that the user has rights to perform the current database operation.
•
Database Queries - avoid using dynamic SQL queries, if possible, and only use parameters.
•
User Data - only send back data that the current user is entitled to view or return user-friendly exceptions.
Application Configuration Different applications require different configurations but the focus of this section is on those that fall under the responsibility of the web developer.
Encrypting Sensitive Configuration Information
Any sensitive configuration information on the web.config and beyond should be properly encrypted to avoid exposing sensitive details like connection strings to potential attackers. You can use the web.config protectedData section to indicate which sections of the web.config file should be encrypted. The following shows how to protect the connectionStrings section: Diagram 7: Protecting the connectionStrings section within the web.config Collapse
<protectedData> <protecteddatasections>
Note: This is a more suitable way to protect data across different web servers. Unlike DPAPI, the information is not tied down to any particular machine. You can now use aspnet_regiis to encrypt the appropriate section: aspnet_regiis -pef connectionStrings "c:\...\MyWebApplication"
Protect Internal Exceptions Prevent detailed exceptions from being displayed on the client’s browser entirely or display this only when the client is browsing on the web server itself.
Final Note Even though it can be tempting to push off dealing with security (or avoiding it altogether), its importance should never be underestimated. An attack or even a request for a security audit by a customer can cost you time, money and potentially your company’s reputation.