Subject: [OAUTH-WG] non-TLS clients and making the state parameter be use-once for authcode requests

From: Roger Crew <[email-masked]>
Date: Wed, 16 Nov 2011 12:53:48 -0800

So I'm trying to figure out if there's ANY way in which
non-TLS-capable clients (i.e., clients that cannot receive TLS
requests, e.g., because they're stuck in a shared-hosting environment
and can't have dedicated IP, can't use SSL URIs, etc... even if they
*can* initiate TLS connections to the auth server) 
can use the authorization code flow securely.

And it seems to me the CSRF scenario (10.12) makes this COMPLETELY
hopeless unless the following are requirements are introduced
(this would be a modification of 4.1.2/3):

(*) When a "state" parameter is provided, the authorization code
    ultimately issued needs to be bound to that value as well
    (i.e., in addition to being bound to the client identifier and the
    redirection URI).

(*) The access token request (4.1.3) needs to be allowed to include a
    "state" parameter and, if included, the authorization server MUST
    then reject the request if the provided state value is not
    identical to the corresponding value from the original
    authorization request.

(*) A client MUST NOT use a particular state value for more than one
    authorization request.  If a given (client_id, state) pair is used
    more than once, the authorization server MUST deny the
    authorization request, MUST deny any subsequent request for an
    access token for that client based on that state value, and SHOULD
    attempt to revoke all tokens previously issued for that client
    that are based on THE code derived from that state value.

Oddly enough, this leads to some desirable properties.

This, along with the existing requirement that authorization codes
cannot be reused, in a sense, "guards both ends" of the redirection
chain that 

- begins with the initial client response (to whatever request) that
  transmits the state value to the browser, and 
- ends with the final browser request that transmits the code
  to the client's redirect_uri, 

of which only the first and last messages can be non-TLS-protected
(because all other requests/responses are between the browser and the
authorization server, which is required to use TLS).

The key point is that, with the above requirements, for any given
(client_id, state) pair, at most one corresponding redirect chain 
can be carried to completion (i.e., issuance of an authorization code)

While an attacker who discovers the state value can attempt to obtain
a valid authorization code with her own credentials, if she does so
and her authorization request reaches the auth server first, then the
legitimate resource owner's own such request will fail in an obvious
way, and, having seen this failure, said owner will then have reason
to know that any subsequent report by the client that access has been
granted is necessarily bogus.

In this world, the authorization code, rather than being a bearer
instrument that must be kept secret at all costs, now becomes merely a
certificate that a redirect chain was carried to completion on SOME
browser SOMEWHERE.

The client, upon receiving the state value at the redirect_uri WILL be
able to verify that the request came from the same browser that it
originally sent the state value to (whether because the request is
accompanied by a matching 'secure' cookie **OR** because the browser
and client are, say, using digest authentication and state is bound to
the hashed user:realm:password secret they share -- which does NOT
entail TLS),

An attacker may still be able to launch a CSRF request at redirect_uri
AND have sufficient network control to block the legitimate request
from getting through AND be able to use the same state value so that
the client won't be able to detect the substitution.  BUT, if she
doesn't use the same authorization code as well, the authorization
server will note the mismatch and client's subsequent access_token
request will fail.  And if she uses the same code, she's not deriving
any benefit other than to be able to do DOS attacks.

Thus, both the state value and the code can now safely (or at least
more safely than before) be made public.

Conversely, without these additional requirements, i.e., if the
authorization server does NOT check that the code matches the state
given in the access_token request, then the CSRF undetectably succeeds
EVEN IF TLS is being used at redirect_uri.  And if the state can be
used more than once to obtain a valid code, then it can be used in
other browser sessions and the CSRF attacker indeed will have
something to substitute that can do damage.

Therefore, with the current spec, if the state value can leak out AT
ALL, it's game over.

Which then means that a non-TLS-capable client, which has no secure
way to transmit the state value to the browser, is COMPLETELY USELESS
in OAuth2,

At which point I don't understand why the current spec doesn't just
require TLS clients and call it a day.

-- 
Roger Crew
206.940.5732
[email-masked]