Use cURL with SNI (Server Name Indication)
To be able to use SNI, three conditions are required:
- Using a version of Curl that supports it, at least 7.18.1, according to the change logs.
- Using a version of Curl compiled against a library that supports SNI, e.g. OpenSSL 0.9.8j (depending on the compilation options some older versions).
- Using TLS 1.0 at least (not SSLv3).
Note that Curl's debug code (-v
) only displays the major version number (mainly to distinguish between SSLv2 and SSLv3+ types of messages, see ssl_tls_trace
), so it will still display "SSLv3" when you use TLS 1.0 or above (because they're effectively SSL v3.1 or above, 3 is the same major version number).
You could check that your installed version of curl can use SNI using Wireshark. If you make a connection using curl -1 https://something
, if you expand the "Client Hello" message, you should be able to see a "server_name
" extension.
I'm not sure which SSL/TLS version is used by default (depending on your compilation options) when you use curl
without -1
(for TLS 1.0) or -3
(for SSLv3), but you can try to force -1
on your command, since it won't work with SSLv3 anyway.
fake Server Name indication (SNI) in libcurl with OpenSSL backend
If you want to "fake" the SNI then CURLOPT_RESOLVE
or CURLOPT_CONNECT_TO
are available options to reach the same end goal.
CURLOPT_RESOLVE example
Run a HTTPS server on 127.0.0.1 but make curl think it is example.com
when it connects to it (so it sends that as SNI and in the Host:
header)
CURL *curl;
struct curl_slist *host = NULL;
host = curl_slist_append(NULL, "example.com:443:127.0.0.1");
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_slist_free_all(host);
CURLOPT_CONNECT_TO example
Run a dev HTTPS server on the host name server1.example.com
but you want curl to connect to it thinking it is the www.example.org
server.
CURL *curl;
struct curl_slist *connect_to = NULL;
connect_to = curl_slist_append(NULL, "www.example.org::server1.example.com:");
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.org");
curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_slist_free_all(connect_to);
curl: how to specify target hostname for https request
Indeed SNI in TLS does not work like that. SNI, as everything related to TLS, happens before any kind of HTTP traffic, hence the Host
header is not taken into account at that step (but will be useful later on for the webserver to know which host you are connecting too).
So to enable SNI you need a specific switch in your HTTP client to tell it to send the appropriate TLS extension during the handshake with the hostname value you need.
In case of curl
, you need at least version 7.18.1 (based on https://curl.haxx.se/changes.html) and then it seems to automatically use the value provided in the Host
header. It alo depends on which OpenSSL (or equivalent library on your platform) version it is linked to.
See point 1.10 of https://curl.haxx.se/docs/knownbugs.html that speaks about a bug but explains what happens:
When given a URL with a trailing dot for the host name part: "https://example.com./", libcurl will strip off the dot and use the name without a dot internally and send it dot-less in HTTP Host: headers and in the TLS SNI field.
The --connect-to
option could also be useful in your case. Or --resolve
as a substitute to /etc/hosts
, see https://curl.haxx.se/mail/archive-2015-01/0042.html for am example, or https://makandracards.com/makandra/1613-make-an-http-request-to-a-machine-but-fake-the-hostname
You can add --verbose
in all cases to see in more details what is happening. See this example: https://www.claudiokuenzler.com/blog/693/curious-case-of-curl-ssl-tls-sni-http-host-header ; you will also see there how to test directly with openssl
.
If you have a.example
in your /etc/hosts
you should just run curl with https://a.example/
and it should take care of the Host
header and hence SNI (or use --resolve
instead)
So to answer your question directly, replace
curl --header 'Host: a.example' https://x.example
with
curl --connect-to a.example:443:x.example:443 https://a.example
and it should work perfectly.
RESTful, web-facing service with SNI (Server Name Indication)
If your Spring application is behind an apache web server, acting as a gateway (a.k.a. reverse proxy), you can use the following apache configuration to restrict the access:
<Location "/url/of/the/webservice">
Require ip 111.222.333.444
</Location>
From the documentation :
The
<Location>
directive limits the scope of the enclosed directives by URL.This
Require
directive tests whether an authenticated user is authorized according to a particular authorization provider and the specified restrictions.
Getting Server/ Server name indication in Python
SNI
You should be able to get there with Python 3.x series, 3.2+ if I understand correctly.
If you're stuck with Python 2, look at what python-requests does with two optional dependencies, ndg-httpsclient
and pyasn1
.
My understanding is that you can't really do much with plain Python <= 2.7.8 due to devs' perplexing view that SNI is somehow a feature of Python
EDIT
Thanks to PEP466, Python 2.7.9 contains ssl module backported from Python 3.4 and you have all the OpenSSL features.
Other projects
Perhaps sslyze (github) can do what you want already?
Related Topics
Getting Data Posted in Between Two Dates
PHP Adds Keys to Decoded and Then Encoded JSON Data
How to Trigger Xdebug Profiler for a Command Line PHP Script
How Add Custom Validation Rules When Using Form Request Validation in Laravel 5
Get the Site Status - Up or Down
Install Yii2 Extension Manually Without Using Composer
Phpmailer Sending Mail to Spam in Hotmail. How to Fix
Changing the Add to Cart Button Text in Woocommerce for Items with Variations
Preparing PHP Application to Use with Utf-8
Asynchronous Processing or Message Queues in PHP (Cakephp)
Problem with Adding Root Path Using PHP Domdocument
What Is Unexpected T_Variable in PHP
Phpunit Best Practices to Organize Tests
Tracking Email with PHP and Image