Thursday, March 5, 2020

Auth0 and JWKS - What about the Key ID?

I recently inherited a software project, which needed some updates. It is a NodeJS project that was run as a Google Cloud function. It was part of a software system which made use of the Auth0 (https://auth0.com) service for authentication of users. Another application took care of the authentication and received a JSON Web Token (JWT), which is an encrypted item. That token is passed to the Google Cloud function. Next that Cloud Function must validate it.

To do this, the function makes use of two npm libraries: jsonwebtoken and jwks-rsa. Jwks-rsa is associated with the Java Web token Key Signing protocol. This is basically a simple https endpoint that returns a JSON string. By using a key id to index into that JSON (array), you can get the public key. Auth0 generates a RSA private/public key pair, unique for each account. They sign the JWT authentication tokens they provide with the private key. Then JWKS provides the public key which can be used to verify the JWT actually came from Auth0.

Here are some excerpts from the code I was looking at:

const jwt = require("jsonwebtoken");
const jwksClient = require("jwks-rsa");

const client = jwksClient({
            strictSsl: true,
            jwksUri: "https://your-company.auth0.com/.well-known/jwks.json",
            cache: true,
            rateLimit: true,
            jwksRequestsPerMinute: 5,
});

client.getSigningKey('some random characters that looked like base 64 were here', (err, key) => {
...
});

Of course, "your-company" and "some random..." were both replaced with values I am not sharing. Your-company is based on the account created at Auth0. The "some random..." turns out to be the Key ID.

Instinctively I reacted against that Key ID being hard-coded into a source file that is stored in an Internet accessible git repository. Is that secure? And, where did that Key ID come from? Was it available in the Auth0 web GUI? Could it be retrieved from another source at run time?

I asked about this on the Auth0 forum (you need to create an account to view). I got directed to the documentation and given the suggestion that the Key ID shouldn't be encoded. I was left with more questions.

Finally, it occurred to me to just curl the endpoint referenced (jwksUri) and see what it contained. It is a JSON array, with a single entry which is an object containing the "kid" Key ID and "x5c" public key. This is completely open. All someone needs is the "your-company" part of the URI and they can retrieve this too. All they can really do with the public key found here is validate the Auth0 Tokens are valid and from Auth0. So no big deal. I also determined that you get the Key ID by just looking at the data. Then .getSigningKey uses that Key ID to identify the correct array element.

Will there ever be more than one array element? Well, the Auth0 documents have this to say about that:
Currently, Auth0 only supports a single JWK for signing; however, it is important to assume this endpoint could contain multiple JWKs. As an example, multiple keys can be found in the JWKS when rotating signing certificates. 
So, what if there are two? How do you know which one is the right one? I have no answer for that.

Bottom line, it is not a problem of security risk hardcoding the Key Id. It reveals nothing further that isn't already publicly accessible. However, what if the Key ID should change? That would be a problem which currently lacks a good solution. Now, perhaps Auth0 will never change the Key Id. They could still change the actual private/public keys, and update the public key on the JWKS endpoint and things would be fine.

So, for now, keep the Key ID hardcoded, but document what to do if it ever stops working (access the endpoint and get the new Key ID).

Saturday, February 15, 2020

Dealing With Sparse Monitoring Data on Google Cloud Platform - Get the Raw Data!


I just wrote a blog post for my company, New Context Security, on how to deal with sparse monitoring data on Google Cloud Platform, using a Python script to retrieve the actual data:

https://newcontext.com/python-to-retrieve-gcp-stackdriver-monitoring-data/