Skip to content
This repository was archived by the owner on May 21, 2022. It is now read-only.

Support aud claim verification for arrays #188

Closed
wants to merge 2 commits into from

Conversation

kekoav
Copy link

@kekoav kekoav commented Dec 20, 2016

This references issue #184 .

The goal is to support compliance with RFC 7519 Section 4.1.

JWT audience claims aud should support an array of valid aud values in the token. When validating a token, a single aud value is provided and compared against the array of aud claims.

Care was taken to maintain backward compatibility with the string version of aud as it works today. This is also part of the spec, so this should not be a breaking change at all. A single string is supported and is treated as a list of one item.

The library already supports creation of JWTs with this structure using the MapClaims, but obviously the StandardClaims struct is too rigid for this use case.

Please give me your feedback- I'm happy to improve this pull request as needed.

…ings.

Add tests to help ensure backward compatibility, and compliance with RFC 7519 Section 4.1. https://tools.ietf.org/html/rfc7519#section-4.1
@kekoav
Copy link
Author

kekoav commented Dec 20, 2016

So the build fail is on Go 1.3 it's due to a poor implementation of the ConstantTimeCompare() function which assumed equal lengths, and panics 😮 if they are different . This fail should occur on any comparison using this method where the values have dissimilar lengths when using Go 1.3. I could fix this by performing the length check before calling ConstantTimeCompare(). This is a bit annoying because it adds an extra check for the majority of users Go 1.4+, but if we need to retain Go 1.3 support then we have to do something like that.

Go 1.3 pkg/crypto/subtle/constant_time.go

// ConstantTimeCompare returns 1 iff the two equal length slices, x
// and y, have equal contents. The time taken is a function of the length of
// the slices and is independent of the contents.
func ConstantTimeCompare(x, y []byte) int {
	if len(x) != len(y) {
		panic("subtle: slices have different lengths")
	}
...

Fixed in Go 1.4 pkg/crypto/subtle/constant_time.go

// ConstantTimeCompare returns 1 iff the two slices, x
// and y, have equal contents. The time taken is a function of the length of
// the slices and is independent of the contents.
func ConstantTimeCompare(x, y []byte) int {
	if len(x) != len(y) {
		return 0
	}
...

It would be nice to have a more robust suite of tests around claim validation - this issue should cause panics in 1.3 if a JWT claim for aud, or iss is attempted to be validated against strings with a different length.

@dgrijalva
Copy link
Owner

Might make sense to figure out sunsetting 1.3 support. How do you think that interacts with semver?

} else {
return false

for _, a := range aud {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some information leaked here, since an early match will take less time. I'm not sure if that presents an actual security risk, though.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's a good point- we can run the comparison on all items, and return true if any of them matched. On a related note- the way ConstantTimeCompare() is written can leak information on the length of the string since when the strings are not equal in length it takes less time.

aud := m["aud"]

switch aud.(type) {
default:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work with the default case first? I thought it had to be last...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works first, I saw this first at https://golang.org/doc/effective_go.html#type_switch . I agree it probably reads better last though.

@kekoav
Copy link
Author

kekoav commented Dec 21, 2016

@dgrijalva

Might make sense to figure out sunsetting 1.3 support. How do you think that interacts with semver?

I'm not sure exactly about what to do with Semver for dropping a supported language version. I think it just needs to be communicated clearly. My initial thought is since there are no breaking API changes, this is definitely not a major release. Dropping Go 1.3 is notable enough that you may want it to be a minor point release (3.1).

@kekoav kekoav changed the title Issue #184 Support aud claim verification for arrays Support aud claim verification for arrays Dec 21, 2016
@kekoav
Copy link
Author

kekoav commented Jan 26, 2017

@dgrijalva any feedback on this PR? I'm happy to improve this PR based on your feedback.

matchesAny := false
for _, a := range aud {
if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 {
matchesAny = true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can break here, or simply return true

func verifyAud(aud string, cmp string, required bool) bool {
if aud == "" {
func verifyAud(aud []string, cmp string, required bool) bool {
if len(aud) <= 0 || aud[0] == "" {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why <=?

@dgrijalva
Copy link
Owner

A modified version of this has landed in the v4 branch.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants