Re: [OAUTH-WG] Initial JSON Web Token Best Current Practices Draft

Neil Madden <neil.madden@forgerock.com> Tue, 17 April 2018 21:09 UTC

Return-Path: <neil.madden@forgerock.com>
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 852361289B0 for <oauth@ietfa.amsl.com>; Tue, 17 Apr 2018 14:09:10 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -2
X-Spam-Level:
X-Spam-Status: No, score=-2 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=ham autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (1024-bit key) header.d=forgerock.com
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 32L8nEFfsuuE for <oauth@ietfa.amsl.com>; Tue, 17 Apr 2018 14:09:07 -0700 (PDT)
Received: from mail-wr0-x234.google.com (mail-wr0-x234.google.com [IPv6:2a00:1450:400c:c0c::234]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id AAF0412D9FF for <oauth@ietf.org>; Tue, 17 Apr 2018 14:09:05 -0700 (PDT)
Received: by mail-wr0-x234.google.com with SMTP id z73so39209207wrb.0 for <oauth@ietf.org>; Tue, 17 Apr 2018 14:09:05 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=forgerock.com; s=google; h=mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=xygc6qiV+TNYEITG0+n+aYqlW/4rf6iEkEfRzmwEm2g=; b=EWxDEI/zqnR95iSIK47btH+Vvgr3mKER2HkNOi7gSJtZ3ydG8AAGQkQuKcOOep+Ua7 1nkYjyCHcSxw/jb7cSmtsVz3kZJVk51PAhtBZl1YfvYm2HE7bZBZtkkRBLWaQkdFYJXJ 1znAPmZlCuui0bB3F9/R9naZwSa1WrDX0PKjw=
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=xygc6qiV+TNYEITG0+n+aYqlW/4rf6iEkEfRzmwEm2g=; b=EU3c3iFxCCl1qyPMxKUQzFrcvo63DQ17A/JV0+cdkGXB35YrX/hqLePINEoIE/JuiP uCC2lEQkUFqv/6KCY6x0rAwpgkL+af58rv/gi5kmVPi1AXYvVsXLwRTSBdje3kMhQxuk EMc7UtBkbwgaGFLOtyw+UAlrneDuwMmzNLJ8DLhEPFrcq5vhoDAUPh37pmGe3aG4mzDE n/kyC4XEDyPgX9hTJ6vFI2ldxxaOT5B2vuUy/YONE3B54C+dZeSBUMGXjFeTDAhftjfN KdE7jRae1Q01T7RKJCd9VIVxPi9ALY1vUL3DLtsYsZM0A7S2iQD5N74/ldPPXb9gSKT6 uUZQ==
X-Gm-Message-State: ALQs6tDPUb6cmCOtqAp4tkF6NUqwO1udgYyu0bEI14iYxgup7CRZ6imR tE3QLQsuYPwWFvzPgpJWO+FjlHzThwk=
X-Google-Smtp-Source: AIpwx4/e3Ll1H9AMjPa65o/nMgvzTQD9uJeg5/8PGsox1t3rl6FfeME1pGwsvq+ZAgOrgzkhYeh84A==
X-Received: by 10.28.198.77 with SMTP id w74mr2096747wmf.36.1523999343720; Tue, 17 Apr 2018 14:09:03 -0700 (PDT)
Received: from guest2s-mbp.home (198.179.93.209.dyn.plus.net. [209.93.179.198]) by smtp.gmail.com with ESMTPSA id l131sm20102361wmb.36.2018.04.17.14.09.02 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 17 Apr 2018 14:09:02 -0700 (PDT)
Content-Type: text/plain; charset="utf-8"
Mime-Version: 1.0 (Mac OS X Mail 11.3 \(3445.6.18\))
From: Neil Madden <neil.madden@forgerock.com>
In-Reply-To: <B38A4D02-A475-479A-90B4-868676F7C607@forgerock.com>
Date: Tue, 17 Apr 2018 22:09:01 +0100
Cc: "oauth@ietf.org" <oauth@ietf.org>
Content-Transfer-Encoding: quoted-printable
Message-Id: <79532099-B05C-4C22-83CE-E9CE2025B191@forgerock.com>
References: <CY4PR21MB0504E898E2414522D172663BF5F50@CY4PR21MB0504.namprd21.prod.outlook.com> <B38A4D02-A475-479A-90B4-868676F7C607@forgerock.com>
To: Mike Jones <Michael.Jones@microsoft.com>
X-Mailer: Apple Mail (2.3445.6.18)
Archived-At: <https://mailarchive.ietf.org/arch/msg/oauth/6zLSy3XTovgAoPSPgMKeHDMT82w>
Subject: Re: [OAUTH-WG] Initial JSON Web Token Best Current Practices Draft
X-BeenThere: oauth@ietf.org
X-Mailman-Version: 2.1.22
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: <https://mailarchive.ietf.org/arch/browse/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: Tue, 17 Apr 2018 21:09:10 -0000

Hi Mike,

I sent this originally back in June last year, I can see some of these points have been addressed in -01, but not others, so I will include further comments in-line below. (Apologies if I missed replies - I’ve realised a few messages from this WG have ended up in my spam folder).

As a general point, the BCP is for JWT, but some of the advice (and my points below) apply more generally to other JOSE objects (as pointed out in the introduction). Should this be reflected in the title?


> On 4 Jun 2017, at 15:11, Neil Madden <neil.madden@forgerock.com> wrote:
> 
> I originally set this message just to the BCP authors. As requested by Mike Jones, I am sending it here too:
> 
> Hi,
> 
> I've just seen this draft best-practice guide for JWTs pop up. I have a number of suggestions for improvements. Mostly, I think the advice is good but should be spelt out a bit more clearly. Here are some suggestions:
> 
> 1. Explicitly spell out the ECDH-ES public key validation routines from NIST. I have a blog post summarising them: https://neilmadden.wordpress.com/2017/05/17/so-how-do-you-validate-nist-ecdh-public-keys/

To be clear here, I think section 3.4 (Validate Cryptographic Inputs) should have text along the lines of the following added:

“ECDH-ES ephemeral public key (epk) inputs should be validated according to the recipient’s chosen elliptic curve. For NIST prime-order curves P-256, P-384 and P-521, validation MUST be performed according to Section 5.6.2.3.4 “ECC Partial Public-Key Validation Routine” of NIST Special Publication 800-56A revision 3 [1]. Where the X25519 or X448 curves of RFC 8037 [2] are used, then the implementation MAY reject any ephemeral public key that produces an all-zero ECDH shared secret as per RFC 7748 Section 6 [3]."

[1] https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar3.pdf 
[2] https://tools.ietf.org/html/rfc8037 
[3] https://tools.ietf.org/html/rfc7748#section-6

I should note that the final sentence about validating X25519/X448 public keys is not without some controversy. See https://moderncrypto.org/mail-archive/curves/2017/000896.html for a rabbit-hole discussion together with associated links.

I do not feel confident to say what the right answer is for those curves - whether to validate at all, and if so whether to blacklist bad public keys or check for an all-zero shared secret. Perhaps we should get CFRG input on this point? Or should we take RFC 7748 as being the considered CFRG opinion on this topic (and so copy the “MAY” wording)?

> 2. Recommend that (despite the -ES) ECDH is safest when *both* keys are ephemeral (eg you use some initial step to retrieve a fresh authenticated ephemeral key from the other party). 

I think this comment is not necessary in hindsight. While fully-ephemeral ECDH has some advantages (as does static-static in some cases, e.g. NaCl crypto_box), it’s a more complicated discussion. For this BCP it is best to concentrate on ECDH-ES as specified and ensure that implementations perform proper validation of ephemeral public keys.

> 3. Spell out how to authenticate ECDH ephemeral keys. For instance, include an inner signed JWT that repeats the epk and possibly the apu/apv claims too. 

This is probably a more general point that none of the public-key encryption modes in JOSE are authenticated. If sender authentication is required then nested signed-and-encrypted objects should be used.

> 4. Recommend that the apu and apv claims as a bare minimum include a hash of both public keys and SHOULD include any other known identifiers. 

Section 5.8.2 of NIST SP.800-56A rev 3 (referenced in point 1 above) gives some advice on the kinds of things that should be included here. Appendix B of the same document provides some rationale for why this should be done, as does RFC 7748 Section 7 (Security Considerations). RFC 7518 (JWA) Section 4.6.2 already normatively references NIST SP.800-56A, but only for “applications wishing to conform to [NIST.800-56A]”. In my opinion, this should be strengthened in this BCP to a SHOULD/RECOMMENDED.

> 5. Recommend that the receiving party recalculates the apu and apv claims from known inputs to check they match. (Or leave these out of the JWT and require the other party to recalculate them).
> 6. Provide explicit key lifetime requirements. E.g., for AES GCM this should not exceed 2^32 messages for randomly-generated IVs, and not exceed 2^64 *blocks* in total (so unless you restrict JWT lengths to less than 2^32 blocks and use a message counter as IV then this also limits to 2^32 messages). For CBC it should not exceed 2^48 messages. 

I withdraw this comment, RFC 7518 Section 8.2 already addresses this point.

> 7. Require that the "alg" and "enc" claims are ONLY used to reject unexpected algorithms, NEVER to select an algorithm (even amongst a "supported" set). Cryptographic agility should be achieved by using "kid" claims that reference one of a set of known keys and the key should specify the algorithm (either explicitly or by the key parameters/size). 

I think this is now adequately addressed by section 3.1.

> 8. Deprecate or strongly recommend against all of the RSA encryption modes except OAEP-256. 
> 9. Strongly discourage any use of compression with encrypted JWEs - it is too easy to leak sensitive information. 
> 10. Recommend that if a JWE is used to encrypt a password or other value for which knowing the length may be a weakness, that such fields are explicitly padded by the application or at least use CBC mode to reduce the amount of length information leaked to a multiple of the AES block size. 

These last two points are related: encryption hides everything apart from the length of a plaintext. Compression varies the length based on the content. The combination of the two weakens the security of the encryption, but the length may already be sensitive. Applications should consider what information might be leaked in these cases and make informed decisions about whether to pad or compress content before encryption.

> 11. Require constant-time comparisons of HMAC tags. 

This point is already covered by RFC 7518 Section 3.2.

> 12. Recommend using deterministic ECDSA signatures as described in RFC 6979 to minimise the risk of leaking the private key. 
> 13. Recommend that if the ECDH calculation produces an all-zero shared secret that this is rejected. 

This is covered in my suggestion in point 1.

> 14. Never produce a signed JWT containing a "sub" claim unless you are explicitly vouching for the identity of that subject. It is far too easy to accidentally produce valid OIDC id tokens from unrelated code!

I think this is now covered by the section on explicit typing. 

Explicit typing is a welcome addition. A complementary approach would be to recommend using different cryptographic keys for different types of JWTs. One way to achieve this for symmetric algorithms or ECDH would be to use a key-derivation function to derive unique keys based on some domain separation string.

I find the discussion in section 3.2 about the “none” algorithm unnecessary for the BCP. I would say using “none” should not be a recommended best practice.

A colleague recently shared this pen-tester JWT cheat-sheet with me: https://assets.pentesterlab.com/jwt_security_cheatsheet/jwt_security_cheatsheet.pdf which hints at a few other points worth mentioning (I can draft text for these if you wish):

- If the “kid” header is used to perform a lookup in a database then there is a risk of SQL/LDAP injection if the value is not validated first or appropriate interfaces used (e.g. prepared statements).
- The “jwk” header should not be directly trusted, but only ever used to match a known key. Likewise, I’d say the “jku” header should really be matched against a known whitelist before being trusted, as it might additionally lead to SSRF attacks if the server can be tricked into loading arbitrary URLs.

Kind regards,

Neil