elixir-mint / mint

Functional HTTP client for Elixir with support for HTTP/1 and HTTP/2 🌱
Apache License 2.0
1.36k stars 111 forks source link

Ciphers are blacklisted that are valid for HTTP/1 #113

Closed lucaspiller closed 5 years ago

lucaspiller commented 5 years ago

The 'safe by default' change #46 introduced restrictions on what cipher suites are used for SSL connections. The HTTP/2 spec specifically blacklists certain ciphers, however some of those are still valid for use with HTTP/1 connections.

It seems a lot of Outlook Web Access ISS servers are configured with 'TLS_RSA_WITH_AES_128_CBC_SHA' which is on the blacklist (as it doesn't support forward secrecy), so trying to connect results in this:

iex(8)> Mint.HTTP.connect(:https, "owa.marriott.com", 443, [protocols: [:http1], transport_opts: [verify: :verify_none]])
{:error, :closed}

Although visiting this site in Chrome says it is secure, Qualys SSL Labs does say the cipher is weak, so I believe the way this is implemented now is the right choice.

But for people like me who just want to make HTTP/1 connections to weaker servers this is a problem. I can pass in a list of ciphers, and update them as new ciphers are supported, but maybe there should be an option to use weaker ciphers in Mint?

ericmj commented 5 years ago

I cannot access owa.marriott.com, do you know of an outlook server that is publicly accessible?

You can override the default ciphers, see: https://hexdocs.pm/mint/Mint.HTTP.html#connect/4-transport-options

Maybe we should provide another list of defaults for HTTP/1. Not sure which ones to pick though. /cc @voltone

voltone commented 5 years ago

You can test this against https://static-rsa.badssl.com/.

The issue here is RSA key exchange. Not only does RSA Kx lack perfect forward secrecy, it was also found to have a weakness that could allow an attacker to decrypt TLS sessions.

Because of this, support for RSA Kx has been removed in a number of protocols and systems: TLSv1.3 no longer supports it (it's not just disabled, it cannot be used with this protocol version), HTTP/2 specifically disallows it, and Erlang/OTP disables it by default since release 21.0. So in fact, most Elixir/Erlang HTTP clients on OTP 21 will have trouble connecting to sites that do not support (EC)DHE key exchange.

One way to enable RSA Kx (on OTP 20.3 or later) would be:

ciphers =
  :ssl.cipher_suites(:all, :"tlsv1.2")
  # Select RSA Kx, but not in combination with weak ciphers:
  |> :ssl.filter_cipher_suites(key_exchange: &(&1 == :rsa), cipher: &(&1 not in [:rc4_128, :des_cbc, :"3des_ede_cbc"]))
  # Append (least preferred) to the defaults:
  |> :ssl.append_cipher_suites(:ssl.cipher_suites(:default, :"tlsv1.2"))
Mint.HTTP.connect(:https, "static-rsa.badssl.com", 443, transport_opts: [ciphers: ciphers])
lucaspiller commented 5 years ago

@ericmj owa.marriott.com was just an example I found from Google, as I don't want to post my client's URL here. It seems their service isn't that reliable though which is probably why you couldn't connect. email.mdc.edu is another example.

@voltone I don't think that is the same issue as what I am seeing, as I can just pass in all the supported cipher suites and it will connect (Erlang/OTP 21.1 on Linux):

iex(8)> Mint.HTTP.connect(:https, "email.mdc.edu", 443)                                                     
{:error, :closed}

iex(9)> Mint.HTTP.connect(:https, "email.mdc.edu", 443, [transport_opts: [ciphers: :ssl.cipher_suites()]])
{:ok,
 %Mint.HTTP1{
   buffer: "",
   host: "email.mdc.edu",
   private: %{},
   request: nil,
   requests: {[], []},
   socket: {:sslsocket, {:gen_tcp, #Port<0.14>, :tls_connection, :undefined},
    [#PID<0.230.0>, #PID<0.229.0>]},
   state: :open,
   transport: Mint.Core.Transport.SSL
 }}

If I do the same with your example is returns an error:

iex(1)> Mint.HTTP.connect(:https, "static-rsa.badssl.com", 443, transport_opts: [ciphers: :ssl.cipher_suites()])

09:28:46.155 [info]  ['TLS', 32, 'client', 58, 32, 73, 110, 32, 115, 116, 97, 116, 101, 32, 'hello', 32, 'received SERVER ALERT: Fatal - Handshake Failure', 10]
{:error, {:tls_alert, 'handshake failure'}}

Plus HTTPoison/Hackney will connect out of the box:

iex(1)> HTTPoison.head("https://email.mdc.edu/owa")
{:ok,
 %HTTPoison.Response{
   body: "",
   headers: [
     {"Content-Length", "43"},
     {"Content-Type", "text/html; charset=utf-8"},
     {"Server", "Microsoft-IIS/8.5"},
     {"request-id", "767f0c79-a047-47aa-8b4a-5c979b508c08"},
     {"Set-Cookie",
      "ClientId=DQPENAKEWUGTCEHYVWCG; expires=Sat, 07-Mar-2020 08:23:28 GMT; path=/; HttpOnly"},
     {"X-Powered-By", "ASP.NET"},
     {"strict-transport-security", "max-age=31536000; includeSubdomains"},
     {"X-FEServer", "EXCHANGE04"},
     {"Date", "Fri, 08 Mar 2019 08:23:27 GMT"},
     {"Connection", "close"}
   ],
   request: %HTTPoison.Request{
     body: "",
     headers: [],
     method: :head,
     options: [],
     params: %{},
     url: "https://email.mdc.edu/owa"
   },
   request_url: "https://email.mdc.edu/owa",
   status_code: 440
 }}
voltone commented 5 years ago

Right, so this server does support ECDHE, but does not appear to support AES-GCM, so it requires an AES-CBC cipher to be enabled in the client. The HTTP/2 blacklist filters out any non-AEAD ciphers, so CBC is not enabled by default.

Right now the preferred way to select the OTP default cipher list would be transport_opts: [ciphers: :ssl.cipher_suites(:default, :"tlsv1.2")] (because cipher_suites/0 is deprecated).

@ericmj what do you say: should there be a flag for this, similar to the cipher_suites option in Plug (https://hexdocs.pm/plug/https.html#predefined-options)?

whatyouhide commented 5 years ago

I think with good documentation, that's the way to go @voltone. Thanks a lot for the help. Do you want to take care of this with a PR, or should we do it?

voltone commented 5 years ago

I will propose some additional docs