Web Application Security Guide/Miscellaneous points
This section contains some general security hints for web applications.
- Do not rely on Web Application Firewalls for security (however, consider using them to improve security)
- If external libraries (e.g. for database access, XML parsing) are used, always use current versions
- If you need random numbers, obtain them from a secure/cryptographic random number generator
- For every action or retrieval of data, always check access rights
- Do not, under any circumstances, attempt to implement cryptographic algorithms yourself. Use high-level libraries for cryptography.
- Ensure debug output and error messages do not leak sensitive information
- Mark problematic debug output in your code (e.g.
//TODO DEBUG REMOVE) even if you intend to remove it after just one test
- Do not use “
eval()” and similar functions
- Avoid “
system()” and similar functions if possible
- Avoid “
- Ensure database servers are not directly reachable from the outside
- Consider to block old browsers from using your application
Web Application Firewalls (WAFs) can prevent existing security holes from being abused. They will make attacking your web application significantly harder and more annoying for the attacker, increasing the probability that a non-determined attacker will move on to a different target. However, they can usually be bypassed by a determined attacker. Your actual defense is to secure your applications. The WAF is there to provide some additional protection against mistakes in doing so. Having a Web Application Firewall does not allow you to skimp on securing your applications. A WAF that is not precisely tuned to an application will often block legitimate requests and pass attacks through/allow bypassing. This is especially true of the often-used free Core Rule Set of mod_security.
Outdated library versions may contain security issues.
If low-quality random numbers are used, for example for the generation of password reset tokens, attackers may be able to guess the value and circumvent security measures.
Not checking access rights at every step leads to significant vulnerabilities, for example users being able to look at data for which they have no permission (e.g. membership database supposed to show a logged-in member his information – changing the ID in the URL gives information about other members due to missing check).
Cryptography is extremely complicated and mistakes are hard to avoid or discover even for cryptography experts. Secure ciphers are developed over months of work by multiple experts and reviewed by hundreds of them. Do not try to invent a secure cipher. Do not attempt to implement existing ciphers, either, mistakes can go unnoticed and make your result insecure. Use existing, reliable libraries.
Debug output and error messages can give attackers valuable information. Notably, there have been multiple instances where debug output of the following form compromised security: “Provided token 1234 was invalid, expected value 5678” (the attacker gets the correct answer which he just needs to supply in his next attempt). For production versions, displaying of error messages should usually be suppressed. Consider replacing HTTP error pages to hide even basic information like paths.
Marking any debug output that is supposed to be removed ensures that you cannot forget removing it – just search for “TODO” and “REMOVE” before release. Make it a habit to mark it always, even if you intend to remove it “immediately”. You can always get distracted and forget.
Using dynamic code via “
eval()” and similar functions is usually unnecessary and small mistakes tend to cause code injection issues. Therefore, these dangerous functions are to be avoided. The same is valid for “
system()”, however, this cannot be always avoided. If used, input to “
system()” has to be correctly escaped, of course — using existing shell-escape functions or a function that automatically escapes the parameters.
Keeping database servers unreachable from the outside, e.g. by binding them to 127.0.0.1 if they run on the same machine as the web application or by using firewalls with IP white lists, prevents attackers from using stolen database passwords to actually access the database.
Browsers that are no longer supported by the vendor tend to have critical security issues. They are a sign of a badly maintained client that is very prone to malware attack and they often lack security features relevant to web applications. Blocking can be done using the User-Agent header or for IE using conditional comments. Blocking outdated browsers can force clients to use a secure browser; however, it can prevent people who can’t update their browser from using the application. Unless you know that all clients should be having IE8 or newer, blocking anything newer than IE6 is not advised. Note that the Mozilla Firefox 3.6.x branch is still supported (as of September 2011), while the Firefox 4 branch is not. Obviously, intentional blocking of older browsers is mainly relevant to web applications that require very high security.