Earlier this month, Verizon released its 2019 Data Breach Investigation Report. It revealed, unsurprisingly, that a good chunk of breaches were the result of attacks at the application layer and that there was a major shift (almost to the 50% crossover point) in payment card breach volume sources to compromising web servers.
Rapid7’s own [Master] Chief Data Scientist Bob Rudis and our Rapid7 Labs research team pored over the report to identify some key points to help the Rapid7 community navigate through this sea of information. In his blog post summarizing the top findings in the 2019 Verizon DBIR report, Bob provided some guidance to help you better safeguard your organization, and the following section really hit home for me:
“It’s time to get serious about adopting critical security headers like Content Security Policy and designing web applications modularly to enable clean and easy use of subresource integrity attributes on resources you load. While you can start with just focusing on the core pages that deal with logins and payment card transactions, you should consider adopting these two technologies holistically across all web-facing components. If you source your e-commerce applications from a third party, ensure you mandate the use of these technologies in your procurement processes.”
During my spare time, I’ve built both an auditing tool and a tool to retrieve the Top 500 list from Moz to better understand how the world’s largest companies use headers. Through this, I’ve discovered the best place to address vulnerabilities is within your software itself. Click here to evaluate your site.
Browsers may have varying or even no support for the various security headers, so they should be part of defense in depth and not viewed as a holistic solution.
As this project plays very nicely into how you can heed Bob’s advice, I’ve created a series of real-world scenarios in which attackers can manipulate unsecured HTTP headers and how to prevent your organization from falling victim to tactics used to exploit them. But before I get into the fun stuff, let’s start with a refresher on what HTTP headers are.
What are HTTP headers?
Before you even see a page load or download file, your browser (or any client, such as cURL, wget, etc.) and a server have a conversation. During this conversation, the browser may ask for a specific resource, request a specific language, and tell the server what type of browser you are using.
The server responds with information, such as how many bytes are ready to be sent, the type of server being used, and the cookies to set.
Here’s an example request:
GET /index.html HTTP/1.1 Host: www.rapid7.com User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Referer: https://www.google.com/?q=best+dast+ever Connection: keep-alive Upgrade-Insecure-Requests: 1
Here’s an example response:
200 OK Connection: Keep-Alive Content-Encoding: gzip Content-Type: text/html; charset=utf-8 Date: Mon, 31 Feb 2020 00:00:00 GMT Last-Modified: Mon, 18 Jan 2019 02:36:04 GMT Server: Apache Transfer-Encoding: chunked X-Frame-Options: DENY (Response body here)
As you can see, there’s a lot of conversations going on behind the scenes—and this happens several times for each page you requested. Not all of these headers are strictly for rendering your page: Some tell the site not to track you (such as DNT headers), while others tell the browser not to allow frames on the site (such as X-Frame-Options). Sometimes these headers leak into the user realm (bet you’ve seen a few 404: File Not Found pages). The better you know your site, the more restrictive you can be when configuring your headers, and the more secure your site can be.
I will cover the most common security headers you will find on servers across the internet. For each of these headers, I’ll provide a standards document if one is available (such as a Working Draft or RFC), assuming the scope isn’t too broad (the entire HTTP specification, for example). I will also include additional resources and links to the CWEs, where applicable. This document uses the term “media types” to refer to what was previously called “MIME Types,” as IANA has changed the terminology.
Custom headers and the “X-” prefix
Standards organizations—what would we do without them? RFC 2047 §5.1 states that any nonstandard HTTP header be denoted and prefixed with “X-” (hence many of the headers in this blog). RFC 6648 deprecates this. Chances are that the legacy “X-” headers will outlive this blog post, so for nearly all purposes and intents, these should be used in lieu of their non-prefixed alternatives.
Imagine, if you will…
In this hypothetical scenario, imagine you are checking your employer’s intranet forum and you see a topic that catches your eye: “An estimated 60% of the workforce will be laid off this week!”
Panic ensues until the inevitable HR memo comes out stating that this forum post is false and no one is getting laid off. An investigation into how this could have happened uncovers that your organization has fallen victim to a social engineering scheme. But how did this schemer get in?
Here are some examples of vulnerabilities that may have been exploited:
- You can set your account’s password without providing your current password
- There is a cross-site scripting (XSS) vulnerability in the forums
- The forum admin only skimmed this wonderful blog entry.
These would allow the attacker to wreak havoc by using the following tactics:
- Make a really crafty forum topic title that everyone will click and talk about
- Leverages the XSS vulnerability within a forum topic
var cookie = document.cookie;
- Makes an XMLHttpRequest to their server with this cookie value
- Stand up a server to receive these cookies
- Make a cURL request back to the server, using the cookie to authenticate
- Set the password to a new value
- Log the user out
In this situation, anybody who clicks the forum topic will instantly have their account password reset and be logged off. With this tactic, attackers will be collecting accounts like Pokémon.
So, which headers can help us out here?
HTTP is a stateless protocol, which means it cannot associate state between two requests. Sessions are a technique of providing a unique identifier to your site’s users, allowing them to persist login state across several page loads. These may be persisted across URLs (bad) or across cookies (good). Because of that, we need to ensure we protect information from being stolen. There are three security-focused attributes within a cookie header, but today we are going to focus on HTTPOnly:
Set-Cookie: id=Rapid7; Expires=Wed, 06 Mar 2019 02:42:26 GMT; Secure; HttpOnly; SameSite=strict
The HTTPOnly flag informs the browser that the cookie may only be transmitted via HTTP—that is, it is not available to java script in the form of
alert(document.cookie);. This greatly reduces the utility in an XSS attack.
As discussed, there is little excuse for not using HTTPS throughout your application. The “Secure” flag instructs the browser to only send this cookie value to requests made via HTTPS. If your connection is subject to an HTTP Downgrade and MiTM attack, this cookie will not be transmitted which will prevent account theft.
The SameSite attribute is used to control how cookies are applied to requests when they originate from a third-party domain. If a user is logged into example.com, they can click around the site and maintain logged-in state. If they then visit rapid7.com, and Rapid7 links back to Example.com to leverage an XSRF vulnerability, then this will be executed against the user’s account on example.com.
If the SameSite attribute is set to Strict, the previous example would fail. The browser would detect this and not transmit the cookies—therefore, the attack would apply to a guest account.
X-XSS-Protection: 1; mode=block
Moz Top 500
This header is present on 35% of sites. Many browsers have built in XSS protection that site operators can control. A blank setting disables the protection mechanism, and an attribute of “1” instructs the browser to attempt to sanitize the content. Finally, setting the header to “1; mode=block” tells the browser to stop rendering the page. The latter option is recommended.
This header enables the in-built XSS filtering or blocking that browsers use, and therefore may have also blocked this attack by restricting access to the page where the XSS vulnerability existed.
The decision to go with the account theft model was to illustrate a real-world situation that could be dire to an organization without backups or that is visible to the public. Cookies can simply be stored, with no programmatic action being performed. It is possible for a stolen cookie to be used to perform any number of actions on your site, from buying items to deleting customers.
Other ways to limit this attack include:
- Regenerate cookie values and expire the previous value whenever the following happens:
- A user authenticates, or after logoff
- A user changes roles, such as logging into an admin panel
- After several page loads, this limits the validity period
- Limit the session duration
- Set either a “0” on the session cookie, or an explicit expiry time. (Also enforce this server-side, since spoofing this on the client is really easy.)
- Leverage an additional identifier along with the cookie, such as the user’s IP address, user agent, or other information.
- Write secure code
So, what are you waiting for? Go and evaluate your site to verify that your web server is correctly responding with headers designed for security, and stay tuned for my next blog on “Hidden Helpers: Security-Focused HTTP Headers.”