Web App Hardening Checklist
Following are the best practices:
-
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
-
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 or stored procedures to access the database.
-
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 altogether 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.
-
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).
-
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 including 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 Iframing of your application in current browsers by including the HTTP response header “X-Frame-Options: deny”.
-
Prevent Iframing in outdated browsers by including a JavaScript frame breaker which checks for Iframing 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.
-
Mark cookies with the “secure” attribute.
-
Use the Strict-Transport-Security (HSTS) header.
-
If your web application performs HTTPS requests, make sure it verifies the certificate and host name.
-
Consider limiting trusted CAs when 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 instead of GET or POST parameters (Example in 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 errors. If truncation is necessary, ensure to check the value after truncation and use only the truncated value.
-
Make sure trimming does not occur and 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".
-
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.
-
-
HTTPS
-
Secure REST services must only provide HTTPS endpoints. This protects authentication credentials in transit, for example passwords, API keys or JSON Web Tokens. It also allows clients to authenticate the service and guarantees integrity of the transmitted data.
-
See the Transport Layer Protection Cheat Sheet for additional information.
-
Consider the use of mutually authenticated client-side certificates to provide additional protection for highly privileged web services.
-
-
Access Control
-
Non-public REST services must perform access control at each API endpoint. Web services in monolithic applications implement this by means of user authentication, authorisation logic and session management. This has several drawbacks for modern architectures which compose multiple microservices following the RESTful style.
-
In order to minimize latency and reduce coupling between services, the access control decision should be taken locally by REST endpoints.
-
User authentication should be centralized in a Identity Provider (IdP), which issues access tokens.
-
-
JWT
-
A cryptographic signature or message authentication code (MAC) to be used to protect the integrity of the JWT.
Refer the link for more information.
Signatures should be preferred over MACs for integrity protection of JWTs. If MACs are used for integrity protection, every service that is able to validate JWTs can also create new JWTs using the same key. This means that all services using the same key have to mutually trust each other. Consequence of this is that a compromise of any service also compromises all other services sharing the same key.
-
The relying party i.e. token consumer must validate a JWT by verifying its integrity and claims contained based on its own configuration or hard-coded logic. It must not rely on the information of the JWT header to select the verification algorithm.
Some claims have been standardised and should be present in JWT used for access controls. The following of the standard claims should be verified as a minimum:
> iss or issuer - is this a trusted issuer? Is it the expected owner of the signing key?
> aud or audience - is the relying party in the target audience for this JWT?
> exp or expiration time - is the current time before the end of the validity period of this token?
> nbf or not before time - is the current time after the start of the validity period of this token?
-
As JWTs contain details of the authenticated entity (user etc.) a disconnect can occur between the JWT and the current state of the users session, for example, if the session is terminated earlier than the expiration time due to an explicit logout or an idle timeout. When an explicit session termination event occurs, a digest or hash of any associated JWTs should be submitted to a block list on the API which will invalidate that JWT for any requests until the expiration of the token. See the JSON_Web_Token_for_Java_Cheat_Sheet for further details.
-
-
API Keys
-
API keys must be used with public REST services without access control as they run the risk of being farmed leading to excessive bills for bandwidth or compute cycles. API keys can also reduce the impact of denial-of-service attacks.
-
Require API keys for every request to the protected endpoint.
-
Return 429 Too Many Requests HTTP response code if requests are coming in too quickly.
-
Revoke the API key if the client violates the usage agreement.
-
Do not rely exclusively on API keys to protect sensitive, critical or high-value resources.
-
-
Restrict HTTP Methods
-
Apply an allow list of permitted HTTP Methods e.g. GET, POST, PUT.
-
Reject all requests not matching the allow list with HTTP response code 405 Method not allowed.
-
Make sure the caller is authorised to use the incoming HTTP method on the resource collection, action, and record. See Bypassing Web Authentication and Authorization with HTTP Verb Tampering for an explanation of this common misconfiguration.
-
-
Input Validation
-
Do not trust input parameters/objects and reject unexpected/illegal content.
-
Validate input: length / range / format and type.
-
Achieve an implicit input validation by using strong types like numbers, booleans, dates, times or fixed data ranges in API parameters.
-
Constrain string inputs with regexps.
-
Make use of validation/sanitation libraries or frameworks in your specific language.
-
Define an appropriate request size limit and reject requests exceeding the limit with HTTP response status 413 Request Entity Too Large.
-
Consider logging input validation failures. Assume that someone who is performing hundreds of failed input validations per second is up to no good.
-
Have a look at input validation cheat sheet for comprehensive explanation.
-
Use a secure parser for parsing the incoming messages. If you are using XML, make sure to use a parser that is not vulnerable to XXE and similar attacks.
-
-
Validate Content Types
-
Document all supported content types in your API. A REST request or response body should match the intended content type in the header. Otherwise this could cause misinterpretation at the consumer/producer side and lead to code injection/execution.
-
Validate request content types. Reject requests containing unexpected or missing content type headers with HTTP response status 406 Unacceptable or 415 Unsupported Media Type.
-
For XML content types ensure appropriate XML parser hardening, see the XXE cheat sheet.
-
Avoid accidentally exposing unintended content types by explicitly defining content types e.g. Jersey (Java) @consumes("application/json"); @produces("application/json"). This avoids XXE-attack vectors for example.
-
Send safe response content types.
-
It is common for REST services to allow multiple response types (e.g. application/xml or application/json), and the client specifies the preferred order of response types by the Accept header in the request. Do NOT simply copy the Accept header to the Content-type header of the response.
-
Reject the request (ideally with a 406 Not Acceptable response) if the Accept header does not specifically contain one of the allowable types.
-
Services including script code (e.g. JavaScript) in their responses must be careful to defend against header injection attack via proper validation.
-
Ensure sending intended content type headers in your response matching your body content e.g. application/json and not application/javascript.
-
-
Management Endpoints
-
Avoid exposing management endpoints via Internet.
-
If management endpoints must be accessible via the Internet, make sure that users must use a strong authentication mechanism, e.g. multi-factor.
-
Expose management endpoints via different HTTP ports or hosts preferably on a different NIC and restricted subnet.
-
Restrict access to these endpoints by firewall rules or use of access control lists.
-
-
Error Handling
-
Respond with generic error messages - avoid revealing details of the failure unnecessarily.
-
Do not pass technical details (e.g. call stacks or other internal hints) to the client.
-
-
Audit Logs
-
Write audit logs before and after security related events.
-
Consider logging token validation errors in order to detect attacks.
-
Take care of log injection attacks by sanitising log data beforehand.
-
-
Security Headers
-
There are a number of security related headers that can be returned in the HTTP responses to instruct browsers to act in specific ways.
The following headers should be included in all API responses:
Cache-Control: no-store
Content-Security-Policy: frame-ancestors 'none'
Content-Type
Strict-Transport-Security
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
-
The headers below are only intended to provide additional security when responses are rendered as HTML. However, if there is any uncertainty about the function of the headers, or the types of information that the API returns (or may return in future), then it is recommended to include them as part of a defence-in-depth approach.
Content-Security-Policy: default-src 'none'
Feature-Policy: 'none'
Referrer-Policy: no-referrer
-
-
CORS
-
Cross-Origin Resource Sharing (CORS) is a W3C standard to flexibly specify what cross-domain requests are permitted. By delivering appropriate CORS Headers your REST API signals to the browser which domains, AKA origins, are allowed to make JavaScript calls to the REST service. Disable CORS headers if cross-domain calls are not supported/expected.
-
Be as specific as possible and as general as necessary when setting the origins of cross-domain calls.
-
-
Sensitive Information in HTTP Requests
-
RESTful web services should be careful to prevent leaking credentials. Passwords, security tokens, and API keys should not appear in the URL, as this can be captured in web server logs, which makes them intrinsically valuable.
OK:
https://example.com/resourceCollection/[ID]/action
https://twitter.com/vanderaj/lists
NOT OK:
https://example.com/controller/123/action?apiKey=a53f435643de32 because API Key is into the URL.
-
In POST/PUT requests sensitive data should be transferred in the request body or request headers.
-
In GET requests sensitive data should be transferred in an HTTP Header.
-
-
HTTP Return Codes
-
HTTP defines status code. When designing REST API, don't just use 200 for success or 404 for error. Always use the semantically appropriate status code for the response. Here is a non-exhaustive selection of security related API status codes. Use it to ensure you return the correct code.
Codes:
200
201
202
301
304
307
400
401
403
404
405
406
413
415
429
500
501
503
-