fix(pinning): add callback for leaf certificate for better pinning
New Pull Request Checklist
- [X] I have read the Documentation
- [X] I have searched for a similar pull request in the project and found none
- [X] I have updated this branch with the latest
developto avoid conflicts (via merge from master or rebase) - [X] I have added the required tests to prove the fix/feature I am adding
- [X] I have updated the documentation (if necessary)
- [X] I have run the tests and they pass
Pull Request Description
There are debates about the usefulness of certificate or public key pinning. It is required at many major companies. If someone chooses to do it then they need access to a leaf certificate.
The existing recommendations on certificate verification don't provide a leaf certificate for the request.
All of the following are insufficient.
- Add a certificate to the
SecureContext - Use
badCertificateCallback - Use the
http_cert_pinningpackage
First. When you add a cert to the SecureContext, it will only check it against the root certificate. This means that you're going to declare trust in DigiCert or AWS or your self-signed cert. This works for a self-signed cert, but for all others this means that any middle man with a cert from your same provider can hijack your traffic.
Second. As my new test shows, badCertificateCallback does not return a leaf certificate. Instead it returns one higher up the chain. That often means that you're pinning to an AWS or other authority, and not the leaf certificate made specifically for your server.
Third. The http_cert_pinning package doesn't actually do anything with the certificates that Dio is handling. Instead, it maintains a cache of domains which it has separately tested against a list of certificate fingerprints it was given. When a request comes in, the plugin looks at the requested host, and simply asks the package if it ever checked that domain to see if the package got the correct certificates. It doesn't actually look at the certificates from this connection in Dio.
The problem stems from Dart itself.
As you can see, there is an open issue for Dart complaining that the badCertificateCallback doesn't return a leaf certificate. But one of the commenters found a workaround, and left some instructions on how to get the correct certificate.
That comment was very easy to apply to the DefaultHttpRequestAdapter. It simply adds one more callback at the right place in the response processing. If it isn't used, the request continues as expected. As my tests show, it properly delivers the leaf certificate to the callback where users can decide on the methods they want to use to approve or reject a certificate. The tests also show that badCertificateCallback does not return the leaf certificate.
Please consider adding this so that we may get one step closer to implementing full certificate pinning in pure Dart.
It would be great to have this
Hi @timshadel , I saw a package that does similar things: https://github.com/diefferson/http_certificate_pinning, can you identify the difference between your PR and the plugin? Thanks.
Hi everyone! We've made our hardfork repo public and published a new version of dio, named diox.
The new package contains the PR of the fix.
Please refer to https://pub.dev/packages/diox/versions/5.0.0-dev.1 to use the fork.
You can also see why we're working for a hardfork at https://github.com/cfug/diox/issues/29 and https://github.com/flutterchina/dio/issues/1607.