[personal profile] mjg59
As I mentioned last time, bearer tokens are not super compatible with a model in which every access is verified to ensure it's coming from a trusted device. Let's talk about that in a bit more detail.

First off, what is a bearer token? In its simplest form, it's simply an opaque blob that you give to a user after an authentication or authorisation challenge, and then they show it to you to prove that they should be allowed access to a resource. In theory you could just hand someone a randomly generated blob, but then you'd need to keep track of which blobs you've issued and when they should be expired and who they correspond to, so frequently this is actually done using JWTs which contain some base64 encoded JSON that describes the user and group membership and so on and then have a signature associated with them so whenever the user presents one you can just validate the signature and then assume that the contents of the JSON are trustworthy.

One thing to note here is that the crypto is purely between whoever issued the token and whoever validates the token - as far as the server is concerned, any client who can just show it the token is just fine as long as the signature is verified. There's no way to verify the client's state, so one of the core ideas of Zero Trust (that we verify that the client is in a trustworthy state on every access) is already violated.

Can we make things not terrible? Sure! We may not be able to validate the client state on every access, but we can validate the client state when we issue the token in the first place. When the user hits a login page, we do state validation according to whatever policy we want to enforce, and if the client violates that policy we refuse to issue a token to it. If the token has a sufficiently short lifetime then an attacker is only going to have a short period of time to use that token before it expires and then (with luck) they won't be able to get a new one because the state validation will fail.

Except! This is fine for cases where we control the issuance flow. What if we have a scenario where a third party authenticates the client (by verifying that they have a valid token issued by their ID provider) and then uses that to issue their own token that's much longer lived? Well, now the client has a long-lived token sitting on it. And if anyone copies that token to another device, they can now pretend to be that client.

This is, sadly, depressingly common. A lot of services will verify the user, and then issue an oauth token that'll expire some time around the heat death of the universe. If a client system is compromised and an attacker just copies that token to another system, they can continue to pretend to be the legitimate user until someone notices (which, depending on whether or not the service in question has any sort of audit logs, and whether you're paying any attention to them, may be once screenshots of your data show up on Twitter).

This is a problem! There's no way to fit a hosted service that behaves this way into a Zero Trust model - the best you can say is that a token was issued to a device that was, around that time, apparently trustworthy, and now it's some time later and you have literally no idea whether the device is still trustworthy or if the token is still even on that device.

But wait, there's more! Even if you're nowhere near doing any sort of Zero Trust stuff, imagine the case of a user having a bunch of tokens from multiple services on their laptop, and then they leave their laptop unlocked in a cafe while they head to the toilet and whoops it's not there any more, better assume that someone has access to all the data on there. How many services has our opportunistic new laptop owner gained access to as a result? How do we revoke all of the tokens that are sitting there on the local disk? Do you even have a policy for dealing with that?

There isn't a simple answer to all of these problems. Replacing bearer tokens with some sort of asymmetric cryptographic challenge to the client would at least let us tie the tokens to a TPM or other secure enclave, and then we wouldn't have to worry about them being copied elsewhere. But that wouldn't help us if the client is compromised and the attacker simply keeps using the compromised client. The entire model of simply proving knowledge of a secret being sufficient to gain access to a resource is inherently incompatible with a desire for fine-grained trust verification on every access, but I don't see anything changing until we have a standard for third party services to be able to perform that trust verification against a customer's policy.

Still, at least this means I can just run weird Android IoT apps through mitmproxy, pull the bearer token out of the request headers and then start poking the remote API with curl. It may all be broken, but it's also got me a bunch of bug bounty credit, so, it;s impossible to say if its bad or not,

(Addendum: this suggestion that we solve the hardware binding problem by simply passing all the network traffic through some sort of local enclave that could see tokens being set and would then sequester them and reinject them into later requests is OBVIOUSLY HORRIFYING and is also probably going to be at least three startup pitches by the end of next week)

RE: Bearer tokens are just awful

Date: 2022-04-05 03:41 pm (UTC)
From: (Anonymous)

Is there a way for the server to indicate that the bearer token expiration is too long and to renegotiate the expiry date? The downside is:

  • It requires an additional logic step to be added to the server code or a proxy somewhere before it like an API gateway (so that you don't have different expiry logic spread all over the application space).
  • It may require the renewal of the bearer token for other apps that use the same token, since the most restrictive expiry policy would determine the expiry policy for all other apps using that token.

But that seems like what we want. You might get some increased traffic, to renew the tokens, but as long as the bearer token expiring has some reasonable minimum level (a few mins, maybe?) then that should be lost in the regular traffic noise.

The challenge is whether that use case is available via the bearer token protocol. If not, it's possible to implement it on the server-side with a proxy keeping track of tokens and their expiration and how long they have been used by the system. If a token has been used longer than your expiry policy then reject that token to force a re-issue. But just because you can doesn't mean it's easy or that you should. A proxy that does this expiry tracing has lots of potential for misconfiguration, complicated edge cases, and possibly abuse. It would be better if the protocol allowed for expiry-based policy negotiation as part of the token auth process, and leave the expiry check for the protocol.

Re: Bearer tokens are just awful

Date: 2022-04-06 05:39 am (UTC)
From: (Anonymous)
The article has a point but it's over-dramatic about it.

The tokens are signed by the server, so they can't be modified before handing them back later or it will fail the signature check on the JWT and be discarded. So the expiry date of the token is something the server decides when it issues it; checking it for being "too long" at the server is meaningless then. The server doesn't think its own expiry dates it selected are "too long".

The signed tokens can actually contain arbitrary additional server-decided info such as client IP it was handed to, and the server can check when given it back that the client is still on the same IP. In the same way, the token can bind to a role (eg, allowed to read only account xyz).

So bearer tokens can still be part of zero trust, the part that says you knew one kind of login secret.

Re: Bearer tokens are just awful

Date: 2022-04-07 01:36 am (UTC)
sweh: (Default)
From: [personal profile] sweh
"Yes".. and "No".

I did some testing, in the past, with Google OIDC. It's quite easy to integrate with, so I created a test page that is configured to only allow my login

AuthType openid-connect
Require user sweharris@gmail.com 


And this worked quite well. If you try to access the page you'll get a login prompt ("what google account do you want to use").

The problem...

Hello Stephen Harris (sweharris@gmail.com)

Your claim was issued at Wed Apr 6 21:24:36 EDT 2022

Your claim will expire at Wed Apr 6 22:24:36 EDT 2022

Your access token expires at Wed Apr 6 22:24:35 EDT 2022

Activity in my logs will show as user sweharris@gmail.com

(All this information is magically handled by the apache OIDC module).

So we can see that the Google claims last 1 hour. That would seem reasonable, except regulatory environments such as PCI may require "15 minute idle expiration" (PCI 8.1.8, from memory).

If we do a session expiration within the claim period and force a redirect to the login page then google will automatically create a new token. You don't effectively have a session expiration 'cos there's no new authentication event (the user doesn't get asked for credentials or to approve session continuation).

There _is_ a logout URL... but that logs you out of Google, globally!

I've seen smaller credit unions use 3rd party OIDC providers that set a 12hr token. I can not see how this can possibly be PCI compliant.

Re: Bearer tokens are just awful

Date: 2022-04-11 04:19 pm (UTC)
From: [personal profile] mebrown
You can address this by using the OpenID redirect URL to launch a "normal" session that you can expire, ie. dont use the JWT directly as your 'session'. This is basically what I've done on a couple products and it seems to work well.

You can expire a session with immediate effect, and make the user re-do their oidc login (whereupon you can check the account expiry/etc)

Token Binding/Proof of Possession

Date: 2022-04-05 06:30 pm (UTC)
From: [personal profile] mebrown

Couple things that can start to help with this situation:

  1. Token Binding + client TLS certs You store the private keys for the client TLS cert in your TPM, then you can indicate the TLS cert fingerprint in the JWT. The server validating the JWT ensures that the fingerprint in the token matches the TLS remote side certificate fingerprint. This gets part of what you are asking about.

  2. OAuth Proof of Possession Similar concept, as part of the JWT validation, you can require that the sender prove they have possession of a specific private key.

Re: Token Binding/Proof of Possession

Date: 2022-04-07 06:34 am (UTC)
From: [personal profile] nolaviz

Let's make this even better...

Can a local process somehow tell the TPM to shut down until reboot? Then we could also have a local daemon that tracks client state, and if it detects a compromise - it would disable the local TPM, which would cause the already-issued tokens to become useless.

(Sure, an attacker taking over the client could disable this daemon; but it's another hurdle.)

Profile

Matthew Garrett

About Matthew

Power management, mobile and firmware developer on Linux. Security developer at Aurora. Ex-biologist. [personal profile] mjg59 on Twitter. Content here should not be interpreted as the opinion of my employer. Also on Mastodon.

Expand Cut Tags

No cut tags