· engineering · 7 min read
Protect your users with a secure HTTP response
Penetration test reports often highlight insecure responses. Let's have a look at how we can secure their responses, with HTTP Headers and more.
Table of Contents
Developers often prioritize vulnerabilities that directly affect application functionality, like brute-force login attempts or SQL injection.
However, insecure HTTP responses can have a significant, albeit indirect, impact. They can expose sensitive data, enable phishing attacks, allow session hijacking, and ultimately compromise user trust in your application.
Though infrastructure-level mitigation is possible (e.g., nginx or CloudFront response policies), it is essential that developers ensure the security of the application, independent of the infrastructure configuration.
Despite this, Penetration test results all to often reveal the same, easily fixable, issues in HTTP responses. This suggests a lack of awareness or understanding, either of their existence/implementation, or importance, which we will address below.
Security headers
As we’re dealing with HTTP responses, it’s no surprise that most of the considerations relate to HTTP headers. Headers allow the server to provide metadata alongside the content (body) of the response. Security headers are simply specific http headers which instruct the browser how to behave when using this resource. Fortunately, the majority of these headers are trivial to implement, and simply enable/disable browser features.
Strict-Transport-Security (HSTS)
Enforces HTTPS connections for the current domain (and potentially any subdomains). Browsers will cache this directive for a set period (max-age) - and during that period ALL requests will use https. HSTS prevents accidental (or malicious) downgrading of the connection to an unencrypted one and helps mitigate man in the middle attacks.One obvious potential flaw, is that a user needs to visit the server first before they can retrieve the header, thus that initial request could potentially be insecure. As a work around browsers will also lookup a new domain in a preload list before attempting an insecure connection.
Getting on the list is easy (add thepreload
directive) but getting off the list is VERY hard!When possible, extend the coverage to
includeSubDomains
, extending coverage to all subdomains rather than the user needing to receive the header on each subdomain in use.Although the user only needs to receive this header once, it is good practice to return it with all requests.
An Example HSTS header, cached for a year with subdomains and preload:
Note: There’s a really interesting exploit described here where HSTS and subdomains can be used to track users.
Read more with MozillaLocation
This is not strictly a security header, yet it can play a fundamental role in securing responses. Any unencrypted request to your application should either return nothing, or return a redirection to the same url with a secure protocol (using this header).
This is typically enabled via a configuration option in web frameworks, or handled in infrastructure, for example as a rewrite rule in nginxContent Security Policy (CSP)
Informs the browser what your page expects to implement, and from where content scripts and media can load. By far the most complicated header to implement, especially in sites with a CMS like element, or single page apps creating inline styles, sufficiently implementing a CSP is tricky business, which i’ll cover in a separate postX-Content-Type-Options This header instructs the browser to accept the content type provided in the header, and not try to guess it.
Without this header, a maliciously crafted file uploaded (for example purporting to be an image) may be executed by the browser (as it may see it as a script).
Always send this header, along with a valid content-type.
There are no options, the header is simply:Important: Static assets like files are often served directly, yet are among the most likely to be affected. Ensure that these files also serve the correct header.
Read more with MozillaX-Frame-Options
Prevent your page from being loaded in an iframe/embed. There are two possible options, either blanket deny, or on the same origin (identical protocols, hostname, and port).Although this header is rendered obsolete by ‘frame-ancestors’ in a CSP header, it still provides protection to older browsers.
Read more with MozillaCross-Origin Resource Sharing (CORS)
Cross Origin Resource sharing is a group of headers which together instruct the browser whether and to what extent they can communicate with your page from another domain. You will need to configure this if for example your client is on domainA, and you wish to use your api on domainB.
As they are somewhat more complicated to correctly implement and there are a few nuances, this will be covered in a separate postClear-Site-Data
This header can instruct the browser to discard data (for example cookies, local storage) related to the site. This header should always be sent in response to the user logging out to clear their session data from the client.X-XSS-Protection
This deprecated header should NOT be implemented, as it can introduce vulnerabilities rather than prevent them!
Read more with MozillaReferrer-Policy
This restricts what information the browser adds to the referrer header when a visitor leaves your site. Though modern browsers have sane defaults, this can protect users from potentially exposing any sensitive query string parameters.Feature Policy (Permissions-Policy) This header will inform the browser what features you wish to use (as well as their use in frames, and optionally across other domains). We can use it for example, to prevent third parties from accessing the microphone, or to allow iframes to go fullscreen.
The options and uses are extensive, and I would encourage you to read this excellent article Mozilla has on it.
For further reading on HTTP headers for securtiy, I would recommend this OWASP cheatsheet, or the linked mozilla resources above.
Sensitive Data Exposure
- Error handling
Ensure that errors are handled gracefully - uncaught or poorly handled error messages often reveal sensitive information. - Unnecessary Data Exposure
Something I’ve noticed particularly in SSR generated sites, is exposing more information than required in the response (for example, exposing the entire user object for hydration, when only a few properties are needed). This can easily unintentionally expose very sensitive data. - User exists
Operations like log in attempts or password recovery should give ambiguous results and not indicate, for example, whether a given email is registered with your platform. - X-Powered-By Header
This and similar headers, are often added by servers or software and should be removed where possible, as they make it easier for an attacker to identify you as a victim for any known vulnerabilities without providing any benefit to you or your users. - Input validation Validate and escape any user input which will be displayed on your site, to prevent malicious scripts being injected and executed in your users browser. (A good CSP Header will prevent these being executed, but its best not to rely on that!)
Cookies
Cookies are visible to (and can be changed by) the user. They are sent with every request to the server, and as such we want to be sure that no sensitive information is contained within them, and that they are not accessible to third parties.
- Secure cookies When the
Secure
attribute is in a cookie, it will only be sent to the origin in an encrypted, HTTPS request, thwarting man-in-the middle attacks. - Http only The
HttpOnly
attribute prevents cookies being modified by javaScript in the browser, essential for session cookies - Use alternatives JSON Web Tokens can provide a more secure, less hijackable alternative to the traditional session cookie.
A word about HTTPS
By now it should be clear, that enforcing HTTPS is absolutely essential to prevent eavesdropping and man-in-the-middle attacks.
HTTPS uses encryption to protect communication between the client and server, with newer versions providing stronger encryption and faster handshakes.
Typically application developers cannot control which HTTPS versions are offered by the server, however with the rise of Infrastructure as code, developers are increasingly involved in configuring and deploying infrastructure like Content Delivery Networks.
In such situations it becomes the responsibility of the developers to ensure that latest protocol versions are available, and outdated ones disallowed.
For example in AWS Cloudfront, this can be achieved by selecting TLSv1.2_2021
as a security policy.
Conclusion
Securing HTTPS responses is an essential part of the security posture of your application, helping you protect your users and their data, and mitigate consequences of common attacks.
Secure responses are not a one time fix, and will need occasionally re-evaluating as time goes by and the application, threats and technologies evolve.
Secure response have other benefits too, such as increased user trust, improved SEO results, and of course, satisfyingly empty Penetration test results!
About James Babington
A cloud architect and engineer with a wealth of experience across AWS, web development, and security, James enjoys writing about the technical challenges and solutions he's encountered, but most of all he loves it when a plan comes together and it all just works.
No comments yet. Be the first to comment!