Add `token_auth` with timing-safe comparison to compliment `basic_auth`
Issue Details
Recap of real-life discussion with @mholt, and a follow up to the security vulnerability created by solutions like https://caddy.community/t/how-to-verify-a-header-to-validate-an-api-key/6626, which we discussed at https://caddy.community/t/a-post-in-how-to-verify-a-header-to-validate-an-api-key-requires-staff-attention/33063/2 (private link).
We have basic_auth which performs timing safe comparisons and works for standard HTTP Basic Auth.
We also have arbitrary header matchers, but they are not timing safe.
What I would like is the ability to do a timing-safe comparison that can support other standard and non-standard but common auth token formats such as:
-
Authorization: Token $GITHUB_API_KEY(pre-OIDC standardization) -
Authorization: Bearer $OLLAMA_API_KEY(post-OIDC standardization, ubiquitous) -
X-API-Key: $XCODE_LLM_API_KEY(also quite common across many products)
I believe this will best serve users as a built-in token_auth directive as it is very simple and shouldn't require all of a custom xcaddy build.
token_auth /api/expensive/* sha256 {
X-API-Auth {env.OLLAMA_API_KEY}
}
I could also see having functionality to distinguish between multiple keys to be available via {http.auth.token.id}, for parity with basic_auth.
token_auth /api/expensive/* sha256 {
X-API-Auth {env.OLLAMA_API_KEY} ollama
X-API-Auth {env.OPENAI_API_KEY} openai
}
I disagree with the idea of a generic constant time header matcher - to me that sounds like a solution in search of a problem in the same way that it would to have created a generic base64 encoder module rather than basic_auth.
I also disagree with the idea of a more complex token module that would handle arbitrary types of message digests.
This is a simple, ubiquitous problem with a well-understand and industry accepted solution.
On the security front I would suggest using sha256(guess) == sha256(answer) for the constant-time mechanism. If I understand correctly, one of the issues with the SPECTRE family of vulnerabilities is that timing-safe functions are not always timing safe due to optimizations in at the CPU level. I know that's also true in compilers, though the Go authors probably use tricks with bitwise operations that bust compiler optimization.
Assistance Disclosure
Ai NOT used
If AI was used, describe the extent to which it was used.
No response
Hi AJ, thanks for opening an issue for this!
I disagree with the idea of a generic constant time header matcher - to me that sounds like a solution in search of a problem in the same way that it would to have created a generic base64 encoder module rather than basic_auth.
I am still leaning towards this though; can you explain why it is a "solution in search of a problem"? It sounds like the problem is right here, what you posted.
Hey again Matt,
I've provided a specific problem and a specific solution analogous to basic auth.
I'm afraid that the burden would be on you to find a generic set of not-well-specified problems that need a generic, not-well-specified solution.
I'm proposing a directive that is easy to identify that we have clear evidence people have been looking for and is a de facto standard in the industry.
I see muddying that into a generic function for arbitrary logic as just something that will be at best missed by the people who need it - because most people aren't sophisticated and security aware enough to imagine looking for a constant time comparator - as seen in the forum post I referenced.
Thanks for the reply.
I do appreciate the proposed solution. I still would be interested in exploring multiple approaches so we can converge on the best one.
I'm afraid that the burden would be on you to find a generic set of not-well-specified problems that need a generic, not-well-specified solution.
That's fair; but to clarify, it's not solely my burden, anyone who is interested in this can also participate.
The other option is to implement a new provider for the authentication module: https://caddyserver.com/docs/json/apps/http/servers/errors/routes/handle/authentication/#providers
basic_auth is one such provider, and it has its own Caddyfile directive because it's common enough.
A new authentication provider could also be given a Caddyfile directive, that would do essentially what you want, except perhaps for this part: "... a more complex token module that would handle arbitrary types of message digests." (I dunno how much "more complex" it would be yet.)
... disagree with the idea of a more complex token module ...
Right -- to clarify, I don't think it would add so much more complexity (I mean, it could, I haven't spiked it out yet), so it shouldn't be something you disagree with. i.e. it is a possible way forward.