SOLVED: IE, SSL and Nginx problems

We are experience a problem with some users hitting our site via ssl https://.org. On most browsers it is working correctly. On some instances of Internet Explorer however the domain never resolves. It errors out with "Internet Explorer cannot display the webpage."

GoDaddy who is the certificate authority insists there is no problem with the certificate itself and that it must be something with our server config. The certificate is set up to accept SSL 2.0, SSL 3.0 and TLS 1.0 as well as every conceivable encryption algorithm. Again this is a problem with IE only. Any advice, suggestions or insight would be very helpful!

Thanks

Edit: Some additional information from the access.log. This is what we see in the log when the problem occurs:

207.152.167.191 - - [10/Feb/2012:11:00:27 -0500] "-" 400 0 "-" "-"

207.152.167.191 - - [10/Feb/2012:11:00:27 -0500] "-" 400 0 "-" "-"

and nothing in the error.log

14 Replies

Our ngnix.conf

user www-data www-data;

worker_processes 4;

pid /var/run/nginx.pid;

events {

worker_connections 256;

multi_accept on;

}

http {

#

Basic Settings

#

sendfile on;

tcp_nopush on;

tcp_nodelay on;

keepalive_timeout 75 20;

typeshashmax_size 2048;

clientbodybuffer_size 1k;

clientheaderbuffer_size 1k;

clientmaxbody_size 10m;

largeclientheader_buffers 3 3k;

connectionpoolsize 256;

requestpoolsize 4k;

clientbodytimeout 60;

clientheadertimeout 60;

send_timeout 60;

server_tokens off;

servernameshashbucketsize 128;

servernamein_redirect off;

include /etc/nginx/mime.types;

default_type application/octet-stream;

#

Logging Settings

#

access_log /var/log/nginx/access.log;

error_log /var/log/nginx/error.log;

Log Format

logformat main '$remoteaddr $host $remoteuser [$timelocal] "$request" $status $bodybytessent "$httpreferer" "$httpuseragent" $sslcipher $request_time';

Create zone request limit

limitreqzone $binaryremoteaddr zone=iapp:10m rate=60r/m;

#

Gzip Settings

#

gzip on;

gzip_disable "msie6";

gzip_vary on;

gzip_proxied any;

gzipcomplevel 1;

gzip_buffers 16 8k;

gziphttpversion 1.1;

gzip_disable "MSIE [1-6].";

gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

Global SSL options

ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES128-SHA;

ssl_engine aesni;

sslpreferserver_ciphers on;

ssl_protocols SSLv2 SSLv3 TLSv1;

sslsessioncache off;

sslsessiontimeout 5m;

sslsessioncache shared:SSL:10m;

sslsessiontimeout 10m;

#

If HTTPS, then set a variable so it can be passed along.

#

map $scheme $server_https {

default off;

https on;

}

#

Virtual Host Configs

#

include /etc/nginx/conf.d/*.conf;

include /etc/nginx/sites-enabled/*;

}

Not being familiar with nginx, I can't help much there, but here is what I get in Chrome (16) for http://www.privacyassociation.org/

This webpage has a redirect loop
The webpage at http://www.privacyassociation.org/ has resulted in too many redirects. Clearing your cookies for this site or allowing third-party cookies may fix the problem. If not, it is possibly a server configuration issue and not a problem with your computer.

Here are some suggestions:
Reload this webpage later.
Learn more about this problem.

Error 310 (net::ERR_TOO_MANY_REDIRECTS): There were too many redirects.

The next request gets redirected to https://www.privacyassociation.org/ , and the next request will go to http://www.privacyassociation.org/… I'm seeing a pattern here…

Thanks for the head's up. You caught us in the middle of trying to get users to automatically redirect to http as a short-term solution. If you try it again are you still getting the redirect loop?

Still redirecting here. Do you have any caching going on?

Caching is off. I can see it in Chrome too now. Arrgh! It works in Firefox. Now I'm really confused.

Yup, I see the same here. I'll see what I can see in the config, but as I said I'm not familiar with nginx. The config seems pretty straightforward though…

If it helps here is the sites conf:

server {

server_name ..org;

access_log /var/log/nginx/www..org-access.log main buffer=32k;

error_log /var/log/nginx/www..org-error.log error;

expires max;

limit_req zone=iapp burst=200 nodelay;

listen 443 ssl;

ssl on;

sslsessioncache shared:SSL:10m;

ssl_certificate /path;

sslcertificatekey /path;

root /var/empty;

rewrite ^ http://www. permanent;

}

server {

listen 80;

listen 443 ssl;

limit_req zone=iapp burst=200 nodelay;

server_name ..org;

root /var/path;

errorpage 401 /errorpage.php?c=401;

errorpage 403 /errorpage.php?c=403;

errorpage 404 /errorpage.php?c=404;

errorpage 500 /errorpage.php?c=500;

errorpage 502 503 504 /errorpage.php?c=50x;

add_header Cache-Control "public";

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

ssl on;

sslsessioncache shared:SSL:10m;

ssl_certificate /etc/nginx/ssl/path;

sslcertificatekey /etc/nginx/ssl/path;

access_log /var/log/nginx/www..org-access.log main buffer=32k;

error_log /var/log/nginx/www..org-error.log error;

Redirects non-www to www

if ($host = '.org') {

rewrite ^/(.*) http://www. permanent;

}

location / {

index index.php;

Only server pages to the these domain requests.

if ($host !~ ^(.org|www..org|live..org)$ ) {

return 444;

}

Only honor http GET HEAD and POST. This will ignore all others.

if ($request_method !~ ^(GET|HEAD|POST)$ ) {

return 444;

}

So long as all the previous tests have passed, load the rewritten index.php page.

try_files $uri $uri/ @ee;

}

create the reference rewrite and the rewrite rule.

location @ee {

rewrite ^(.*) /index.php?/$1 last;

}

Deny access to hidden files (start with .)

location ~ /. {

access_log off;

lognotfound off;

deny all;

}

location /search/crawl {

rewrite ^(.*) /index.php?/$1 last;

allow 127.0.0.1;

deny all;

}

location //crawl {

rewrite ^(.*) /index.php?/$1 last;

allow 127.0.0.1;

deny all;

}

Don't log access to thes files

location = /favicon.ico {

lognotfound off;

access_log off;

}

location = /robots.txt {

allow all;

lognotfound off;

access_log off;

}

Only allow our network to these types of files.

location ~* .(rb|log)$ {

deny all;

}

location ~ .(jpe?g|png|gif|mp3|pdf|flv)$ {

valid_referers none blocked .org *..org;

if ($invalid_referer) {

return 403;

}

}

location =/error_page.php {

fastcgi_pass unix:/tmp/phpfpm.sock;

fastcgiparam SCRIPTFILENAME $documentroot$fastcgiscript_name;

include fastcgi_params;

internal;

}

Web conference alias and flash video settings

location ^~ /media {

Block specific agents, mostly bots that don't honor our robots.txt file.

if ($httpuseragent ~ "Wget/|Teleport Pro|WebCopier|fetch|Download|Alligator|FileHound|free-downloads.net|Charon/|BackStreet|EyeCatcher|FDM|FreshDownloads"){

return 403;

}

root /var/www/;

flv;

}

Setup the expires for caching static files. max=Dec 31, 2037, 1y=One Year, 31d=31 Days, 24h=24 Hours

location ~* ^.+.(js|css|png|jpg|jpeg|gif|ico)$ {

access_log off;

expires off;

}

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

Some basic cache-control for static files to be sent to the browser

expires off;

add_header Pragma public;

add_header Cache-Control "public, must-revalidate, proxy-revalidate";

}

This is the index.php specific settings

location /index.php {

include fastcgi_params;

set $script $uri;

set $path_info $uri;

this will set the path_info when it exists as query string: /index.php?/something/here

if ($args ~* "^(/.+)$") {

set $path_info $1;

}

fastcgiintercepterrors on;

fastcgi_pass unix:/tmp/phpfpm.sock;

fastcgi_index index.php;

fastcgiparam SCRIPTFILENAME $documentroot$fastcgiscript_name;

fastcgiparam PATHINFO $path_info;

}

These will process all other .php pages

location ~ .php$ {

if (!-f $documentroot$fastcgiscript_name){

return 404;

}

fastcgipassrequest_body off;

clientbodyinfileonly clean;

fastcgiparam REQUESTBODYFILE $requestbody_file;

fastcgisplitpath_info ^(.+.php)(/.+)$;

fastcgiintercepterrors on;

fastcgiparam SCRIPTFILENAME $documentroot$fastcgiscript_name;

fastcgi_pass unix:/tmp/phpfpm.sock;

fastcgi_index index.php;

include fastcgi_params;

}

}

What versions of IE have you tested with? And what version of OpenSSL are you running? OS? Any other data may help…

I know there were some issues with some older OpenSSL libraries that refused the ciphers from IE, but that that was back for IE6-7. I don't know when that was fixed, or if was just IE's move to SSLv3+. The problem sounds a little like that one, or another nginx issue when built with threaded perl on red hat/centos.

@AgentOfPork:

What versions of IE have you tested with? And what version of OpenSSL are you running? OS? Any other data may help…

I know there were some issues with some older OpenSSL libraries that refused the ciphers from IE, but that that was back for IE6-7. I don't know when that was fixed, or if was just IE's move to SSLv3+. The problem sounds a little like that one, or another nginx issue when built with threaded perl on red hat/centos.

We are running Ubuntu 11.10 with OpenSSL 1.0.0e

The problem is with IE 8 on one instance but it could happen in others.

Thanks!

Edit: And you can see in the config that we are allowing ssl 2,3 and tls 1 with a large number of ciphers.

Well, I would be looking at either a cipher problem (OpenSSL drops the connection if it can't negotiate a common cipher suite with the client, or a stong enough cipher), or a rewrite rule. Rewrites and redirect options/scripts can be a pain, as it's possible to get different behaviors with different browsers.

It's getting a little deeper than I can swim, I'm hoping with the info someone else can jump in? Sorry I can't be more help atm. :(

I'm at work and must leave for a support call, but I'll check back when I can. I'm interested in a resolution!

@AgentOfPork:

It's getting a little deeper than I can swim, I'm hoping with the info someone else can jump in? Sorry I can't be more help atm. :(

I appreciate the help! The rewrite rule is pretty simple and is in the sites config above. But I do suspect that to be at the heart of the issue, I just don't see anything wrong.

After much teeth gnashing and investigating a packet capture uncovered the culprit. The short answer is that our configuration had specified which ciphers to accept.

ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES128-SHA;

That was our mistake. When we commented that out the problem went away. For those that may come across this at a future date the Wireshark packet capture showed the follow attempt to negotiate with the above config line enabled:

4    0.028693    10.0.2.15   50.116.48.191   TLSv1   131 Client Hello

5    0.029430    50.116.48.191   10.0.2.15   TCP 60  https > csdm [ACK] Seq=1 Ack=78 Win=65535 Len=0

6    0.080633    50.116.48.191   10.0.2.15   TLSv1   61  Alert (Level: Fatal, Description: Handshake Failure)

And here is the same part of the capture with the line commented out:

6    0.067025    10.0.2.15   50.116.48.191   TLSv1   163 Client Hello

7    0.067793    50.116.48.191   10.0.2.15   TCP 60  https > saiseh [ACK] Seq=1 Ack=110 Win=65535 Len=0

8    0.079714    50.116.48.191   10.0.2.15   TLSv1   191 Server Hello, Change Cipher Spec, Encrypted Handshake Message

I am unclear why there was a different in negotiation without ciphers specified but it definitely fixed the issue. A little additional information for investigation purposes:

  • https > csdm was negotiating on port 1472.

  • The details of the alert failure on line 6 of the failed capture error out with "Content Type: Alert (21)" which is a failed to decrypt error.

  • https > saiseh was negotiating on port 1644.

I hope this helps someone in the future and if anyone can add more information please do!

That cipher list is awfully long. I wouldn't be surprised if something in there was incompatible with some of the low-quality browsers out there. I just do the following, and it works fine without compromising security. (You can remove the "!kEDH" at the end if you want maximum security at the expense of performance. And please do disable SSLv2, it's insecure and completely unnecessary.)

ssl_ciphers HIGH:!ADH:!MD5:!aNULL:!eNULL:!MEDIUM:!LOW:!EXP:!kEDH;
ssl_protocols SSLv3 TLSv1;
ssl_prefer_server_ciphers on;

It's also a bad idea to lump all virtual hosts into a giant server { } block and use "if" blocks to handle redirects. The nginx official guide strongly recommends that you use one separate virtual hosts for this. Have an SSL virtual host that handles the www domain at port 443. This is your main site. Also have a none-SSL virtual host that handles the www domain at port 80. You can make this redirect to the SSL site if you want. Finally, have a non-SSL virtual host that handles the non-www domain. Everything in this virtual host should redirect to the www domain. That'll make a very short server { } block, but it's many times more manageable than an "if" block inside another virtual host. This would have prevented your redirection woes.

We went through and cleaned up the config and took your recommendations. Thanks for that info.

Reply

Please enter an answer
Tips:

You can mention users to notify them: @username

You can use Markdown to format your question. For more examples see the Markdown Cheatsheet.

> I’m a blockquote.

I’m a blockquote.

[I'm a link] (https://www.google.com)

I'm a link

**I am bold** I am bold

*I am italicized* I am italicized

Community Code of Conduct