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/>
- [OAUTH-WG] Comments on draft-richer-oauth-introsp… Thomas Broyer
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Eve Maler
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Richer, Justin P.
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Thomas Broyer
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Thomas Broyer
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Torsten Lodderstedt
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Richer, Justin P.
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Thomas Broyer
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Thomas Broyer
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Torsten Lodderstedt
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Thomas Broyer
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Richer, Justin P.
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Thomas Broyer
- Re: [OAUTH-WG] Comments on draft-richer-oauth-int… Justin Richer