Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Net::HTTP adapter trusts all system root CAs when a ca_file is specified #14

Open
aetherknight opened this issue Apr 9, 2014 · 5 comments

Comments

@aetherknight
Copy link

Problem

Faraday always trusts the OpenSSL system root CAs, even when a :ca_file or a :ca_path are specified, eg to implement CA pinning, or to reduce the number of trusted certificates.

Example

Faraday.new('https://www.google.com', ssl: { ca_file: '/not/used/by/google/ca.pem' }).get('/') # => #<Faraday::Response:0x007ffd580b19d8 ...

Expected behavior:

An error about server certificate certificate validation, because the website's certificate does not match the :ca_file

Root Cause:

Within the net_http adapter, ssl_cert_store will create a certificate store that includes the OpenSSL system root CAs if :cert_store is not specified:

https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/net_http.rb#L105

      def ssl_cert_store(ssl)
        return ssl[:cert_store] if ssl[:cert_store]
        # Use the default cert store by default, i.e. system ca certs
        cert_store = OpenSSL::X509::Store.new
        cert_store.set_default_paths
        cert_store
      end

I would think that Faraday should only set a default :cert_store if there is no :ca_file, no :ca_path, and no :cert_store specified.

@mislav
Copy link

mislav commented Apr 9, 2014

Thanks for the nice report. I get the problem; however, I have the feeling that people right now are using ca_file to provide an extra custom certificate on top of system CA certs. Flipping the switch on this behavior would be backwards-incompatible.

How about that you can choose to disable the default cert store if you deliberately want to do CA pinning? E.g.

Faraday.new('...', ssl: { ca_file: 'ca.pem', cert_store: false })

Would that satisfy your needs?

We would need to investigate how current HTTP libs (including net/http) behave in this regard: are we able to turn off the default system certs by passing no cert store object? If you have time and will to test this, it would be great.

@aetherknight
Copy link
Author

Hey @mislav thanks for the response.

One immediate remediation I am currently using is to set the :cert_store to an empty certificate store:

cert_store = OpenSSL::X509::Store.new
Faraday.new('...', ssl: { ca_file: 'ca.pem', cert_store: cert_store })

Or to just use a cert_store:

cert_store = OpenSSL::X509::Store.new
cert_store.add_file('ca.pem')
Faraday.new('...', ssl: { cert_store: cert_store })

However, the only adapters that currently support :cert_store are:

  • httpclient
  • net_http
  • net_http_persistant

The httpclient adapter does not add a default certificate store, and the net_http_persistant adapter does the same thing as net_http (although it currently supports fewer SSL config options).

I'll take a little time to see how the other HTTP libs behave when just a :ca_file is specified, eg to see if they still trust the CA root. (The curl command, when built with openssl support, disables the system root when I specify a CA file, but I haven't tested the other Ruby HTTP libs yet aside from net/http).

@aleksandrs-ledovskis
Copy link

I'll take a little time to see how the other HTTP libs behave when just a :ca_file is specified, eg to see if they still trust the CA root. (The curl command, when built with openssl support, disables the system root when I specify a CA file, but I haven't tested the other Ruby HTTP libs yet aside from net/http).

Have tested situation on CRuby 2.7 & 3.0 (both with OpenSSL's 1.1.1) with Net::HTTP.

http = Net::HTTP.new('example.com', 443) # Certificate https://crt.sh/?id=3704614715
http.use_ssl = true
http.start
  1. Passing concatenation of CA and Root (https://crt.sh/?id=853428 + https://crt.sh/?id=3427370830) certs works

    http.ca_file = '/tmp/example_com_chained.pem' 
  2. Passing just a root CA (https://crt.sh/?id=853428) cert works

    http.ca_file = '/tmp/example_com_root.pem' 
  3. Passing just a single CA (https://crt.sh/?id=3427370830) cert doesn't work (SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get issuer certificate) (OpenSSL::SSL::SSLError))

    http.ca_file = '/tmp/example_com_ca.pem' 
  4. Passing concatenation of CA and Root (https://crt.sh/?id=853428 + https://crt.sh/?id=3427370830) certs doesn't work for a page served with different CA/Root combination (e.g., "google.com") - fails with SSL_connect returned=1 errno=0 state=error: certificate verify failed (unable to get issuer certificate) (OpenSSL::SSL::SSLError)

    http = Net::HTTP.new('google.com', 443)
    http.ca_file = '/tmp/example_com_chained.pem' 

@iMacTia
Copy link
Member

iMacTia commented Nov 15, 2021

Thanks for jumping in on this @aleksandrs-ledovskis, would you please clarify out of the 4 points above which ones are working as expected and which ones are not?

@iMacTia iMacTia transferred this issue from lostisland/faraday Jan 2, 2022
@aleksandrs-ledovskis
Copy link

would you please clarify out of the 4 points above which ones are working as expected and which ones are not?

The examples are from Net::HTTP standard library and as such these are all working as expected. I have just provided an example/reference regading "how others do it".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants