46

I am trying to use cURL to post to an API that just started using SNI (so they could host multiple ssl certs on 1 IP address).

My cURL stopped working as a result of this move to SNI. They explained that it's because cURL is getting *.domain-a.com back instead of *.domain-b.com so the SSL fails.

This seems to be a bug in cURL because the API URL has no errors when visited from a browser.

Using this code, it does work:

exec('curl -k -d "parameters=here", https://urlhere.com/', $output);
print_r($output);

However, using -k is bad because it doesn't verify the SSL cert.

Using this code, does NOT work:

exec('curl -d "parameters=here", https://urlhere.com/', $output);
print_r($output);

So my question is, how can I use curl with SNI and still verify the SSL (not have to use -k). Is there another setting in PHP or a cURL option I can set to work around this?

Justin
  • 26,443
  • 16
  • 111
  • 128

2 Answers2

67

Along with the required library and Curl version, the request should use resolve to send SNI in the curl:

curl -vik --resolve example.com:443:198.18.110.10 https://example.com/
  • 2
    In my case I was accessing the server by IP. The certificate was downloaded by accessing the same server, by IP, with Firefox. If I trusted the certificate in my Mac's Keychain, then everything worked accessing it by IP and without validating the certificate in cURL. But when I removed that certificate from Keychain and tried to validate it in cURL, I kept getting "verification failed" errors until I resolved that IP to the name I had downloaded the original certificate from, as this answer suggests. – riverhorse Apr 14 '20 at 10:45
57

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.

Bruno
  • 119,590
  • 31
  • 270
  • 376
  • If you want to use libcurl, set `CURLOPT_SSLVERSION` to `1` (to be equivalent to `-1` on the command line). – Bruno Oct 17 '12 at 20:26
  • For me, it was having an old version of cURL installed. Updating it fixed it. – Justin Jul 19 '13 at 22:20
  • 8
    Note that curl on Mac OS X (as of Yosemite) does not send SNI when used with -k or --insecure. – ghodss Feb 28 '16 at 10:22