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
try to find a valid certificate in the chain #241
Conversation
75a843b
to
736d8d9
Compare
|
afaik this disables verification of the chain and all you need is a certificate with a matching hostname in order for it to verify. it doesn't look it requires the cert with the valid hostname to chain back to a root cert. |
|
@benmmurphy hrm what if it just ignore the self signed certificate and continue then? Part of the queston is if there is any valid doc that could explain how to reorder a chain like the browsers or curl do :/ |
|
Using You can see that the chain is out of order in the response, but that if you connect the issuers ( You can see that As far as how you should actually verify each step in the chain, I guess you'd need to look at the OTP SSL code, or in openssl, or maybe the PKI stuff in OTP will do it properly once the chain's in the correct order. And if it will, then probably a PR fixing it in OTP would be useful. |
Except that I see that Ingela has already said on the mailing list that she'd prefer not to do that. Maybe a PR making it configurable would be useful...? |
|
my code is correct assuming you are able to substitute all the CA certs for the single CA cert is use. the correct solution is to find a path from the first certificate the in the chain to CA root. you can do this by a DFS. the go code by adam langley is the best example of this. https://github.com/golang/go/blob/master/src/crypto/x509/verify.go |
|
How about a project that bundles a set of custom verify_fun/2 implementations, with different capabilities? Any :ssl client, direct or indirect (e.g. through Hackney), can then pass a parameter to select the most appropriate one. Hackney could choose a preferred default, but users could override that. This could be an evolution of the ssl_verify_hostname package. Examples include: reordering cert chain (yes/no), CRL/OCSP checking (yes/no), support for certain X.509 extensions, etc. My concern is that if different libraries start implementing their own verify_fun to replace OTP's, it will be difficult for users to verify the security of each one. A centralised project would hopefully attract experts who can review and fix any security issues. |
|
@voltone sounds like a good idea. It would help a lot of people and make sure to use the right code among projects... |
|
@benmmurphy I was looking at it. I am not sure yet though how the hostname checking is working. It seems to me that such thing should happen after the chain have been reordered.. I am thinking it should work that way:
The part I am not sure to follow in the code the pasted is the way you handle the self signed certificate. Can you elaborate on it? |
CA certificates are self-signed certificates. That is: the root of the chain is always self-signed. The only thing that makes them "CA" certificates is that they're in a trusted list[1]. If you find a self-signed certificate anywhere else in the chain, then it's not a chain, because subject == issuer and you've broken the chain. [1] There's also an is-certificate-authority flag in the cert, but that doesn't make them trusted roots. |
Sometimes servers send only 'first' certificate without intermediates bundle, to actually verify each step in the chain client should implement AIA which is ok for Hackney, not sure what to do with non-http clients. btw. this is why most problem domains work in browsers - they can download intermediate certs themselves. |
|
@benoitc in the code i posted we don't verify the hostname because this is used internally with a custom CA and we trust all the certs generated by this CA. [which may actually be a mistake now i think about it :)] but you can add hostname verification by passing a different function other than default_verify into the public_key:pkix_path_validation. basically pkix_path_validation is being called twice. once by ssl where we hook the validation function to get access to the full chain and then again from our validation function to validate the chain we generate. so there are two major improvements that need to be made:
and yeah. when you start to see the code for 1) you will understand why this needs to be done in OTP or for OTP to provide proper hooks for it. |
|
@benmmurphy thanks for the code. I am trying right now to have a play but using a list of trusted root instead of one rootcertifcate. No success yet though :) @deadtrickster do you have any link about that that could helps me to figure what to do exactly. Basically Hackney should act like a browser. Any patch that would help to implement it would be definitely accepted :) |
|
@rlipscombe right right, was more thinking to self signed certificate that are not trusted |
I'd prefer this to be optional. That is: I'd like to be able to decide on a certificate verification function per connection (because I might want to use different trusted roots, different self-signed certificates, etc.); if we could invent a suite of different verification strategies, as @voltone suggests, then this could be pluggable. |
|
@rlipscombe i am speaking about the defaults. You can still override a connection with your own ssl options like usual :) |
|
@deadtrickster also I am agree we need an hih level framework that would allow to validate an SSL connection without having to know much about the openssl API. |
|
@benoitc |
|
@deadtrickster i mean the erlang SSL/public_key api :) |
|
Also very interesting reading Speeking of openssl, what to do with older OTP releases? they are actually based on openssl aren't they? Honestly I feel like this (stuff we are talking about here) has to be part of OTP itself actually |
|
@deadtrickster what would be the point of having it in OTP? Is this easier to handle at that level? |
|
OTP already has ssl, public_key and friends, I also know they rewrote everything from scratch to move away from openssl. That cache stuff leaves open questions however, about you know per application (library) cache, etc... |
|
this code extracts AIA extension and prints uri entries from google.com certificate. URL presented in aia extension points to intermediate GIAG2 certificate Certificate chain for google.com extensions_list(E) ->
case E of
asn1_NOVALUE -> [];
_ -> E
end.
select_extension(Id, Extensions) ->
Matching = [Extension || #'Extension'{extnID = ExtId} = Extension <- Extensions, ExtId =:= Id],
case Matching of
[] -> undefined;
[H|_] -> H
end.
select_ca_issuers(ADs) ->
Matching = [AccessDescription#'AccessDescription'.accessLocation || #'AccessDescription'{accessMethod = ADId} = AccessDescription <- ADs, ADId =:= ?'id-ad-caIssuers'],
case Matching of
[] ->
undefined;
[AD|_] -> AD
end.
test_aia() ->
Cert = google_cert(),
TBSCert = Cert#'OTPCertificate'.tbsCertificate,
Extensions = extensions_list(TBSCert#'OTPTBSCertificate'.extensions),
AIAExt = select_extension(?'id-pe-authorityInfoAccess', Extensions),
case AIAExt of
undefined ->
[];
_ ->
select_ca_issuers(AIAExt#'Extension'.extnValue)
end.Output: ssl_verify_hostname_tests:test_aia().
{uniformResourceIdentifier,"http://pki.google.com/GIAG2.crt"}the code is dead simple - more complex part is actually downloading certificates (also that LDAP...) |
|
Just a heads up. The code I've posted is broken as well. :( it accepts all self signed certs. yay. So depending on the bad_cert error it will skip the valid_peer or valid step so you can't rely on valid_peer or valid being called. :/ You can see it here where it deletes your verify callback and recursively calls path validation. https://github.com/erlang/otp/blob/maint/lib/public_key/src/public_key.erl#L671 also i think this bug has been a little confused because the problem site was rest-api.pay.nl (which has reordering problems) but the test case at the top is for rest-api.nl (which is self-signed and should not be accepted) |
|
ok here is my attempt to use the ca certs cached by the ssl app. so remaining issues are:
but OTP really needs to just supply a hook point which is. reorder_chain(PeerCert, Chain, CertDBHandle, CertDBRef) and you can just send back another chain that is however you want it to look like. if this was done right it would fix the broken partial chain function that is super dangerous. also this might not work in all cases. i suspect the reordering will only work if the last certificate is either in the CA store or issues by a cert in the CA store because of the protection i've had to add to prevent accepting invalid chains. however, i think this can be fixed by a partial chain handler that always returns a CA (even if it can't find a proper one) then instead of bailing on unknown_ca it will bail on invalid_issuer which is safe to recover from. |
|
@benmmurphy Nice! I think you're right It would be better to have this hook function. Are you working on a patch? Also I wonder if waiting this hook function we couldn't add this verification directly to hackney and mark it as experimental since it rely on non documented function? Maybe you could create a PR with it? What is the effect of not decrementing the refcount? |
|
@deadtrickster That's neat. Sounds "easy" to add... It could eventually be combined with the partial function though not sure yet how to do it. Btw the |
|
I'm not working on an OTP patch. I've thought more deeply about this and I don't think generic reordering can be done without OTP support or taking over verification of SSL certs completely. so before i was hoping we would only hit the reorder code in a fallback but i think it will be necessary to always run the reorder code. This is because:
|
|
I played with incomplete chains more last weekend and here is what I found:
google_cert can be found in ssl_verify_hostname tests it simply loads google.com cert from the file |
|
that's a long time. closing it. feel free to reopen it if needed. |
should fix #240 . I am not sure yet how secure it is. At least both sites tested pass while invalid don't.