Web Application Security Guide/Checklist

Miscellaneous points

  • 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
  • Ensure database servers are not directly reachable from the outside
  • Consider to block old browsers from using your application

File inclusion and disclosure

  • Do not take file names for inclusions from user input, only from trusted lists or constants.
    • If user input is to be used, validate it against a whitelist. Checking if the file exists or if the input matches a certain format is not sufficient.
  • Avoid having scripts read and pass through files if possible.
  • If you read and deliver files using user-supplied file names, thoroughly validate the file names to avoid directory traversal and similar attacks and ensure the user is allowed to read the file.
  • Ensure the application runs with no more privileges than required.

File upload vulnerabilities

  • Avoid unnecessary file uploads
  • Ensure that files uploaded by the user cannot be interpreted as script files by the web server, e.g. by checking the file extension (or whatever means your web server uses to identify script files)
  • Ensure that files cannot be uploaded to unintended directories (directory traversal)
  • Try to disable script execution in the upload directory
  • Ensure that the file extension matches the actual type of the file content
  • If only images are to be uploaded, consider re-compressing them using a secure library to ensure they are valid
  • Ensure that uploaded files are specified with the correct Content-type when delivered to the user
  • Prevent users from uploading problematic file types like HTML, CSS, JavaScript, XML, SVG and executables using a whitelist of allowed file types
  • Prevent users from uploading special files (e.g. .htaccess, web.config, robots.txt, crossdomain.xml, clientaccesspolicy.xml)
  • Prevent users from overwriting application files
  • Consider delivering uploaded files with the “Content-disposition: attachment” header

SQL injection

  • use prepared statements to access the database – or –
  • use stored procedures, accessed using appropriate language/library methods or prepared statements
  • Always ensure the DB login used by the application has only the rights that are needed

Cross-site scripting (XSS)

  • Escape anything that is not a constant before including it in a response as close to the output as possible (i.e. right in the line containing the “echo” or “print” call)
  • If not possible (e.g. when building a larger HTML block), escape when building and indicate the fact that the variable content is pre-escaped and the expected context in the name
  • Consider the context when escaping: Escaping text inside HTML is different from escaping HTML attribute values, and very different from escaping values inside CSS or JavaScript, or inside HTTP headers.
    • This may mean that you need to escape for multiple contexts and/or multiple times. For example, when passing a HTML fragment as a JS constant for later includsion in the document, you need to escape for JS string inside HTML when writing the constant to the JavaScript source, then escape again for HTML when your script writes the fragment to the document. (See rationale for examples)
    • The attacker must not be able to put anything where it is not supposed to be, even if you think it is not exploitable (e.g. because attempts to exploit it result in broken JavaScript).
  • Explicitly set the correct character set at the beginning of the document (i.e. as early as possible) and/or in the header.
  • Ensure that URLs provided by the user start with an allowed scheme (whitelisting) to avoid dangerous schemes (e.g. javascript:-URLs )
  • don’t forget URLs in redirector scripts
  • A Content Security Policy may be used as an additional security measure, but is not sufficient by itself to prevent attacks.

XML and internal data escaping

  • Avoid XML if possible.
  • For XML, use well-tested, high-quality libraries, and pay close attention to the documentation. Know your library – some libraries have functions that allow you to bypass escaping without knowing it.
  • If you parse (read) XML, ensure your parser does not attempt to load external references (e.g. entities and DTDs).
  • For other internal representations of data, make sure correct escaping or filtering is applied. Try to use well-tested, high-quality libraries if available, even if it seems to be more difficult.
  • If escaping is done manually, ensure that it handles null bytes, unexpected charsets, invalid UTF-8 characters etc. in a secure manner.

XML, JSON and general API security

  • Ensure proper access control to the API
  • Do not forget that you need to correctly escape all output to prevent XSS attacks, that data formats like XML require special consideration, and that protection against Cross-site request forgery (CSRF) is needed in many cases.
  • Use standard data formats like JSON with proven libraries, and use them correctly. This will probably take care of all your escaping needs.
  • Make sure browsers do not misinterpret your document or allow cross-site loading
    • Ensure your document is well-formed
    • Send the correct content type
    • Use the X-Content-Type-Options: nosniff header
    • For XML, provide a charset and ensure attackers cannot insert arbitrary tags
    • For JSON, ensure the top-level data structure is an object and all characters with special meaning in HTML are escaped

(Un)trusted input

  • Thoroughly filter/escape any untrusted content
  • If the allowed character set for certain input fields is limited, check that the input is valid before using it
  • If in doubt about a certain kind of data (e.g. server variable), treat it as untrusted
  • If you are sure, but there is no real need to treat it as trusted, treat it as untrusted
  • The request URL (e.g. in environment variables) is untrusted
  • Data coming from HTTP headers is untrusted
    • Referer
    • X-Forwarded-For
    • Cookies
    • Server name (!)
  • All POST and GET data is untrusted
    • includes non-user-modifiable input fields like select
  • All content validation is to be done server side

Cross-site request forgery (CSRF)

  • Include a hidden form field with a random token bound to the user’s session (and preferably the action to be performed), and check this token in the response
  • Make sure the token is non-predictable and cannot be obtained by the attacker
    • do not include it in files the attacker could load into his site using <script> tags
  • Referer checks are not secure, but can be used as an additional measure

Clickjacking

  • Prevent (i)framing of your application in current browsers by including the HTTP response header “X-Frame-Options: deny
  • Prevent (i)framing in outdated browsers by including a JavaScript frame breaker which checks for (i)framing and refuses to show the page if it is detected
  • For applications with high security requirements where you expect users to use outdated browsers with JavaScript disabled, consider requiring users of older browsers to enable JavaScript

Insecure data transfer

  • Use SSL/TLS (https) for any and all data transfer
  • Do not start communicating via http, only redirecting to https when “needed”
  • Mark cookies with the “secure” attribute
  • Use the Strict-Transport-Security header where possible
  • Educate users to visit the https:// URL directly
  • If your web application performs HTTPS requests, make sure it verifies the certificate and host name
    • Consider limiting trusted CAs if connecting to internal servers

Session fixation

  • Regenerate (change) the session ID as soon as the user logs in (destroying the old session)
  • Prevent the attacker from making the user use his session by accepting session IDs only from cookies, not from GET or POST parameters (PHP: php.ini setting “session.use_only_cookies”)

Session stealing

  • Set the “HttpOnly” attribute for session cookies
  • Generate random session IDs with secure randomness and sufficient length
  • Do not leak session IDs

Truncation attacks, trimming attacks

  • Avoid truncating input. Treat overlong input as an error instead.
  • If truncation is necessary, ensure to check the value after truncation and use only the truncated value
  • Make sure trimming does not occur or checks are done consistently
  • Introduce length checks
    • care about different lengths due to encoding
  • Make sure SQL treats truncated queries as errors by setting an appropriate SQL MODE

Password security

  • Do not store plain-text passwords, store only hashes
  • Use Argon2, scrypt, bcrypt, or some other secure hashing algorithm specifically designed for secure password "storage".[1][2]
  • Use per-user salts
  • Use strengthening (i.e. multi-iteration hashing to slow down brute force attempts)
  • Limit login attempts per IP (not per user account)
  • Enforce reasonable, but not too strict, password policies
  • If a password reset process is implemented, make sure it has adequate security. Questions like “mother’s maiden name” can often be guessed by attackers and are not sufficient.

Comparison issues

  • Know comparison types in your programming language and use the correct one
  • When in doubt (especially with PHP), use a strict comparison (PHP: "===")
  • When comparing strings for equality, make sure you actually check that the strings are equal and not that one string contains the other

PHP-specific issues

  • Do not use the short form “<?”, always use the full form “<?php
  • When using the nginx web server, make sure to correctly follow the official installation instructions and pay attention to the "Pitfalls" page. Beware of tutorials that often contain working but insecure configuration examples.
  • preg_replace can act as eval() in certain cases. Avoid passing user input to it. If you must, correctly filter and escape it.
  • Use the Suhosin (including the patch, if possible) and configure it with strict rules
    • Enable suhosin.executor.disable_emodifier
    • Enable suhosin.executor.disable_eval if possible
    • Set suhosin.mail.protect to 2 if possible
  • When updating PHP to PHP 5.4 from an older version, ensure legacy applications do not rely on magic quotes for security.

Prefetching and Spiders

  • Use POST requests instead of GETs for anything that triggers an action

Special files

  • Know the meaning of these files
  • Ensure robots.txt does not disclose "secret" paths
  • Ensure crossdomain.xml and clientaccesspolicy.xml do not exist unless needed
  • If used, ensure crossdomain.xml and clientaccesspolicy.xml allow access from trusted domains only
  • Prevent users from uploading/changing special files (see file upload vulnerabilities section)

SSL, TLS and HTTPS basics

  • Follow SSLLabs best practices including:
    • Ensure SSLv2 is disabled
    • Generate private keys for certificates yourself, do not let your CA do it
    • Use an appropriate key length (usually 2048 bit in 2013)
    • If possible, disable client-initiated renegotiation
    • Consider to manually limit/set cipher suites
  1. Patrick Mylund Nielsen. "Storing Passwords Securely".
  2. Wikibook Cryptography/Secure Passwords describes more of the history and theory behind designing a hashing algorithm for password storage.