[fetch] Request for support for certificate pinning (#98)

**I'd like to propose support for certificate pinning.** 

Note that this is not the same as HSTS although it seems to be related.

I see two use cases that I believe are both useful:

Global
---

The first one is to have a global hash of valid domains to valid fingerprints. All HTTPS requests would look up the domain in the hash and match the cert (chain) to the valid fingerprints. Unexpected certificates would fail the request.

A request could have a flag that indicates whether it wants to opt out of this or weather the request should fail or instead just continue with a flag set. I do think that it should fail by default because this would allow to add certificate pinning to an application without changing all the fetch calls.

Example:

    GlobalFetch.addCertFingerprintsSha256({
        "*.example.com": [ "E7:EF:F9:08:26:0B:B2:81:74:E8:C6:65:95:42:35:C0:B0:19:7E:79:8D:E4:3F:54:82:1F:B3:3E:AB:8B:1E:A2" ],
        "*.google.com": [ "61:53:B1:8A:15:64:51:13:6A:78:37:DB:8D:0F:5A:73:B9:30:8E:80:5C:83:C8:CD:0F:66:9A:B3:DD:74:80:1F" ]
    )

    // somewhere else
    GlobalFetch.fetch("https://...") ... // will fail for anything but example.com and google.com or if any of those use an unexpected cert

    GlobalFetch.fetch( { url: "https://...", certpinning: "off" }) ... // will not fail due to cert pinning

Per call
---

The second case is a per-call option. An additional parameter in the option hash would give valid expected fingerprints for this request. If the server cert does not match one of the passed fingerprints the request would fail (or be flagged).

Example:

    GlobalFetch.fetch( { url: "https://www.example.com/", certs_sha1: [ "EC:AD:27:F6:69:AE:C4:FA:95:2F:6E:58:4A:74:D5:9C:8D:8A:12:5F" ] }) ...

This could be combined with the global list of fingerprints with the same flag as in the global case, so the possible cases would be:
- no certs, cert pinning off: default, no cert pinning, all request succeed
- have global certs, cert pinning on "strict" by default
- "strict": all requests have to match a known cert and fail if they don't
- "flag": requests that don't match a know cert will be flagged
- "off": certificate pinning tuned off for this request
- "private": ignore global list, only use certs in the options. Fails the request if it dies not match. This is the default if a list of certs is passed to the fetch call.
- "private-flag" or just "flag" with certs passed in the options: Just like "private" but only flags the request.
- "merge": The cert must match one of the global _or_ one of the passed certs.
- "merge-flag": Like "merge" but only flags the request

Since HSTS only supports sha256 certificate fingerprints (for now) I think it should at least support that. Sha1 might be an option as well. Just like HSTS the parameters or functions should have different names according to the fingerprint types they access.

I'm not sure about the specific names for the functions and parameters, any comments or suggestions are welcome.

As an alternative to this I could also see the server cert chain including the public keys and/or its fingerprints be exposed in the response which would allow the client to do cert pinning by itself. This could potentially be more useful for other applications (than cert pinning) but would also make cert pinning more tedious.

---
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/fetch/issues/98

Received on Thursday, 30 July 2015 14:38:23 UTC