-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Open
Labels
area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends.library-iotype-enhancementA request for a change that isn't a bugA request for a change that isn't a bug
Description
Support for pinning base64 SHA-256 hashes as in HTTP Public Key Pinning (HPKP) or SHA-1 base64 hashes in SecurityContext.
Support for Subject Public Key Info in X509Certificate.
normally in native android and ios apps key pinning done similar like this https://square.github.io/okhttp/3.x/okhttp/okhttp3/CertificatePinner.html
wederchr, rsilvr, sprymak, 3ace, minikin and 54 morekrispypen, VikasJilla1, alexbejann and szugyi
Metadata
Metadata
Assignees
Labels
area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.Use area-vm for VM related issues, including code coverage, and the AOT and JIT backends.library-iotype-enhancementA request for a change that isn't a bugA request for a change that isn't a bug
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
data
getter to X509Certificate #33115rsilvr commentedon Feb 22, 2019
I'm also in need of such a feature. Even certificate pinning I found that is only possible in dart to add a self-signed certificate as trusted (in the badCertificateCallback), but I couldn't find any way of validating myself in every call the certificate of the server I'm connecting to. In a MITM attack the default security context implementation wouldn't be enough.
ivnsch commentedon Apr 20, 2019
@rsilvr Are you saying that certificate pinning doesn't work with arbitrary certificates? Can anyone confirm this? This would make this functionality pretty much useless.
sandeepcmsm commentedon Apr 22, 2019
@i-schuetz factory SecurityContext({bool withTrustedRoots: false}); by setting withTrustedRoots to false will deny all request even if they are root trusted certificates and will fallback to badCertificateCallback. Then you can verify your self signed certificates.
rsilvr commentedon Apr 23, 2019
@sandeepcmsm Thank you a lot for making this clear! I've tried something similar to this some months ago but I have no idea why it wasn't working before...
pythoneer commentedon Jun 3, 2019
@i-schuetz what do you mean by "This would make this functionality pretty much useless." There is no functionality to pin a key at all. At least that i am aware of.
The only thing that comes close is to use
SecurityContext({bool withTrustedRoots: false});
to make every certificate fail and usebadCertificateCallback
to check the public key, but this "disables" the validation of the certificate chain. So if you want to use public key pinning to avoid MITM attacks (by installing fake certificates on the device) i think this is useless. I am not 100% sure but i guess one can crate a certificate with the public key, it is just not valid – but that is not a problem for the attacker because you deactivated validation to check the public key.All you can do currently is "first check the public key" with a webservice call and validation deactivated (a "pilot" call) and after that activate it (with
withTrustedRoots: true
) to make the "original" call. The problem here is, that an attacker knows it needs to present a certificate with the right public key (but not validateable) on the first "pilot" call and after that just a self signed certificate installed on the device (but with a wrong public key) on the second call. And this makes both approaches useless – i think.The only "right" way is to either have a callback for all requests the gives us the Certificate (like in
badCertificateCallback
) but not just for the bad ones and we can check the public key by hand. Or even better, give the SecurityContext a list of public keys we want to allow when we havewithTrustedRoots: true
.Because what we want is to validate AND check the public key on the same webservice call uninterrupted (roughly on the same connection).
mleonhard commentedon Sep 26, 2019
I think you missed a point. Public key cryptography works because the public and private keys are linked by math. If you encrypt with the public key, only someone holding the private key can decrypt. If an attacker makes a new certificate using the same public key, they cannot use it because they don't know the corresponding private key. SSL/TLS connection setup will fail.
So I believe one can implement certificate pinning just by checking the public key of the certificate. That's the only thing that matters for security.
mleonhard commentedon Sep 27, 2019
I believe that certificate pinning is already possible without this feature request:
SecurityContext
that doesn't trust the OS's certificates:SecurityContext(withTrustedRoots: false);
.SecurityContext.setTrustedCertificates(rootCertificateBytes)
to make Dart accept all certificates signed by the root private key.It's possible that
SecurityContext.setTrustedCertificates(selfSignedCertificate)
would also work. I found nothing in the docs about this. If this is true, then we could eliminate some steps above.HttpClient.badCertificateCallback
is an alternative if you're doing only https. See https://stackoverflow.com/a/54838348mleonhard commentedon Sep 28, 2019
Key rotation reduces risk. When an attacker obtains an old server hard drive or backup file and gets an old server private key from it, they cannot impersonate the current server if the key has been rotated. Therefore always generate a new key when updating certificates. Configure the client to trust the old key and the new key. Wait for your users to update to the new version of the client. Then deploy the new key to your servers. Then you can remove the old key from the client.
The sole purpose of this feature request is to facilitate key pinning without rotation, a bad security practice. Please comment and explain if you disagree. Otherwise, let's close this feature request.
Below are several examples certificate pinning, supporting the good security practice of annual key rotation.
Create a self-signed certificate every year and have the clients trust only this year's and last year's certificates:
Create self-signed certificates and have the client trust only those certificates, and ignore the hostname in the request. This is useful when using Terraform to deploy the server to AWS Elastic Beanstalk. The server binary blob must contain the certificates, yet the server's hostname is not known until the deployment completes.
An alternative to pinning the public key is to use a certificate authority. Create the certificate authority files on a secure laptop and keep them on a removable drive in a safe. Whenever you need a new certificate, get the removable drive and generate and sign a new server certificate. Here's an example of the
openssl
commands to run and how to configure Dart to trust only certificates signed by your certificate authority:9 remaining items
wederchr commentedon Aug 25, 2020
Certificate Pinning is possible using setTrustedCertificatesBytes. However, HPKP is not possible as you've already mentioned.
mleonhard commentedon Aug 25, 2020
@yapcwed if you have figured out how to get certificate pinning to work in dartlang, please share details on how you accomplished it. When I tried to get it working, I was thwarted by these bugs:
TMSantos commentedon Aug 26, 2020
@mleonhard only way I found to retrieve the correct certificate was using HttpClientResponse object
Here you can access:
Etc and it has the correct certificate, instead of CA certificate only.
I somehow made this to be compatible with "http" by returning response like this:
I would prefer to do the pinning normally at badCertificateCallback, but until it is fixed, this was the only way I found to perform certificate pinning, at response level
mleonhard commentedon Aug 26, 2020
@TMSantos Thanks for sharing your workaround. It checks the certificate after sending the request data. I want to verify the certificate before that happens. Is there a way to make the TLS connection first, check the server certificate, and then do an HTTP request over the TLS channel?
wederchr commentedon Aug 27, 2020
@mleonhard Ah I forgot to mention that my use case is probably different. I have a flutter app that connects to a specific endpoint. To prevent MITM attacks I pin the gateway's certificate using
setTrustedCertificatesBytes
as follows.TMSantos commentedon Aug 27, 2020
@mleonhard that's the optimal scenario, is also what I want, but didn't find an workaround to do the check before sending the request data yet
rorystephenson commentedon Apr 23, 2021
Any progress on this front? I've tried all the suggestions and if I'm understanding correctly public key pinning is not possible right now if you don't have the possibility to use your own CA, which I unfortunately don't. The two aforementioned bugs seem to render this impossible for now?
zdnet commentedon Jul 26, 2022
Any progress on this issue? public key pinning is a very common way to aviod MIMT, suggest Dart could implement it ASAP officially. Thanks.
Ephenodrom commentedon Aug 25, 2022
Just to hang in and connect these two issues : #47695
Access to full certificate chain would be great.
Ephenodrom commentedon Aug 31, 2022
@TMSantos
Have you thought about bundling OpenSSL within a flutter app and using s_client via ffi to fetch the certificates ? This should give access to the complete chain and ssl pinning would be possible !?
sebkoller commentedon Nov 26, 2022
I just released a package that does public key pinning (SHA-256 of SPKI): https://pub.dev/packages/certificate_pinning_httpclient
But it only works on Android and iOS right now. It downloads the certificate chain once via a method channel and uses them
with a SecurityContext.
vanlooverenkoen commentedon Jun 27, 2023
We wanted to check the sha1 of the intermediate certificate & the common name of the leaf certificate. But this is also not possible