Welcome! Log In Create A New Profile

Advanced

How to deny serving requests if Host field and SNI do not match?

March 17, 2024 02:52AM
Hello everyone!

I'm trying to understand the behavior of nginx when it is configured to serve
multiple domains with different certificates.

I have a single server with single IP address, and several domains, say,
domain-a.com, domain-b.com. nginx is 1.24.0, configured as follows:

server {
listen 443 ssl http2 default_server;

ssl_reject_handshake on;

return 444;
}

server {
listen 443 ssl http2;
server_name domain-a.com sub.domain-a.com;

ssl_certificate /etc/certs/domain-a.com.crt; <- this is wildcard cert
ssl_certificate_key /etc/certs/domain-a.com.key; <- for domain-a.com, *.domain-a.com

return 200 "serving domain-a.com";
}

server {
listen 443 ssl http2;
server_name domain-b.com sub.domain-b.com;

ssl_certificate /etc/certs/domain-b.com.crt; <- this is wildcard cert
ssl_certificate_key /etc/certs/domain-b.com.key; <- for domain-b.com, *.domain-b.com

return 200 "serving domain-b.com";
}

Here's what happens when I send some requests with curl:

$ curl https://domain-a.com
$ curl https://sub.domain-a.com
$ curl https://domain-b.com
$ curl https://sub.domain-b.com
: these returns expected responses for matching domain names.

$ curl https://domain-a.com -H 'Host: domain-b.com'
$ curl https://domain-b.com -H 'Host: domain-a.com'

Here is the interesting part. nginx negotiates SSL connection for SNI
domain-a.com but the response is from domain-b's virtual server! And vice versa.

$ curl https://domain-a.com -H 'Host: not_matching_any_server_name'

This too negotiates SSL for SNI domain-a.com, but doesn't send any response.
Because it lands in the default_server block, I suppose.

The question is, when reading request Host field, does nginx validate it against
negotiated SNI or not? Is there any setting I'm missing to deny serving
requests if Host and SNI mismatch? Or is it just impossible to implement such
check due to protocols requirements?

I think that choosing virtual server solely based on Host field and ignoring SNI
may pose a risk from a security research perspective. E.g. if client negotiates
SSL for domain-a.com, I certainly do not want my server to even hint that it can
serve domain-b.com, much less send valid responses for that domain.

The best I could come up with was to insert into each server this:

if ($ssl_server_name != $host) {
return 421;
}

but I'm not sure whether this breaks any clients.

I looked through older threads and there were ones asking the same thing
(https://forum.nginx.org/read.php?2,281564,281564), though not sure whether
answers are up to date.
Subject Author Posted

How to deny serving requests if Host field and SNI do not match?

arisudesu March 17, 2024 02:52AM

Re: How to deny serving requests if Host field and SNI do not match?

arisudesu March 19, 2024 11:33AM

Re: How to deny serving requests if Host field and SNI do not match?

arisudesu March 26, 2024 10:59AM



Sorry, only registered users may post in this forum.

Click here to login

Online Users

Guests: 240
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready