Companies selling “security scorecards” are on the rise, and have started to become a factor in enterprise sales. I have heard from customers who were concerned about purchasing from suppliers who had been given poor ratings, and in at least one case changed a purchasing decision based initially on the rating.
I investigated how these ratings companies calculate company security scores, and it turns out they use a combination of HTTP security header usage and IP reputation.
IP reputation is based on blacklists and spam lists combined with public IP ownership data. These should generally be clean as long as your company doesn’t spam and can quickly detect and stop malware infections. HTTP security header usage is calculated similar to how the Mozilla Observatory works.
Therefore, for most companies, their score is largely determined by the security headers being set on public facing websites.
Setting the right headers can be done quickly (usually without significant testing), can improve website security, and can now help you win deals with security conscious customers.
I am dubious about the value of this test methodology and exorbitant pricing schemes these companies ask. I don’t believe it correlates to real product security all that well. However it certainly increases the importance of spending time setting headers and getting them right.
In this article, I will walk through the commonly evaluated headers, recommend security values for each, and give a sample header setting. At the end of the article, I will include sample setups for common applications and web servers.
A CSP is used to prevent cross site scripting by specifying which resources are allowed to load. Of all the items in this list, this is perhaps the most time consuming to create and maintain properly and the most prone to risks. During development of your CSP, be careful to test it thoroughly – blocking a content source that your site uses in a valid way will break site functionality.
CSP’s can be complex and confusing, so if you want a deeper dive, see the official site.
A good starting CSP might be the following (this likely requires a lot of modifications on a real site). Add domains in each section that your site includes somewhere.
# Default to only allow content from the current site # Allow images from current site and imgur.com # Don't allow objects such as Flash and Java # Only allow scripts from the current site # Only allow styles from the current site # Only allow frames from the current site # Restrict URL's in the <base> tag to current site # Allow forms to submit only to the current site Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';
This header tells the browser that the site should only be accessed via HTTPS – always enable when your site has HTTPS enabled. If you use subdomains, I also recommend enforcing this on any used sub domains.
Strict-Transport-Security: max-age=3600; includeSubDomains
This header ensures that the MIME types set by the application are respected by browsers. This can help prevent certain types of cross site scripting bypasses.
This one is a bit trickier than the others because you likely want different caching policies for different content types.
Any page with sensitive data, such as a user page or a customer checkout page, should be set to no-cache. One reason for this is preventing someone on a shared computer from pressing the back button or going through history and being able to view personal information.
However, pages that change rarely, such as static assets (images, CSS files, and JS files), are good to cache. This could be done on a page by page basis, or using regex on the server configuration.
# Don’t cache by default Header set Cache-Control no-cache # Cache static assets for 1 day <filesMatch ".(css|jpg|jpeg|png|gif|js|ico)$"> Header set Cache-Control "max-age=86400, public" </filesMatch>
This sets the time the cache should expire the current request. It is ignored if the Cache-Control max-age header is set, so we only set it in case a naive scanner is testing for it without considering cache-control.
For security purposes, we will assume that the browser should not cache anything, so we’ll set this to a date that always evaluates to the past.
This header indicates whether the site should be allowed to be displayed within an iFrame.
This should always be set to deny unless you are specifically using frames, in which case it should be set to same-origin. If you are using Frames with another site by design, you can white list the other domain here as well.
It should also be noted that this header is superseded by the CSP frame-ancestors directive. I still recommend setting this for now to appease tools, but in the future it will likely be phased out.
This can be a little confusing, so I drew up a diagram to illustrate how this header functions:
A sample Cookie definition:
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly
See the excellent Mozilla documentation on cookies for more information.
This header instructs browsers to halt execution of detected cross site scripting attacks. It is generally low risk to set, but should still be tested before putting in production.
X-XSS-Protection: 1; mode=block
Web Server Example Configurations
Generally, it’s best to add headers site-wide in your server configuration. Cookies are the exception here, as they are often defined in the application itself.
Before adding any headers to your site, I recommend first checking the observatory or manually looking at headers to see which are set already. Some frameworks and servers will automatically set some of these for you, so only implement the ones you need or want to change.
A sample Apache setting in .htaccess:
<IfModule mod_headers.c> ## CSP Header set Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; ## General Security Headers Header set X-XSS-Protection: 1; mode=block Header set Access-Control-Allow-Origin: http://www.one.site.com Header set X-Frame-Options: deny Header set X-Content-Type-Options: nosniff Header set Strict-Transport-Security: max-age=3600; includeSubDomains ## Caching rules # Don’t cache by default Header set Cache-Control no-cache Header set Expires: 0 # Cache static assets for 1 day <filesMatch ".(ico|css|js|gif|jpeg|jpg|png|svg|woff|ttf|eot)$"> Header set Cache-Control "max-age=86400, public" </filesMatch> </IfModule>
## CSP add_header Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'; ## General Security Headers add_header X-XSS-Protection: 1; mode=block; add_header Access-Control-Allow-Origin: http://www.one.site.com; add_header X-Frame-Options: deny; add_header X-Content-Type-Options: nosniff; add_header Strict-Transport-Security: max-age=3600; includeSubDomains; ## Caching rules # Don’t cache by default add_header Cache-Control no-cache; add_header Expires: 0; # Cache static assets for 1 day location ~* .(?:ico|css|js|gif|jpe?g|png|svg|woff|ttf|eot)$ try_files $uri @rewriteapp; add_header Cache-Control "max-age=86400, public";
If you don’t have access to the web server, or have complex header setting needs, you may want to set these in the application itself. This can usually be done with framework middleware for an entire site, and on a per-response basis for one-off header setting.
I only included one header for brevity in the examples. Add all that are needed via this method in the same way.
Node and express:
Add a global mount path:
app.use(function(req, res, next) res.header('X-XSS-Protection', 1; mode=block); next(); );
Java and Spring:
I don’t have a lot of experience with Spring, but Baeldung has a great guide to header setting in Spring.
I am not familiar with the various PHP frameworks. Look for middleware that can handle requests. For a single response, it is very simple.
header("X-XSS-Protection: 1; mode=block");
Python / Django
Django includes configurable security middleware that can handle all these settings for you. Enable those first.
For specific pages, you can treat the response like a dictionary. Django has a special way to handle caching that should be investigated if trying to set cache headers this way.
response = HttpResponse() response["X-XSS-Protection"] = "1; mode=block"
Setting headers is relatively quick and easy. You will have a fairly significant increase in your site security for data protection, cross site scripting, and click jacking.
You also ensure you don’t lose future business deals as a result of company security ratings that rely on this information. This practice seems to be increasing, and I expect it to continue to play a role in enterprise sales in future years.
Did I miss a header you think should be included? Let me know!