Configure Nginx to Protect Ghost Login Page

In information security, it is not possible to be correct all the time - things will break, controls will fail, and software bugs will exist; those things are inevitable, so a key element of information security is to reduce the attack surface of a system.

Wikipedia has a good definition of “attack surface”:


“The attack surface of a software environment is the code within a computer system that can be run by unauthorized users. This includes, but is not limited to: user input fields, protocols, interfaces, and services. OSSTMM 3 Defines Attack Surface as ‘The lack of specific separations and functional controls that exist for that vector’. One approach to improving information security is to reduce the attack surface of a system or software. By turning off unnecessary functionality, there are fewer security risks. By having less code available to unauthorized actors, there will tend to be fewer failures. Although attack surface reduction helps prevent security failures, it does not mitigate the amount of damage an attacker could inflict once a vulnerability is found.”

Ghost is some fantastic software but will inevitably have security vulnerabilities, especially in the complex pieces within the administration functions. It would be a good idea to reduce Ghost’s attack surface by restricting what systems can even access the administrative portion of Ghost, and access controls within Nginx allow us to do so if we are using Nginx to proxy connections to Ghost.

Access controls within Nginix are pretty straightforward. For example, if you want to allow a specific IP address to access a portion of your site, but deny all others, you’d define the location and access controls within the server block:

server {
    ...
    location /mysecretlocation/ {
        allow 1.1.1.1;
        deny all;
    }
    ...
}

That’s pretty straightforward, causing Nginx to allow connections to www.example.com/mysecretlocation by the IP address 1.1.1.1, while kicking a 403 Forbidden message to all others.

This gets a little different when using Nginx to proxy connections to Ghost, but it is still pretty straightforward - we just need to add the proxy information to the location block, like this:

server {
    ...
    location /ghost/ {
        allow 1.1.1.1;
        deny all;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   Host $http_host;
        proxy_set_header   Connection "";
        proxy_http_version 1.1;
        proxy_pass         http://ghost_upstream_defined_in_nginx_conf;
    }
    ...
}

This config does the same thing as before but applies it to the proxy for Ghost, which is important since the URI of /ghost/signin doesn’t actually exist in the website’s directory root, but is only understood by Ghost. However, if an IP other than 1.1.1.1 were to go to www.mywebsite.com/ghost/signin, they would get a 403 Forbidden message.

Now, when a vulnerability is identified that affects Ghost’s administration functionality, the website’s exposure to that vulnerability should be greatly reduced.

If you have any feedback or questions, contact me on Twitter at @codyhatch until I get my contact page up on this site.