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

Arisu Desu arisudesu at yandex.ru
Sun Mar 31 12:46:17 UTC 2024


Hello everyone!
My previous attempt at posting to mailing list failed miserably, so 
forgive me
for sending this twice, please.
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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx/attachments/20240331/9d43bc41/attachment.htm>


More information about the nginx mailing list