Re: [OAUTH-WG] Comments on draft-richer-oauth-introspection-04

Justin Richer <jricher@mitre.org> Mon, 28 October 2013 17:57 UTC

Return-Path: <jricher@mitre.org>
X-Original-To: oauth@ietfa.amsl.com
Delivered-To: oauth@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id ECD5621E80B5 for <oauth@ietfa.amsl.com>; Mon, 28 Oct 2013 10:57:35 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -5.83
X-Spam-Level:
X-Spam-Status: No, score=-5.83 tagged_above=-999 required=5 tests=[AWL=-0.432, BAYES_00=-2.599, HTML_MESSAGE=0.001, J_CHICKENPOX_36=0.6, J_CHICKENPOX_43=0.6, RCVD_IN_DNSWL_MED=-4]
Received: from mail.ietf.org ([12.22.58.30]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id VLkr8EETrYDF for <oauth@ietfa.amsl.com>; Mon, 28 Oct 2013 10:57:30 -0700 (PDT)
Received: from smtpksrv1.mitre.org (smtpksrv1.mitre.org [198.49.146.77]) by ietfa.amsl.com (Postfix) with ESMTP id CE90E11E8294 for <oauth@ietf.org>; Mon, 28 Oct 2013 10:57:16 -0700 (PDT)
Received: from smtpksrv1.mitre.org (localhost.localdomain [127.0.0.1]) by localhost (Postfix) with SMTP id 2C0B21F06A6; Mon, 28 Oct 2013 13:57:03 -0400 (EDT)
Received: from IMCCAS01.MITRE.ORG (imccas01.mitre.org [129.83.29.78]) by smtpksrv1.mitre.org (Postfix) with ESMTP id DE8251F0723; Mon, 28 Oct 2013 13:57:02 -0400 (EDT)
Received: from [10.146.15.6] (129.83.31.51) by IMCCAS01.MITRE.ORG (129.83.29.78) with Microsoft SMTP Server (TLS) id 14.3.158.1; Mon, 28 Oct 2013 13:57:03 -0400
Message-ID: <526EA563.1060701@mitre.org>
Date: Mon, 28 Oct 2013 13:56:51 -0400
From: Justin Richer <jricher@mitre.org>
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Thunderbird/24.0
MIME-Version: 1.0
To: Thomas Broyer <t.broyer@gmail.com>
References: <CAEayHENijdeTVu9-OxsnrJEh0JQBrvQo0eKWSjFvXSLqwzVRWg@mail.gmail.com> <63692DF5-4616-4F53-B12E-518397CFEFB3@mitre.org> <CAEayHEMZdOY5G=A5nc_pA14gUcyNpbbeb-pVpo7Cf_yB70Mjjw@mail.gmail.com> <9adcccce-7c48-47b8-b9b8-77a4ce439254@email.android.com> <CAEayHEN5LoX70OObz2jj-7wmH4qqpSQmhiTbe4kwgwpEt4PMkQ@mail.gmail.com> <526AAA2F.5020705@lodderstedt.net> <CAEayHEPSvpaiAiq-eBQodxvoCRgK7TZ45i1gm=sXKs0HT1qj1g@mail.gmail.com> <AEDCC30D-5BE8-464A-AA44-A366049015CF@mitre.org> <CAEayHEOF+t+S1FEztk9234qn-27eMWZ1yiNtN03QK6y1vWthJA@mail.gmail.com>
In-Reply-To: <CAEayHEOF+t+S1FEztk9234qn-27eMWZ1yiNtN03QK6y1vWthJA@mail.gmail.com>
Content-Type: multipart/alternative; boundary="------------020500060601040400040006"
X-Originating-IP: [129.83.31.51]
Cc: "<oauth@ietf.org>" <oauth@ietf.org>
Subject: Re: [OAUTH-WG] Comments on draft-richer-oauth-introspection-04
X-BeenThere: oauth@ietf.org
X-Mailman-Version: 2.1.12
Precedence: list
List-Id: OAUTH WG <oauth.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/oauth>, <mailto:oauth-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/oauth>
List-Post: <mailto:oauth@ietf.org>
List-Help: <mailto:oauth-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/oauth>, <mailto:oauth-request@ietf.org?subject=subscribe>
X-List-Received-Date: Mon, 28 Oct 2013 17:57:36 -0000

What you suggest below is essentially what John Bradley proposed as a 
"holder of key" JWT a while ago. The main difference between the two 
approaches is that mine can also cover aspects of the HTTP request 
itself in the signature. I think you could do both of them together by 
simply making the "signed" and "hash" bits optionally empty, which would 
effectively give you the functionality you're after.

  -- Justin

On 10/26/2013 04:34 PM, Thomas Broyer wrote:
>
>
> On Sat, Oct 26, 2013 at 6:03 PM, Richer, Justin P. <jricher@mitre.org 
> <mailto:jricher@mitre.org>> wrote:
>
>>     >> On our backlog is also support for "service accounts" (to use
>>     Google's terminology), so clients will likely need to do some
>>     crypto-related work. Asking them to do it for each and every
>>     request to sign the access token might not be that
>>     >
>>     >
>>     > I assume you mean signing the request or at least some request
>>     data. Just signing the access token won't help.
>>
>>     I meant signing the access token + PR identifier/URI + some
>>     timestamp, at a minimum.
>>     I explained it better in my answer to Justin; something like
>>     jwt-bearer applied to access tokens.
>>
>>
>
>     I suggested to the group that we try something like this a while
>     ago but it didn't get traction at the time. I never really wrote
>     down the whole process I had in mind, so here's a quick overview:
>
>
>     First, the client gets a token from the auth server using any
>     normal oauth mechanism (or any abnormal mechanism for that
>     matter), and the response looks something like this:
>
>     {
>       token_type: signed,
>       access_token: <public token value>
>       token_key: <private token secret used for signing, maybe as a JWK?>
>       alg: <JWS algorithm to use for signing requests to the RS, I'll
>     use HS256 for my example below but we could probably tweak this to
>     use asymmetric keys too>
>       expires_in: 3600
>       …
>     }
>
>
>     Next, the client figures out the request it wants to make:
>
>     GET http://foo.bar/baz?quxx=bob&woz=whynot HTTP/1.1
>     Accept: application/json
>     X-Other-Header: SomeHeaderValue
>
>
>     Treating this request as a structure, we've got a verb, a url
>     (with scheme, host, path, parameters), and some headers. We want
>     the client to be able to sign some subset of this with the
>     request, so let's say this client wants to sign the verb,
>     scheme+host+port+path, the parameters, and one of the headers. So
>     the way I see it we've got a couple options. First is to repeat
>     all the items to be signed within a JSON structure and use JWS.
>     This is messy and it ignores some of the best stuff about using
>     HTTP/REST these days. Second, we can combine, normalize, and hash
>     the signable items. With this approach and some sufficient
>     handwavium (because I don't really want to get into specifics of
>     serialization and normalization here), we get something like this
>     for a base string:
>
>     GET
>     http://foo.bar/baz
>     quux=bob
>     woz=whynot
>     X-Other-Header=SomeHeaderValue
>
>     which we can hash using a defined algorithm of the same bit size
>     as our signing algorithm. So say we're using HMAC 256, we get a
>     base64url encoded hash blob like this fake one I just made up:
>     HJ23dfjoa32fasd23lajkds
>
>     So great, we've got a hash and we've got a set of data that was
>     hashed. So far we're in the same boat that got a lot of OAuth 1.0
>     implementations in trouble, due to oddities about normalization.
>     So let's take this a step further and tell the server what we're
>     hashing in our signed content, but by repeating just the *keys* to
>     this content and not the content itself, since the keys will be
>     shorter in many cases and less redundant:
>
>     signed: [ "verb", "scheme+host+path", {"query": ["quux", "woz"]},
>     {"header": ["X-Other-Header"]} ]
>
>     Note that this uses arrays, which is important because arrays
>     preserve order in JSON. Now we have a pretty programmatic way of
>     telling the server which bits we care about signing and what order
>     we put them in, with normalizing those aspects. This makes us
>     robust against stuff getting reordered and new headers or query
>     parameters being inserted (which often happens with various web
>     frameworks). Of course, after verifying a signature, an app would
>     want to make sure that important parameters were covered by that
>     signature, but that's up to the app. Any decent library would make
>     the list available to the app easily enough for a quick check.
>
>     OK, so now we've got our hash and our note on what we hashed.
>     Let's put those into a JSON object:
>
>     {
>     hash: HJ23dfjoa32fasd23lajkds,
>     signed: [ "verb", "scheme+host+path", {"query": ["quux", "woz"]},
>     {"header": ["X-Other-Header"]} ],
>     token_id: <public token value>
>     }
>
>     And we'll make our normal JWS header, use the above JSON object as
>     the bode, and sign it with JWS using the secret/key that we got up
>     in the token response:
>
>     eyheaderstufffooo.eybodystuffbaaar.signatureblob
>
>
>     [Side note: Maybe if you really wanted to you could also sign it
>     using the client secret and include the client id in the signed
>     data, like OAuth 1.0 does.]
>
>     And *now* we can send this over using any method comparable to
>     those defined in RFC6750, since it's all a single, self-contained
>     value.
>
>     GET http://foo.bar/baz?quxx=bob&woz=whynot HTTP/1.1
>     Accept: application/json
>     X-Other-Header: SomeHeaderValue
>     Authorization:
>     SignedOAuth eyheaderstufffooo.eybodystuffbaaar.signatureblob
>
>
>     or:
>
>     GET
>     http://foo.bar/baz?quxx=bob&woz=whynot&signed_access_token=eyheaderstufffooo.eybodystuffbaaar.signatureblob HTTP/1.1
>     Accept: application/json
>     X-Other-Header: SomeHeaderValue
>
>
>     Note that in the both cases, the newly introduced header and query
>     parameter are automatically excused from the signature calculation
>     because they don't show up in the signed lists.
>
>
>     OK, so the RS gets this request, and what does it do? Easy:
>
>     First, check the signature on the token. This is self-contained
>     and is a quick JWS operation. [Side note: how does the RS get the
>     private signing/checking key from the AS? I don't care, and
>     neither should you, because it's orthogonal to this part of the
>     spec family.]
>
>     Second, the RS parses the body and reads the "signed" member. This
>     "signed" member tells the RS which parts of the original request
>     it needs to check. Even with extra parameters and bits you end up
>     pulling only the parts that you need. And you know what order to
>     smash them together, too, without doing any kind of sorting!
>
>     Third, the RS calculates the hash of this string and compares it,
>     literally, to the "hash" parameter that was sent.
>
>     Fourth, the RS makes sure any "important" parameters and headers
>     and other bits are actually covered by the hash.
>
>
>
>     Anyway, I think this method is worlds simpler than what we've got
>     in http-mac right now and it goes back to solving a number of the
>     use cases that have been brought up, including my own of simply
>     protecting an HTTP message apart from tokens.
>
>
> Couldn't we have something middle-ground between bearer tokens "in the 
> clear" (RFC6750) and signing the request like http-mac or your proposal?
>
> What I'd need is *just* a way so that a "thing" passed from the Client 
> to the PR couldn't be reused with other PRs (should contain something 
> that identifies the PR), and ideally couldn't also be "replayed" 
> (let's give it a validity period, maybe also a nonce).
> Doesn't that nicely matches with JWT's "aud", "iat", "exp", "nbf" and 
> possibly "jti" of JWT, as already used by jwt-bearer? Let's just use 
> the client_id as "iss" (as in jwt-bearer) and the "access token" as 
> "sub", and sign that.
> It's assumed that the AS already knows the public key of the Client so 
> it can verify the signature; the access token response from the AS 
> would probably include the signature algorithm that the Client is 
> expected to use, but that could also have been "negotiated" before 
> (just as with jwt-bearer, you have to know what the AS expects you to 
> use).
> A compromised PR, despite knowing the access token (unless the JWT is 
> encrypted rather than just signed), couldn't access other resource 
> servers as it couldn't
> Just as with bearer tokens, it's assumed that PRs are reached through 
> TLS (if you need OAuth, it means you're accessing data of the 
> End-User, so you should use TLS anyway for privacy concerns). In the 
> event there's an eavesdropper nevertheless, the "iat", "exp" and "nbf" 
> limit the reuse of the token, and "jti" (if used, and used correctly) 
> totally mitigates the risk of replay attacks.
>
> The major remaining risk is a compromised client (and of course 
> compromised AS). I don't think we can do much things about it though, 
> apart from heavy monitoring at the AS and blacklisting the Client 
> whenever some bad use is detected.
>
> Remember I'm not a security guy though. Would that be too insecure? (I 
> doubt it, everybody uses bearer tokens nowadays, and the approach is 
> the same as with jwt-bearer) Have I missed something?
>
>
> -- 
> Thomas Broyer
> /tɔ.ma.bʁwa.je/ <http://xn--nna.ma.xn--bwa-xxb.je/>