Sanity check of my config - is it secure?

Hi

We had Nginx setup on a domain serving static content
(images[0-9].domain.com). Recently we’ve made our main domain also use
the same Nginx installation and proxy any php requests to Apache.

All is working fine. We’ve set it to serve all static content from our
images.domain.com domains, and it will also catch any static content
served from the main www.domain.com as well, before finally passing any
dynamic php requests through to Apache.

What I want to check is that there are no problems with the
configuration that would let someone view the source of our PHP pages or
access restricted ‘admin’ directories, etc. I’ve not configured NGinx
before so would appreciate some help checking my config?

user www;
worker_processes 6;

error_log /var/log/nginx-error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
#access_log logs/access.log main;

sendfile        on;
#tcp_nopush     on;

 ## Timeouts
    client_body_timeout   10;
    client_header_timeout 10;
    keepalive_timeout     5 5;
    send_timeout          10;

gzip  on;

gzip_static on;

gzip_comp_level   5;
gzip_http_version 1.0;
gzip_min_length   1000;
gzip_types        text/plain text/css image/x-icon

application/x-javascript;

server{
            listen 80 default_server; #all other hosts deny
            server_name _;
            return 444;
}

server { #IMAGES DOMAIN
listen x.x.x.x:80;
server_name images.x.com images1.x.com images2.x.com stat

    server_tokens off;
    gzip_comp_level 9;
    if ($request_method !~ ^(GET|HEAD)$ ) {
     return 444;
    }



    # Only serve these locations
    location ^~ /images/folder1/{

     if ($request_uri ~*

(^/|.ico|.css|.js|.swf|.gif|.jp?g|.png)$ ) {
break;
}

            root /home/vhosts/x.com/httpdocs;
            expires 7d;

    }
    location ^~ /images/folder2/{
           root /home/vhosts/x.com/httpdocs;
            expires 7d;

    }

     location ^~/images/ {
           root /home/vhosts/x.com/httpdocs;


    }


     location ^~ /applets/{
           root /home/vhosts/x.com/httpdocs;
           expires max;
    }
    location ^~ /css/{
           root /home/vhosts/x.com/httpdocs;
           expires max;
    }


    location ~* \.(ico|css|js|swf|gif|jp?g|png)$ {
            root /home/vhosts/x.com/httpdocs;

    }
    location ~* \.(php|html){
            return 444; #should not happen, but just incase
    }
            if ($host !~

^(images.x.com|images1.x.com|images2.x.com)){
return 444;
}
location ^~/admin/ {
deny all;

    }



}

server { #MAIN DOMAIN - REDIRECT TO www.
listen x.x.x.x:80;
server_name x.com;
rewrite ^(.*) http://www.x.com$1 permanent;

    }

server { #MAIN DOMAIN
listen x.x.x.x:80;
server_name www.x.com;

    #access_log /var/log/nginx.access.log;
    error_log /var/log/nginx.error.log;

     location /nginx_status {

        stub_status on;
        access_log   off;
       allow x.x.x.x/24;
        deny all;
    }

    location ^~ /images/folder1/{
           root /home/vhosts/x.com/httpdocs;
            expires 7d;

    }
    location ^~ /images/folder2/{
           root /home/vhosts/x.com/httpdocs;
            expires 7d;

    }


     location ^~/images/ {
           root /home/vhosts/x.com/httpdocs;

    }


     location ^~ /applets/{
           root /home/vhosts/x.com/httpdocs;
           expires max;
    }
    location ^~ /css/{
           root /home/vhosts/x.com/httpdocs;
           expires max;
    }


    location ~* \.(ico|css|js|swf|gif|jp?g|png)$ {
            root /home/vhosts/x.com/httpdocs;
            #expires max;
    }

    location / {
            proxy_pass http://127.0.0.1:8080;

            #CACHING
            #proxy_cache my-cache;
            #proxy_cache_valid 200 302 1m;
            #proxy_cache_valid 404 1m;

             #proxy_redirect off;
            proxy_set_header Host $host;
           proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For

$proxy_add_x_forwarded_for;

            client_max_body_size 200m; #max upload size

            client_body_buffer_size 128k;
            proxy_connect_timeout 900;
            proxy_send_timeout 900;
            proxy_read_timeout 900;
            #proxy_buffer_size 4k;
            #proxy_buffers 4 32k;
            #proxy_busy_buffers_size 64k;
            #proxy_temp_file_write_size 64k;

    }

}

}

Posted at Nginx Forum:

Have a look at these:

http://wiki.nginx.org/Modules

https://calomel.org/nginx.html

And what version of nginx (latest is 1.0.2)? What OS?

On Sun, May 22, 2011 at 15:56, benseb [email protected] wrote:

gzip on;

gzip_static on;

If you’re serving so much static content you may as well enable
gzip_static, although it’s pointless for compressed images like jpg.

gzip_types text/plain text/css image/x-icon

No html? Javascript?

   server_tokens off;

Won’t disable the Server: header. There’s a module that can do that.

    if ($request_uri ~*

(^/|.ico|.css|.js|.swf|.gif|.jp?g|.png)$ ) {

You can probably replace this if-statement with a regex.

    location ^~ /applets/{
          root /home/vhosts/x.com/httpdocs;
          expires max;
   }
   location ^~ /css/{
          root /home/vhosts/x.com/httpdocs;
          expires max;
   }

And merge these locations (as well as others) perhaps? Something like
^~ /(applets|css)/

Just a few hints, like i said i’m no expert.


Mars 2 Stay!

/etc

Thanks for those tips. I’ll have a read around those bits

My main concern is that by combining different locations, etc i may have
accidentally exposed a security issue - I need to ensure everything
that’s not an image/css file/javascript file etc is either denied or
proxied to apache.

Posted at Nginx Forum:

On Sun, 2011-05-22 at 13:24 -0400, benseb wrote:

Thanks for those tips. I’ll have a read around those bits

My main concern is that by combining different locations, etc i may have
accidentally exposed a security issue - I need to ensure everything
that’s not an image/css file/javascript file etc is either denied or
proxied to apache.

You should write some tests! It is much better to use some well thought
out tests in addition to studying the config.

Justin

Can anyone help with the above request, regarding checking if a filename
DOESNT match the whitelist above (block all other filetypes)

Posted at Nginx Forum:

For our static subdomain (which should only serve static content) I’d
like to add a whitelist of content - so if the filename doesn’t match
jpg/png/gif/css/js etc it will return a 444

What’s the correct syntax to do this:

location !~* .(?:ico|css|js|gif|jpe?g|png)$ {
#dont serve any other files
return 444;
}

The above doesnt work as !~* isn’t allowed. What the best way to say
“not one of the following extensions”. All the examples in the
documenation are the opposite?

Cheers

Posted at Nginx Forum:

Thanks, in your opinion what’s the best way to approach this? I
basically want to ensure that our static.domain.com subdomain ONLY
servers image/js/css files.

Whilst I have set ‘location’ for only the folders which have images, etc
in, I want to ensure that if someone put a script into one of those
directories, it would not be executed.

Posted at Nginx Forum:

On 26 Mai 2011 20h36 WEST, [email protected] wrote:

Thanks, in your opinion what’s the best way to approach this? I
basically want to ensure that our static.domain.com subdomain ONLY
servers image/js/css files.

Whilst I have set ‘location’ for only the folders which have images,
etc in, I want to ensure that if someone put a script into one of
those directories, it would not be executed.

If I understood correctly: there’s no need to worry then. If there’s
no embedded interpreter (Perl/Lua) or a fastcgi backend configured on
that vhost. There’s no way that someone will be able to run a script
from the web there.

— appa

I presume in that case, if there was a script and no interpreter, it
would just display the contents of the file - which again could be a
security risk.

So I need to perhaps limit the mime types that can be served, or the
file extensions - which ever is most secure?

I basically don’t want a php script in that directory being served and
the source code being visible

Posted at Nginx Forum:

On 26 Mai 2011 20h22 WEST, [email protected] wrote:

Your’re letting the reverse logical style of mod_rewrite and .htaccess
color your perception.

In Nginx things operate in a forward logical way:

  1. Define which extensions you want to allow, e.g.:

location ~* .(?:jpe?g|png|ico|gif|css|js|) {

serve the files

}

location ~* (which extensions are going to be blocked) {
return 444;
}

Mind you that relying solely on the file extension is a rather weak
way of filtering files. You can tamper the file magic number quite
easily.

Can anyone help with the above request, regarding checking if a
filename DOESNT match the whitelist above (block all other
filetypes)

— appa

On 26 Mai 2011 20h49 WEST, [email protected] wrote:

I presume in that case, if there was a script and no interpreter, it
would just display the contents of the file - which again could be a
security risk.

So I need to perhaps limit the mime types that can be served, or the
file extensions - which ever is most secure?

Do both. That’s my advice. Regarding the later, something along the
lines of:

location ^~ /static_files_dir/ {

location ~* /static_files_dir/.+.(?:jpe?g|png|gif|ico|css|js)$ {
expires 30d;
}

location ~* /static_files_dir/.*.php$ {
return 444; # return an empty response for a php file
}
}

— appa

Hi

On Thu, May 26, 2011 at 20:59, António P. P. Almeida [email protected]
wrote:

return 444; # return an empty response for a php file

AFAIK 444 just closes the connection, sending nothing.
http://wiki.nginx.org/HttpRewriteModule#return

Or does it actually send an empty response (without headers)?


Mars 2 Stay!

/etc

Thanks people

So there’s no way to say “If the file ISNT a jpeg/gif/css/js” deny. The
only way is to say ‘if .php’ deny, ‘if .txt deny’ etc?

I’d prefer to whitelist the files i DO want to return and block
everything else, incase I forget something to block?

Ben

Posted at Nginx Forum:

On 26 Mai 2011 21h10 WEST, [email protected] wrote:

Thanks people

So there’s no way to say “If the file ISNT a jpeg/gif/css/js”
deny. The only way is to say ‘if .php’ deny, ‘if .txt deny’ etc?

I’d prefer to whitelist the files i DO want to return and block
everything else, incase I forget something to block?

Try this:

location ~* /static_files_dir/(?[^.]).(?.)$ {
if ($extension !~ (?:jpe?g|png|gif|ico|css|js)) {
return 444;
}
}

It’s rather ugly :frowning:

— appa

On 26 Mai 2011 21h10 WEST, [email protected] wrote:

Thanks people

So there’s no way to say “If the file ISNT a jpeg/gif/css/js”
deny. The only way is to say ‘if .php’ deny, ‘if .txt deny’ etc?

I’d prefer to whitelist the files i DO want to return and block
everything else, incase I forget something to block?

Try this:

location ~* /static_files_dir/(?:[^.]).(?.)$ {
if ($extension !~ (jpe?g|png|gif|ico|css|js)) {
return 444;
}
}

It’s rather ugly :frowning:

— appa

PS: No need to use ?: in the if condition.

PPS: Note that if the filenames have dots ‘.’ in them the above regex
will fail to capture the extension correctly. You’re opening a can of
worms config wise when going down this path.

On 26 Mai 2011 21h10 WEST, [email protected] wrote:

Thanks people

So there’s no way to say “If the file ISNT a jpeg/gif/css/js”
deny. The only way is to say ‘if .php’ deny, ‘if .txt deny’ etc?

I’d prefer to whitelist the files i DO want to return and block
everything else, incase I forget something to block?

Try this:

location ~* /static_files_dir/(?:[^.]).(?.)$ {
if ($extension !~ (jpe?g|png|gif|ico|css|js)) {
return 444;
}
}

It’s rather ugly :frowning:

— appa

PS: No need to use ?: in the if condition.

Thanks for the advice

Seems strange that this isn’t an easy thing to do. After all, ALL
security advise always recommends whitelisting what you want and denying
everything else!

Posted at Nginx Forum:

On 26 Mai 2011 21h30 WEST, [email protected] wrote:

Thanks for the advice

Seems strange that this isn’t an easy thing to do. After all, ALL
security advise always recommends whitelisting what you want and
denying everything else!

The config with two regex locations nested did that. But if you’re
asking for a catch all regex that blocks every other extension
besides css, js, &c, then you’re thinking in terms of the
complement of the set of allowed extensions.

It’s easier to enunciate the negative than the positivem due to the
fact that you’re “searching” a wide space.

— appa

That would be a nice simple solution. I’ll give that a go!

Posted at Nginx Forum:

Without actually testing anything, can’t you do something like this:

location ~* .(jpe?g|png|gif)$ {
}

location / {
return 444;
}

I.e., if the extension looks like an image, handle it normally.
Otherwise,
in the normal case, return 444 (or whatever error code is appropriate).

Aaron