How to secure your webapp
Written by David Mytton — Subscribe now.
It’s impossible to get to 100% security but there are steps you can take to secure your webapp for your users, to help mitigate against different types of attacks both against you, your webapp and your customers themselves. These are all things we’ve implemented with Server Density v2 to help harden the product as much as possible.
These tips are in addition to security best practices such as protecting against SQL injection, filtering, session handling, and XSRF protection. Check out the OWASP cheat sheets and top 10 lists to ensure you’re covered for the basics before implementing the suggestions below:
When I originally built Server Density back in 2009, access and monitoring agent postbacks were allowed over HTTP and HTTPS to make things as easy as possible to set up – most servers always allow outbound port 80. We changed the default to HTTPS some time ago and it turns out it’s not the issue I thought it would be. SSL is typically considered a performance bottleneck but that isn’t really true, so there’s actually no reason not to force SSL for all connections.
Server Density v2 uses a new URL so we can force SSL for both new agent deployments and access to the web UI. We still support the old domain endpoint under non-SSL but will eventually be retiring it.
Run your URL against the Qualys SSL server test to get an excellent report on how good your implementation is, along with recommendations for further security.
We are still considering the best way to resolve the RC4 vulnerability, which will most likely involve supporting only TLS1.2. However, only dev versions of browsers currently support it. We offer it as an option for browsers to choose and as browser support improves, we’ll likely disable older versions.
Support SSL with perfect forward secrecy
Normally every connection to your SSL URL is encrypted using a single private key. If someone were to capture all traffic, it could be decrypted if they accessed your key. Perfect forward secrecy addresses this by negotiating a new key with every session. This means that compromise of one key will only affect the data in that one session.
To do this you need to allow certain cipher suites in your web server configuration.
ECDHE-RSA-AES128-SHA:AES128-SHA:RC4-SHA is compatible with most browsers. More theoretical and implementation details can be found in this blog post.
We terminate SSL at our nginx load balancers and implement SSL using these settings:
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; # prefer RC4-SHA to avoid BEAST ssl_ciphers ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH;
You can tell if you're connected using perfect forward secrecy by the cipher type by looking for
ECDHE_RSA when viewing the connection details in Chrome:
Use Strict Transport Security
Forcing SSL should be combined with HTTP Strict Transport Security otherwise you have a risk of users entering your domain without the protocol e.g. typing example.com rather than https://example.com and then being redirected to https. It's the redirect that opens a security hole because there's a short time when communication is over HTTP.
This is solved by sending an STS header with your response, which is remembered by the browser and forces it to do the http->https conversion without actually issuing a request.
This is achieved by sending the header with a time that the browser will remember the setting for, before checking again:
Our header is set for 10 years and includes all subdomains because each account gets their own URL e.g. example.serverdensity.io.
Submit your strict transport security setting to browser vendors
There's still a potential hole even with STS headers because they only get sent after the first request. To combat this you can submit your URL to browser vendors and they will force the browser to only ever access your URL over SSL.
You can find out about how this works for Chrome and submit your URL to them. Firefox seeds from the Chrome list.
Enforce a content security policy
Of the top 10 most common security vulnerabilities, cross site scripting (XSS) is number 3. This is where remote code can be injected and executed on your site, usually through incorrect or lack of good filtering.
A good way to combat this is to provide a whitelist of allowed remote resources. If a script URL is not matched by this list, then browsers will block it. It's much easier to implement this on a new product because you can block everything, then open specific URLs as you add functionality. However, using browser developer tools you can easily see which remote hosts are being called.
The CSP we use is:
content-security-policy:script-src 'self' 'unsafe-eval' https://maps.google.com https://*.gstatic.com https://*.googleapis.com https://*.mixpanel.com https://*.mxpnl.com; connect-src 'self' https://maps.google.com https://*.gstatic.com https://*.googleapis.com https://*.mixpanel.com https://*.mxpnl.com; frame-src 'self' https://maps.google.com https://*.gstatic.com https://*.googleapis.com https://*.mixpanel.com https://*.mxpnl.com; object-src 'none'
We have specifically allowed unsafe-eval here as we're working to rewrite a number of 3rd party libraries which require this.
Be careful with wildcarding on domains which can have any content hosted on them. For example wildcarding *.cloudfront.net would allow anyone to host any script because it's Amazon's CDN which everyone can upload files to!
Also note that
Content-Security-Policy is the standard header but Firefox and IE only support
X-Content-Security-Policy. See the OWASP documentation for more information about the header names and directives and check out this tutorial for a good introduction.
Other HTTP security headers
You can enable some additional security features in certain browsers by setting other response headers. They're not widely supported so are only another layer of (thin) protection, but are still worth considering:
x-content-type-options is an IE only header which can prevent content sniffing XSS attacks. This is especially useful if you allow user uploaded content. x-frame-options can help prevent some click-jacking attacks by stopping your site from being loaded in a frame. x-xss-protection is another IE only header to enable some XSS filtering in the browser.
Do passwords, remember me and login resets properly
Since this is the main gateway to your app, you should make sure you implement all stages of logging in properly. It only takes a short amount of time to research and design a secure process:
- Registration and login should use salting and cryptographic functions (such as bcrypt) to store passwords, not plain text or MD5 hashing.
- Password reset should use an out of band method to trigger resets e.g. requiring a username then e-mailing a one time, expiring link to the on-record e-mail address where the user can then choose a new password. Read more guidance and a checklist.
- Remember me functionality should use secure tokens to recognise the user, not storing their credentials in cookies!
Offer multi factor authentication
If your app is anything more than a trivial consumer product, you should offer multi factor authentication which requires the user to authenticate using something they have with them before they can log in. Use the Google Authenticator standard because it has authentication apps available for all platforms and has libraries for pretty much every platform. It's incredibly annoying to have to install a custom, proprietary app for this so do not implement your own system!
Be sure to reauthenticate for things like adding/removing MFA tokens. We decided to do this for all user profile changes but have a timeout so that the user isn't frustrated by having to authenticate every single time for most actions e.g. changing password. But adding a new token/removing an existing one requires authentication every time.
MFA really is crucial for any serious application because it's the only way to protect against account hijacking.
Get security audits
Nobody is perfect and the only way to ensure you're doing things correctly is to get a 3rd party to review your security, ideally by attempting to break it.
We do our own security reviews internally as part of our code review and deployment process (many eyes on the code) but also have regular reviews from external security consultants. Our reviews are done by the guys at &yet (through lift security) but Matasano have a good reputation also. I'd recommend having one firm do an audit, implementing their fixes and then have another firm audit those changes.
Security is all about adding layers to help mitigate risk of attacks, but remember the keyword here is mitigation of risk. New threats are always emerging so you have to do security continuously. This means regular reviews of all existing measures, checking for new defence mechanisms and staying aware of security announcements.