Secure NGINX Webserver24 Mar 2018
Before you begin
This post is specific to Nginx, but many of the principles can be applied to apache or other popular web servers with a few tweaks to the configuration statements.
To compare your security settings before and after, Mozilla will scan your website and give you a grade for your level of security at observatory.mozilla.org. You can also install wapiti to scan your site for vulnerabilities outside of the scope of this post.
View the report for Connor.rocks at observatory.mozilla.org/analyze/connor.rocks
- Nginx main config file
- Diffie-Hellman key file
- You can chose where to put this, but I recommend here for organization.
- PHP configuration file
- /etc/php.ini (default)
SSL/TLS could probably merit an entire post, so I’ll try to keep it sort. LetsEncrypt will do basically everything for free, and all you gotta do is run the installer. Here’s how: download certbot for your setup at certbot.eff.org, run it, and you can now use HTTPS.
From here I recommend upgrading your Diffie-Hellman key. Default key lengths are quite short, so we need to generate a new one. You will need Git if you have not already installed it.
Create the folder /etc/nginx/cert/ if it does not already exist, then run the following command. This could take a while. It took a full episode of Your Lie in April (~20 minutes) for me before it finally finished.
openssl dhparam 4096 -out /etc/nginx/cert/dhparam.pem
When it finishes, add this to your nginx config file right around where the ssl options added by certbot appear.
Last but not least, if yo do not see the following in the config file, add it to ensure that your server prefers to use at least generally acceptable ciphers.
Content Security Policy (CSP)
This will be the hardest section.
Content security policy is one of the most important aspects of securing your visitors aside from SSL/TLS, but is not supported by all browsers. The CSP itself is sent via HTTP headers. It limits what a browser will display by whitelisting or blacklisting specific content from third party sites. For example, if any external scripts run on your site e.g. Google analytics.
The command to add your own strict content security policy is
This command, as written will disable nearly all content from external sites. In order to relax this a bit we need to look at each directive. It should be added outside of any sections to apply it to the whole server, as opposed to a subset.
Each directive is added to the list separated by semicolons in the header. Notable values are ‘none’, ‘self’, and ‘unsafe-inline’. Using ‘none’ will disallow all sources of the content controlled by the directive. Using ‘self’ is just a shorter version of typing your own url, so for this site it would translate to ‘https://connor.rocks’, and would allow content from myself. Last but not least, ‘unsafe-inline’ is used mainly in script-src and style-src allowing scripts or css written in line with HTML. I do not recommend using it because it is quite easy to add lines to HTML in transit, meaning that your visitors may execute code injected by a man in the middle.
This table is an overview of common directives. An extended list can be found at w3.org.
|script-src||'self' js.external.com||Determines allowed sources of client side scripts.|
|style-src||'self' external.website.com||Determines allowed sources of CSS.|
|img-src||'self' external.website.com||Determines allowed sources of images.|
|form-src||'self'||Determines allowed sources of HTML forms. Usually this is just self.|
|frame-src||'self' external.website.com||Determines allowed sources of iframes. This includes things like embedded youtube videos.|
Example to add Paypal donate button to website with strict content security policy
These are the only directives needed to add a PayPal donate button to your site. Using a strict content security policy, browsers will not display the donate button unless you add PayPal as an acceptable source for images and forms.
Redirect traffic to HTTPS
Strict Transport Security (HSTS)
HSTS tells the client that they should use HTTPS, and remember it for the future.
It’s just an extra line in the config.
Max age is the time that the browser should remember that it should use HTTPS on your site. You can append ‘includeSubDomains’ to well… include sub domains.
Double check to be sure the redirect is working
Having the certificates and setup for HTTPS is great and all, but if no one uses it, then it is worthless.
This one is pretty easy. Just add this line that checks for http connections and redirects them to a corresponding https url.
Using this method is effective, but as of writing this, Mozilla’s observatory does not support detection for this method.
Prevent Information Disclosure
If you check the headers on a default install of nginx, you will find that it likes to give visitors a lot of information about your server that they don’t need. You can check the headers using curl -I http://localhost. Look for the ‘Server’ header and look at the information sent. It should look something like this.
$ curl -I http://localhost
HTTP/1.1 200 OK
Server: nginx/1.13.10 (Ubuntu)
Knowing the version number and operating system gives attackers a lot of information about your system for free. I’m not super into that so let’s turn it off. Add this line to an http block in your config file.
Oh boy here we go again. If you’ve got PHP, you’re probably giving out info for free. PHP wants everyone to know what version it is. Now we’re back to checking out your headers. Again, the header we’re looking for will look similar to this.
$ curl -I http://localhost
HTTP/1.1 200 OK
To prevent this we need to change some PHP settings in /etc/php.ini. Inside the file there is a line that will say expose_php, change it to off.
The X-Frame-Options header can be set to inform a browser what content will be allowed to render a page in a frame or object. This prevents third parties from embedding their content in your site.
There are three options for this header
X-Frame-Options: ALLOW-FROM https://external.website.com/
- DENY obviously denies all pages.
- SAMEORIGIN will allow pages from the same origin as the current site. Different browsers interpret origin to mean one of the following: top level, parent, or the whole chain.
- ALLOW-FROM will allow a specific site to embed content. A common use for this is to add YouTube as a valid source to show YouTube videos on your site.
The command to actually add this to your server is
Content Type Check
Again, this one is pretty simple to add.
Detect reflected cross site scripting attacks
You can see all the options for this header at developer.mozilla.org. Otherwise, just add this.
There is much more to do, but this is a solid foundation. If you have any questions or corrections, send them to firstname.lastname@example.org