Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go: Add JWT Algorithm Confusion Query #14534

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

Kwstubbs
Copy link
Contributor

@Kwstubbs Kwstubbs commented Oct 18, 2023

JWT Algorithm Confusion Portion of #14081

@github-actions
Copy link
Contributor

github-actions bot commented Oct 18, 2023

QHelp previews:

go/ql/src/experimental/CWE-347/JWTParsingAlgorithm.qhelp

JWT Method Check

Not verifying the JWT algorithim present in a JWT token when verifying its signature may result in algorithim confusion.

Recommendation

Before presenting a key to verify a signature, ensure that the key corresponds to the algorithim present in the JWT token.

Example

The following code uses the asymmetric public key to verify the JWT token and assumes that the algorithim is RSA. By not checking the signature, an attacker can specify the HMAC option and use the same public key, which is assumed to be public and often at endpoint /jwt/jwks.jso, to sign the JWT token and bypass authentication.

package main

import (
	"fmt"

	"github.com/golang-jwt/jwt/v5"
)

type JWT struct {
	privateKey []byte
	publicKey  []byte
}

func (j JWT) Validate(token string) (interface{}, error) {
	key, err := jwt.ParseRSAPublicKeyFromPEM(j.publicKey)
	if err != nil {
		return "", fmt.Errorf("validate: parse key: %w", err)
	}

	tok, err := jwt.Parse(token, func(jwtToken *jwt.Token) (interface{}, error) {

		return key, nil
	})
	if err != nil {
		return nil, fmt.Errorf("validate: %w", err)
	}

	claims, ok := tok.Claims.(jwt.MapClaims)
	if !ok || !tok.Valid {
		return nil, fmt.Errorf("validate: invalid")
	}

	return claims["dat"], nil
}

References

@Kwstubbs Kwstubbs changed the title JWT Alg Confusion Query Go: Add JWT Algorithm Confusion Query Oct 18, 2023
@owen-mc
Copy link
Contributor

owen-mc commented Oct 18, 2023

482 tests passed; 2 tests failed:
FAILED: /home/runner/work/codeql/codeql/go/ql/test/experimental/CWE-347/CONSISTENCY/UnexpectedFrontendErrors.ql
FAILED: /home/runner/work/codeql/codeql/go/ql/test/experimental/CWE-347/ParseJWTWithoutVerification.qlref

The first one means there are build failures in a .go file in that directory.

go/ql/src/experimental/frameworks/JWT.qll Outdated Show resolved Hide resolved
go/ql/src/experimental/CWE-347/JWTParsingAlgorithm.ql Outdated Show resolved Hide resolved
go/ql/src/experimental/CWE-347/JWTParsingAlgorithm.ql Outdated Show resolved Hide resolved
//Check that the Parse(function or method) does not check the Token Method field, which most likely is a check for method type
not exists(Field f |
f.hasQualifiedName(golangJwtModern(), "Token", "Method") and
f.getARead().getRoot() = c.getCall().getAnArgument()
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to specify which argument?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I think this only works if the function literal is specified in place. If it's assigned to a variable and then that variable is used as an argument, would this catch it?

Copy link
Contributor Author

@Kwstubbs Kwstubbs Oct 19, 2023

Choose a reason for hiding this comment

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

@owen-mc having a bit of trouble coming up with a good way to see if the function name that is passed into the argument has a field read of the Method or not. If you can find another way that avoids strings altogether please let me know.

@Kwstubbs Kwstubbs requested a review from owen-mc October 23, 2023 15:45
// //Flow from NewParser to Parse (check that this call to Parse does not use a Parser that sets Valid Methods)
(
func instanceof SafeJwtParserMethod and
not exists(CallNode c2, WithValidMethods wvm, NewParser m, int i |

Check warning

Code scanning / CodeQL

Omittable 'exists' variable Warning

This exists variable can be omitted by using a don't-care expression
in this argument
.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

will change to getASyntacticArgument

or
exists(FunctionName fn |
c.getCall().getArgument(1) = fn and
fn.toString() = f.getARead().asExpr().getEnclosingFunction().getName()

Check warning

Code scanning / CodeQL

Using 'toString' in query logic Warning

Query logic depends on implementation of 'toString'.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants