Claudio Borges

Technical blog about Linux, BSD, Mac OS X, Games and etc.

Archive for the ‘Linux’ tag

FISL and QconRJ presentation

without comments

Hi folks, long time since my last post. I was a little bit busy with a lot of issues, without time to play video-game (leisure is important), and the worst, without time to play with my daughter.

So, today I’ll talk about my last presentation. In the last two months I presented “how does a big shared web hosting environment work” at FISL and QconRJ. The slides are available here.

That is all for now. See ya.

Written by but3k4

September 26th, 2015 at 2:33 pm

How to get an A+ on Qualys SSL Labs with NginX

without comments

Hi Folks, in this post I’ll show you how to configure your SSL Virtual Host to get an A+ on SSL Labs.

SSL and security issues, in general, are complicated subjects and I won’t describe all SSL aspects, just a few configurations to have the perfect environment with NginX.

The pre-requisites to get an A+ on SSL Labs is to have OpenSSL 1.0 or later. It’s necessary because you need to use TLS 1.2 and your NginX package must be built with it. If you are running the latest version of Debian, Ubuntu or CentOS, you’re already using OpenSSL 1.0.

PS: If you want to use the latest NginX build (1.9.3) for CentOS or Debian, click here.

Continuing with the article, let’s talk about protocols. SSLv2 and SSLv3 are insecure protocols, as every System Administrator knows (or at least should know). SSLv2 doesn’t provide a sufficiently high level of security and it’s deprecated in RFC 6176. On the other hand, SSLv3 was killed by the POODLE attack and it’s deprecated in RFC 7568. Probably, these two arguments are enough for you disable both of them completely.

PS: Disabling SSLv2 and SSLv3 won’t let old browsers like IE6 running on Windows XP access your website, because they don’t support TLS.

Within your SSL virtual host, change the ssl_protocols line to enable the TLS protocols as follows:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

With ssl_protocols configured properly, you will configure the ssl_ciphers. The default ciphers suites used by default by the NginX installation (1.0.5 or later), are enough for an excellent compatibility and security:

ssl_ciphers "HIGH:!aNULL:!MD5";

Or if you want to use a custom line with better performance (it will reduce CPU usage) but with less compatibility:

ssl_ciphers "HIGH:!aNULL:!MD5:!DHE-RSA-AES128-SHA256:!DHE-RSA-AES128-SHA:!DHE-RSA-AES128-GCM-SHA256:!DHE-RSA-AES256-GCM-SHA384:!DHE-RSA-AES256-SHA256:!DHE-RSA-AES256-SHA:!DHE-RSA-CAMELLIA256-SHA:!DHE-RSA-DES-CBC3-SHA:!DHE-RSA-CAMELLIA128-SHA";

PS: To see the complete ciphers suites list, click here.

For the configs above to work properly, I mean, for the server ciphers to be preferred over client ciphers, when using the TLS protocols, enable the following option:

ssl_prefer_server_ciphers on;

In addition, you need to configure a few extra steps. They aren’t related with security directly, but with HTTPS performance.

The ssl_session_cache will configure a cache shared between all worker processes. The cache size is specified in bytes and one megabyte can store about 4000 sessions. The ssl_session_timeout specifies a time during which a client may reuse the session parameters stored in cache. To minimize the TTFB (Time To First Byte), it’s beneficial to set a smaller value for ssl_buffer_size .

To configure them, add or modify the lines (if you already have them) as follows:

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_buffer_size 4k;

It’s time to talk about certificates. Invalid certificates can cause the browser to display warning messages and stop talking with the website. Using a valid certificate is an essential step and sometimes, even using it, you can get warning messages because the certificate signature algorithm is SHA-1. To fix this problem, basically, you need to replace SHA-1 with SHA-2 certificates. To do that, you need to generate a new CSR and request the Certificate Authorities to sign a new certificate with SHA256. SSL Labs or this website can check if your website is using SHA-1.

PS: Don’t forget, you need to get the intermediate CA certificates signed with SHA256 as well.

With the new certificates (if you aren’t using them yet), sometimes it’s necessary to configure the ssl_verify_depth. It sets the verification depth in the client certificates chain.

ssl_verify_depth 3;

Diffie-Hellman is an asymmetric algorithm used by a lot of protocols, including IPSec, SSL, SSH, PGP and other PKI systems. It provides the capability for two communicating parties to agree upon a shared secret key between them. Also, it is an algorithm created to address the issue of secure encrypted keys from being attacked over the internet when in transmission.

The NginX relies on OpenSSL and uses the default Diffie-Hellman DHE key, which includes a 1024-bit key. The problem is, if you are using a Diffie-Hellman primes smaller than 1024-bit, your system is vulnerable to the Logjam Attack. To fix this problem, you need to generate a stronger DHE parameter. I recommend at least a 2048-bit key. The way to generate it is:

mkdir /etc/nginx/ssl
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048

And configure the NginX to use it:

ssl_dhparam /etc/nginx/ssl/dhparam.pem;

HSTS (HTTP Strict Transport Security) is an IETF standards tracking protocol and it is specified in RFC 6797. It is a web security policy mechanism, which is necessary to protect secure HTTPS websites against downgrade attacks, and it greatly simplifies protection against cookie hijacking. Basically, it tells the browser that the website should only be accessed through a secure connection (SSL), instead of using HTTP.

If you access the HTTPS version of the site, the HSTS header will remember this option when trying to access the same site using HTTP and it will automatically switch to HTTPS, provided that the max-age parameter has not expired.

The HSTS Policy is communicated by the server to the user agent via an HTTP response header field named “Strict-Transport-Security”.

To configure HSTS, add the line below to your SSL Virtual Host as follows:

add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";

The option above tells the web browser to always use SSL to access this website for the next 365 days. More information about HSTS, can be found here.

And now (finally) add the Mod SPDY. SPDY (pronounced speedy) isn’t a security protocol, but an open network protocol developed primarily at Google for transporting web content. It works over SSL/TLS and it manipulates HTTPS traffic with the particular goals of reducing web page load latency and improving web security. It’s the precursor of HTTP2.

PS: The NginX team plans to release new versions of both NginX and NginX Plus by the end of 2015 and they will include support for HTTP/2.

To verify if your NginX package has SPDY support enabled, try this:

# nginx -V 2>&1 | sed -e 's/_module /_module\n/g' | grep spdy

If the command above shows the output “–with-http_spdy_module”, your NginX is ok. If not, you need to install a NginX build with SPDY support. If you are running the latest versions of Debian, Ubuntu or CentOS, you already have SPDY support.

With the SPDY module enabled, let’s configure the SSL virtual host to use it. You just need to change your virtual host from:

listen 443 ssl;

to:

listen 443 ssl spdy;
spdy_headers_comp 3;
add_header Alternate-Protocol 443:npn-spdy/3;

and reload your NginX:

# /etc/init.d/nginx reload

Now with SPDY working correctly, modern browsers will get your site content over SPDY and old browsers will use normal SSL. If you want to check if your site is using SPDY, use the SPDYCheck.org. If you are using Google Chrome, there is an extension that checks if the website your are visiting has the SPDY protocol enabled.

Another good practice is to redirect all HTTP traffic through your SSL virtual host. I mean, accessing this website via HTTP will automatically redirect the user to access the website via SSL/TLS and SPDY. To do that, modify your HTTP virtual host as follows:

server {
    listen 80;
    server_name yodaime.claudioborges.org;
    return 301 https://$server_name$request_uri;

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log notice;
}

My HTTPS virtual host is:

server {
    listen 443 ssl spdy;

    root /srv/default/www;
    index index.php index.html;

    spdy_headers_comp 3;
    add_header Alternate-Protocol 443:npn-spdy/3;

    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";

    server_name yodaime.claudioborges.org;

    access_log /var/log/nginx/access.log main;
    error_log /var/log/nginx/error.log notice;

    ssl on;
    ssl_certificate /etc/nginx/ssl/claudioborges_org.pem;
    ssl_certificate_key /etc/nginx/ssl/claudioborges_org.key;
    ssl_verify_depth 3;

    ssl_dhparam /etc/nginx/ssl/dhparam.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    ssl_ciphers "HIGH:!aNULL:!MD5";

    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_buffer_size 4k;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_intercept_errors on;
    }

    location = /favicon.ico {
        access_log off;
        log_not_found off;
    }
}

That is all for now folks. If you have any questions about this article, feel free to ask me.

Written by but3k4

July 29th, 2015 at 4:56 pm

Using NginX to block a specific HTTP user agent

without comments

Hi Folks, this is my first article in English. My next posts will be in English, I hope everybody is comfortable with that.

I have been working with NginX since version 0.9.7. And this is the first of a plenty of articles about it.

NginX is a powerful web server with a lot of features. It can do amazing things, for example, you can use it for HTTP load balancing or as a forward proxy server and its configuration is pretty easy. As opposed to Apache which has dynamic modules that you can load at your will, NginX is a static binary with built-in modules enabled in compile time.

In this article, I’ll explain how to configure a custom error page and how to block a specific user agent. We’ll use the 480 status code in our error page. The 4xx class of status code is intended for cases in which there seems to be a client error. In fact, they’re making a big mistake by using an outdated browser. We want to offer the best services to our customers, even if we need to force them to update their browser.

Before the main location statement, we need to define the user agent that we want to block and the specific error code:

if ($http_user_agent ~* "MSIE 6.0;") {
    return 480;
}

The if statement above will block just Internet Explorer 6. NginX doesn’t support complex conditions or nested if statements. If you want or need to do that, you need to use regular expressions to have multiple matches or a hack (that I will cover in another post)

The code below will block Internet Explorer version between 6.x and 8.x:

if ($http_user_agent ~* "MSIE ([6-8]{1,}\.\d{0,}\w?\d?);") {
    return 480;
}

PS: You can block any HTTP user agents with GET / POST requests.

If you want to use my error page, click here or you can create a 480.html file in your document root, for example /srv/default/www/. This page will be used when ie6 users try to access our website.

The content is:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>Please upgrade your browser</title>
<style type="text/css">
body{text-align:left; font-size:13px; font-family:Tahoma, sans-serif;background:#EEEEEE;}
</style>
</head>
<body>
    <h1>It's time to upgrade your browser</h1>
    <p>You’re using an outdated version of Internet Explorer. Many websites no longer support Internet Explorer 6 and 7. You won't be able to view this website until you upgrade your browser.</p>
    <h2>Internet Explorer 9</h2>
    <span><a href="http://go.microsoft.com/fwlink/?LinkId=398860">Download now</a></span>
    <h3>Not sure?</h3>
    <p>There are many reasons you should upgrade to a newer version of Internet Explorer. Here are just a few:</p>
    <div>
        <ul>
            <li><p>Internet Explorer 9 gives you a faster, safer browsing experience with better privacy protection.</p></li>
            <li><p>It's free and you can download it with just one click if you're using Windows Vista SP2 or higher.</p></li>
        </ul>
    </div>
    <h3>Still have questions?</h3>
    <div>
        <ul>
            <li><p>Visit the <a href="http://go.microsoft.com/fwlink/?LinkId=399116">Internet Explorer Support page</a></p></li>
            <li><p>Visit the <a href="http://support.microsoft.com">Microsoft Support page</a></p></li>
        </ul>
    </div>
</body>
</html>

If you want to see the page preview, click here

The next step is to configure the virtual host to use our error page. So, edit your virtual host file and add the lines:

error_page 480 @480;
location @480 {
    internal;
    try_files /480.html =403;
}

PS: You can put your error pages in another directory. You just need to set a root directive with another directory, ex:

error_page 480 @480;
location @480 {
    internal;
    root /srv/error/www;
    try_files /480.html =403;
}

Now, let’s suppose you want to block offline browsers like wget or libwww-perl. The process is the same, but this time, we will return the 403 error code (Forbidden).

if ($http_user_agent ~* "(wget|libwww-perl)") {
    return 403;
}

My virtual host code is:

server {
    listen 80;
    server_name godaime.claudioborges.org;
    index index.php index.html;
    
    root /srv/default/www;
    
    charset utf-8;
    
    include /etc/nginx/default.d/*.conf;
    
    error_page 480 @480;
    location @480 {
        internal;
        try_files /480.html =403;
    }   

    if ($http_user_agent ~* "MSIE ([5-8]{1,}\.\d{0,}\w?\d?);") {
        return 480;
    }

    if ($http_user_agent ~* "(wget|libwww-perl)") {
        return 403;
    }

    location / {
        try_files $uri $uri/ =404;
    }
    
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_intercept_errors on;
        fastcgi_index index.php;
        include fastcgi_params;
    }

    location = /favicon.ico {
        access_log off;
        log_not_found off;
    }   

    access_log /var/log/nginx/access main;
    error_log /var/log/nginx/error.log;
}     

Now you know how to block HTTP user agents. That is all for now folks.

Written by but3k4

July 18th, 2015 at 9:45 pm