Steve Nice of ForLinux breaks down essential knowledge and best practice tips to help make your web app less vulnerable to security breaches
This article first appeared in issue 227 of .net magazine – the world’s best-selling magazine for web designers and developers.
Last year saw a plethora of web application compromises, which resulted in millions of private details being stolen. You may not be able to stop determined hackers, but by following some basic rules and procedures you can prevent your application from being low-hanging fruit for them – or for the casual script kiddie.
Web applications present a complex set of security issues for developers. The stateless nature of HTTP means that tracking per-user session state becomes the responsibility of the application. As a precursor to this, the application must be able to identify the user by using some form of authentication. Given that all subsequent authorisation decisions are based on the user’s identity, it’s essential that the authentication process is secure and that the session handling mechanism used to track authenticated users is equally well protected. Once authenticated, the user must not be able to tamper with parameters or processes that will lead to the disclosure of sensitive information.
Authentication is the process of validating user access to the application. There are three aspects to consider:
- Identify where authentication is required in your application – generally whenever an access level is crossed. There are three basic levels: public, private and privileged. Each of these needs to be identified within the application and appropriate security put in place.
- Validate who the user is. Users typically authenticate themselves with usernames and passwords.
- Identify the user on subsequent requests. This requires some form of authentication token.
Many web apps use a password mechanism to authenticate users, where users supply a username and password in an HTML form. Issues to consider are:
- Are user names and passwords sent in plaintext over an insecure channel? If so, an attacker can eavesdrop with network monitoring software to capture the credentials. The countermeasure here is to secure the communication channel by using Secure Sockets Layer (SSL).
- How are the credentials stored? If you’re storing user names and passwords in plaintext, either in files or in a database, you’re inviting trouble. Ensure all user details are encrypted. There have been many high profile data breaches over the past year that could have been avoided by using encryption.
- How are the credentials verified? There is no need to store user passwords if the sole purpose is to verify that the user knows the password value. Instead, you can store a verifier in the form of a hash value and re-compute the hash using the user-supplied value during the logon process.
- How is the authenticated user identified after the initial logon? Some form of authentication ticket, for example a cookie, is required. If it’s sent across an insecure channel, an attacker can capture the cookie and use it to access the application, known as session hijacking. Firesheep (see box, opposite) is a Firefox extension that demonstrates this by intercepting unencrypted cookies.
If your application is one of the vast majority that require authentication, a few best practices should be followed to limit your exposure to tampering:
Use account lockout policies for end-user accounts
Disable end-user accounts or write events to a log after a set number of failed logon attempts. Be careful that account lockout policies can’t be abused in denial of service attacks. For example, during a penetration test, I performed a brute force password attack on a login form. I didn’t gain access, but did manage to crash the server owing to the application logging every failed attempt. The solution was to block the IP address after five invalid login attempts.
Support password expiry periods
Passwords should not be static and should be changed as part of routine maintenance through password expiry periods. This may inconvenience users but they should appreciate the extra level of security you are putting in place.
Be able to disable accounts
If the system is compromised, being able to deliberately invalidate credentials or disable accounts can provide a means of preventing additional attacks.
Do not store passwords in user stores
If you must verify passwords, it’s unnecessary to actually store the passwords. Instead, store a one way hash value and then recompute the hash with the user-supplied passwords.
Require strong passwords
Make sure it’s not easy for attackers to crack passwords in the first place. There are many guidelines available, but a general practice is to require a minimum of eight characters in a mixture of uppercase and lowercase characters, numbers, and special characters. Whether you’re using the platform to enforce these for you, or developing your own validation, this step is essential to counter brute force attacks in which an attacker tries to crack a password through systematic trial and error. Use regular expressions to help with strong password validation.
Do not send passwords over the wire in plaintext
Plaintext passwords that are sent over a network are vulnerable to eavesdropping. In order to address this threat, secure the communication channel by, for example, making use of SSL to encrypt the traffic.
Protect authentication cookies
A stolen authentication cookie equates to a stolen login. Protect authentication tickets using encryption and secure communication channels. Additionally, limit the time interval that an authentication ticket remains valid for, to counter the spoofing threat that can result from replay attacks in which an attacker captures the cookie and uses it to gain illicit access to your site. Reducing the cookie timeout does not prevent replay attacks, but it does limit the amount of time the attacker has to access the site using the stolen cookie.
With parameter manipulation attacks, the attacker modifies the data sent between the client and the web application. This may be data sent using query strings, form fields, cookies, or in HTTP headers. The following practices help secure your web application’s parameter manipulation:
Remove parameter values from the URL
Validate all values sent from the client
If you have predefined values in your form fields, using a proxy server users can modify values and post them back to receive different results. Test your own application using a proxy server such as WebScarab. WebScarab is a framework for analysing applications that communicate using the HTTP and HTTPS protocols. It’s written in Java and is thus portable to many platforms. WebScarab has several modes of operation, implemented by a number of plug-ins.
In its most common usage, WebScarab operates as an intercepting proxy, enabling the operator to review and modify requests created by the browser before they are sent to the server and to review and modify responses returned from the server before they are received by the browser. WebScarab is able to intercept both HTTP and HTTPS communication.
Do not trust HTTP header information
HTTP headers are sent at the start of HTTP requests and HTTP responses. Your web application ought to make sure it does not base any of its security decisions on information in the HTTP headers, because it’s easy for an attacker to manipulate the header. For example, the referrer field in the header contains the URL of the web page from where the request originated. Don’t make any security decisions based on the value of the referrer field, for example, to check whether the request originated from a page generated by the web app, because the field is easily falsified.
Secure exception handling can help avoid certain application-level denial of service attacks – and can also be used to prevent valuable system-level information useful to attackers from being returned to the client. For example, without proper exception handling, information such as database schema details, operating system versions, stack traces, file names and path information, SQL query strings and other information of value to an attacker can be returned to the client.
A good approach is to design a centralised exception management and logging solution, and to consider providing hooks into your exception management system to support instrumentation and centralised monitoring to help system administrators.
The following practices will help you to secure your web application’s exception management:
Do not leak information to the client
In the event of a failure, do not expose information that could lead to information disclosure. For example, do not expose stack trace details that include function names and line numbers in the case of debug builds (which should not be used on production servers). Instead, return generic error messages to the client.
Log detailed error messages
Send detailed error messages to the error log. Send minimal information to the consumer of your service or application, such as a generic error message and custom error log ID that can subsequently be mapped to a detailed message in the event logs. Make sure that you don’t log passwords or other sensitive data.
Use structured exception handling and catch exception conditions. Doing so avoids leaving your application in an inconsistent state that could lead to information disclosure. It also helps to protect your application from denial of service attacks. Decide how to propagate exceptions internally in your application and ensure you give special consideration to what occurs at the application boundary.
Security should be at foremost in every application developer’s thoughts. Designing your application from the outset with strong security will help prevent unauthorised data leaks and protect users, and keeping best practices in mind will give you the framework you need to accomplish this.