-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
base: main
Are you sure you want to change the base?
Conversation
QHelp previews: go/ql/src/experimental/CWE-347/JWTParsingAlgorithm.qhelpJWT Method CheckNot verifying the JWT algorithim present in a JWT token when verifying its signature may result in algorithim confusion. RecommendationBefore presenting a key to verify a signature, ensure that the key corresponds to the algorithim present in the JWT token. ExampleThe 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
|
The first one means there are build failures in a .go file in that directory. |
//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() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
// //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
There was a problem hiding this comment.
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
not exists(CallNode c2, WithValidMethods wvm, NewParser m, int i | | ||
c2.getTarget() = m and | ||
( | ||
c2.getSyntacticArgument(i) = wvm.getACall() and | ||
DataFlow::localFlow(c2.getResult(0), c.getReceiver()) | ||
) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for the brackets either.
not exists(CallNode c2, WithValidMethods wvm, NewParser m, int i | | |
c2.getTarget() = m and | |
( | |
c2.getSyntacticArgument(i) = wvm.getACall() and | |
DataFlow::localFlow(c2.getResult(0), c.getReceiver()) | |
) | |
) | |
not exists(CallNode c2, WithValidMethods wvm, NewParser m | | |
c2.getTarget() = m and | |
c2.getASyntacticArgument() = wvm.getACall() and | |
DataFlow::localFlow(c2.getResult(0), c.getReceiver()) | |
) |
or | ||
exists(FunctionName fn | | ||
c.getCall().getArgument(1) = fn and | ||
fn.toString() = f.getARead().asExpr().getEnclosingFunction().getName() | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please update your tests so that something fails when these lines are commented out. (Or, if these lines aren't working at the moment, update the tests so that they fail and will pass when these lines work.) I can then easily see what they're meant to be targeting and suggest how to implement them without using toString
.
JWT Algorithm Confusion Portion of #14081