bauerj / paperless_app

An Android/iOS app for Paperless

Home Page:https://play.google.com/store/apps/details?id=eu.bauerj.paperless_app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Self-Signed Certificates in OS Trust Store are Ignored (Not Properly Validating Certificate Chain)

hydrian opened this issue · comments

Describe the bug
Android App error produces when trying to connect with a trusted cert chain.

To Reproduce
Steps to reproduce the behavior:

  1. Install Paperless app from Google Play store
  2. Enter Paperless-NG HTTPS url
  3. See error
    Screenshot_20220509-105715

Expected behavior
No errors about an insecure connection.

Screenshots
Here is my cert chain
Screenshot_20220509-105747

Screenshot_20220509-110604

Screenshot_20220509-110014

If you look at the fingerprint in the error, it is complaining about the Intermediate Root CA SHA-1 hash. I suspect the cert validation is not following the cert chain properly.

Additional information
Paperless App Version: e.g. 0.1.2 (Google Play Stable)
Do you use Paperless NG as a backend: Yes

Hey,

I suspect this has nothing to do with your certificate chain but with the way Flutter makes network connections. TLS is implemented in Dart instead of using the native OS API. This means that Flutter will not use the OS certificate store to validate certificates. More details can be found in #19

We could only implement this work-around with the red alert dialog that you see to allow self-signed certificates to be used server-side.

Until we have this properly fixed, I'll leave this issue open.

By the way, what I wrote above is only what we could piece together about this. It may be possible that this just needs a configuration change on our side but everything we tried failed.

I'll take a deeper look. I'm a junior Flutter dev, so I'll see what options we have.

I haven't been able to test this yet since I don't have a build environment configured yet, but here is example of what looks like needs to be configured.
master...hydrian:master

I am not a developer, but what @qcasey created in his branch was a step in the right direction, where this solution goes few steps further until a success. Can one of you smart guys take a look there ? I am setting new paperless server, so I may test it if someone wishes to port the solution to paperless app.

At a glance, that solution does look promising

I haven't been able to test this yet since I don't have a build environment configured yet, but here is example of what looks like needs to be configured. master...hydrian:master

This looks exactly like what @qcasey did in 388055b. Please feel free to test this again, maybe we just did something wrong or there was a different bug preventing success.

where this solution goes few steps further until a success

After a quick look, this seems to hard-code a trusted root CA into the app which is kind of the opposite of what we want to achieve here. Please correct my if I'm wrong.

After a quick look, this seems to hard-code a trusted root CA into the app which is kind of the opposite of what we want to achieve here. Please correct my if I'm wrong.

Yeah you're right, I missed that part; it's been a while 😄 It's essentially what I was describing here I suppose: #19 (comment)

The last option I've seen is to manually include your root CA certificate in assets/ and pubspec, then set up Dio to compare bad certificates to that fingerprint. This requires building paperless_app yourself of course, but is the most complete and secure solution.

Please correct me if I am wrong (I am not a Dart expert), but shouldn't it be possible to feed the app a trusted root certificate using a security context function setTrustedCertificates ? I see a file parameter, so shouldn't it be possible to make a function to read the root certificate file first (let the user choose it from disk), store it somewhere in application data, and use it subsequently in https connection calls ?

Yes, sure, but isn't this essentially what where doing right now? I mean we currently only store the cert fingerprint instead of the entire certificate but functionally it should be equivalent. I think from a UX-view the current method is better since no files have to be copied and opened.

Having to import a trusted CA per an app sucks, especially if your user base is on the larger size.

I think there may be another issue going on here too. When I enter my system trusted paperless-NG URL, I get the error. But if press 'NO' or force close/stop the app, when I relaunch the app, it goes directly to the login screen with no errors.

It obvious that app is saving the URL regardless of the untrusted or not status. Not sure if the automatically going to the login screen is because the TLS chain is trusted and the error is the false positive or if fingerprint is just being saved regardless of status and then let through the second time.

I got a debugger on it last night but got tired. I'll try again tonight.

I had a similar problem. I wanted to use an SSL proxy like Burp or Proxyman to intercept HTTPS requests from my Flutter app. The first issue was that Flutter doesn't use the system's proxy settings. I solved that by using the http_proxy plugin. However, I noticed that the http_proxy plugin was allowing any certificate by using:

client.badCertificateCallback =(X509Certificate cert, String host, int port) => true;

I thought "ok lets remove that" since I installed to my device the custom certificate generated by Proxyman my certificate will be trusted and it will not get marked as bad certificate. My assumption though was correct only for iOS. It looks like the HttpClient on iOS is automatically trusting the user installed certificates, but on android that wasn't the case.

So I ended up creating an android specific plugin flutter_user_certificates_android that gets the user installed CA certificates from the devices and I modified the http_proxy plugin so that it uses flutter_user_certificates_android to trust the certificates for any HttpClient (modified http_proxy -> flutter_system_proxy)