smallstep / certificates

🛡️ A private certificate authority (X.509 & SSH) & ACME server for secure automated certificate management, so you can use TLS everywhere & SSO for SSH.
https://smallstep.com/certificates
Apache License 2.0
6.74k stars 440 forks source link

Option to bind JWK provisioner tokens with CSR #1637

Closed maraino closed 3 months ago

maraino commented 11 months ago

Description

In https://github.com/smallstep/certificates/discussions/1636, @daFritz84 proposes that binding JWT tokens for the JWK provisioner to a given CSR might be a good option to prevent some attacks.

Optionally, adding a new field in the token with a CSR fingerprint can be a good idea. Commands that generate their own token and CSR can always add the new field, and the JWK provisioner will validate it. step ca token can have an extra flag to add that fingerprint and a flag to pass a CSR to get the fingerprint from.

It might also be a good idea to add a fingerprint to x509util.CertificateRequest, so it can be optionally checked in a template or in a webhook, although the raw CSR is also present in a webhook request.

daFritz84 commented 11 months ago

Quick note: Unlike certificates, there is no canonical "fingerprint" for CSRs as far as I know. Rather one would generate the message digest of the der format.

openssl req -in example.csr -outform DER | openssl dgst -sha256 -c

There is a rather 🔥 discussion on this right here: https://security.stackexchange.com/questions/262476/getting-fingerprint-of-certificate-signing-request

maraino commented 11 months ago

@daFritz84, that was the idea, and that's exactly how the certificate fingerprint is calculated.

maraino commented 11 months ago

@daFritz84 after our team discussion, some voices were not fully convinced about adding this. Can you provide more insight into why and how you want to use this? How are your token/certificate flows that this can be a problem?

daFritz84 commented 11 months ago

@maraino sure thing. I whipped up a quick sketch to illustrate my case: github_sketch

So I do have a scenario where an 'IoT device' is requesting a certifcate from the 'Certificate authority' in the cloud. The request is transfered via asynchronous communication (publish/subscribe). There is only segment-by-segment encrypted tunnels, and the CSR (as by the nature of async communication of Kafka and MQTT) cached at, at least, two other services. Hence I would prefer a strong binding between the JWT and the CSR.

I hope I have described the scenario clearly, and this brings my point across. Btw. async communication (MQTT) was implemented due to low bandwidth/unreliable networks. In my threat model, the edge gateway or the proxy could be compromised to allow MITM attacks on the CSRs.

maraino commented 10 months ago

@daFritz84, we will add support for this in our cli and certificates, it is not a high priority, so it can take some time.

daFritz84 commented 10 months ago

@maraino Thanks for the x-mas gift 😄, I'll take it.

maraino commented 10 months ago

I've been working on a yet incomplete implementation where tokens for the JWK and X5C provisioners can have a confirmation claim, cnf with a kid parameter as defined in RFC 7800#section-3.4.

The payload of a token with this will look like:

{
  "aud": "https://ca.smallstep.com:9000/1.0/sign",
  "cnf": {
    "kid": "PJLNhtQoBE1yGN_ZKzr4Y2U5pyqIGiyyszkoz2raDOw"
  },
  "exp": 1703803318,
  "iat": 1703803018,
  "iss": "mariano@smallstep.com",
  "jti": "458bd150aaa7ba0bf1b0c927f17e1f52e22954db64e3aaa3b7da92a4ddcdc832",
  "nbf": 1703803018,
  "sans": [
    "mariano@smallstep.com"
  ],
  "sha": "a78a850025c0c234385eda23d7192964ca56aa9d727f535653b0afb81c1e0559",
  "sub": "mariano@smallstep.com"
}

In this case, that kid is SHA-256 of the raw data of a CSR using the base64url without padding encoding. In the same way, I'm also working on adding the same functionality for SSH certificates. In this case, the fingerprint will be in the marshaling of the SSH public key.

When the JWK or X5C provisioner of the CA sees a token with that confirmation claim, it will validate that the fingerprint matches the provided CSR or SSH public key; if it does not, it will return a 403 error.

Note that the fingerprint for a CSR will be based on the full CSR, not just the key.