зеркало из https://github.com/github/codeql.git
Merge pull request #14075 from amammad/amammad-go-JWT
Go: Improved JWT query, JWT decoding without verification
This commit is contained in:
Коммит
96543b8337
|
@ -0,0 +1,28 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
)
|
||||
|
||||
var JwtKey = []byte("AllYourBase")
|
||||
|
||||
func main() {
|
||||
// BAD: usage of a harcoded Key
|
||||
verifyJWT(JWTFromUser)
|
||||
}
|
||||
|
||||
func LoadJwtKey(token *jwt.Token) (interface{}, error) {
|
||||
return JwtKey, nil
|
||||
}
|
||||
func verifyJWT(signedToken string) {
|
||||
fmt.Println("verifying JWT")
|
||||
DecodedToken, err := jwt.ParseWithClaims(signedToken, &CustomerInfo{}, LoadJwtKey)
|
||||
if claims, ok := DecodedToken.Claims.(*CustomerInfo); ok && DecodedToken.Valid {
|
||||
fmt.Printf("NAME:%v ,ID:%v\n", claims.Name, claims.ID)
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
A JSON Web Token (JWT) is used for authenticating and managing users in an application.
|
||||
</p>
|
||||
<p>
|
||||
Using a hard-coded secret key for parsing JWT tokens in open source projects
|
||||
can leave the application using the token vulnerable to authentication bypasses.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A JWT token is safe for enforcing authentication and access control as long as it can't be forged by a malicious actor. However, when a project exposes this secret publicly, these seemingly unforgeable tokens can now be easily forged.
|
||||
Since the authentication as well as access control is typically enforced through these JWT tokens, an attacker armed with the secret can create a valid authentication token for any user and may even gain access to other privileged parts of the application.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Generating a cryptographically secure secret key during application initialization and using this generated key for future JWT parsing requests can prevent this vulnerability.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following code uses a hard-coded string as a secret for parsing user provided JWTs. In this case, an attacker can very easily forge a token by using the hard-coded secret.
|
||||
</p>
|
||||
|
||||
<sample src="ExampleBad.go" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
CVE-2022-0664:
|
||||
<a href="https://nvd.nist.gov/vuln/detail/CVE-2022-0664">Use of Hard-coded Cryptographic Key in Go github.com/gravitl/netmaker prior to 0.8.5,0.9.4,0.10.0,0.10.1. </a>
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* @name Decoding JWT with hardcoded key
|
||||
* @description Decoding JWT Secret with a Constant value lead to authentication or authorization bypass
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id go/parse-jwt-with-hardcoded-key
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-321
|
||||
*/
|
||||
|
||||
import go
|
||||
import experimental.frameworks.JWT
|
||||
|
||||
module JwtParseWithConstantKeyConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof StringLit }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// first part is the JWT Parsing Functions that get a func type as an argument
|
||||
// Find a node that has flow to a key Function argument
|
||||
// then find the first result node of this Function which is the secret key
|
||||
exists(FuncDef fd, DataFlow::Node n, DataFlow::ResultNode rn |
|
||||
fd = n.asExpr()
|
||||
or
|
||||
n = fd.(FuncDecl).getFunction().getARead()
|
||||
|
|
||||
GolangJwtKeyFunc::flow(n, _) and
|
||||
sink = rn and
|
||||
rn.getRoot() = fd and
|
||||
rn.getIndex() = 0
|
||||
)
|
||||
or
|
||||
// second part is the JWT Parsing Functions that get a string or byte as an argument
|
||||
sink = any(JwtParse jp).getKeyArg()
|
||||
}
|
||||
}
|
||||
|
||||
module GolangJwtKeyFuncConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source = any(Function f).getARead()
|
||||
or
|
||||
source.asExpr() = any(FuncDef fd)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(JwtParseWithKeyFunction parseJwt).getKeyFuncArg()
|
||||
}
|
||||
}
|
||||
|
||||
module JwtParseWithConstantKey = TaintTracking::Global<JwtParseWithConstantKeyConfig>;
|
||||
|
||||
module GolangJwtKeyFunc = TaintTracking::Global<GolangJwtKeyFuncConfig>;
|
||||
|
||||
import JwtParseWithConstantKey::PathGraph
|
||||
|
||||
from JwtParseWithConstantKey::PathNode source, JwtParseWithConstantKey::PathNode sink
|
||||
where JwtParseWithConstantKey::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This $@.", source.getNode(),
|
||||
"Constant Key is used as JWT Secret key"
|
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// BAD: only decode jwt without verification
|
||||
notVerifyJWT(token)
|
||||
|
||||
// GOOD: decode with verification or verify plus decode
|
||||
notVerifyJWT(token)
|
||||
VerifyJWT(token)
|
||||
}
|
||||
|
||||
func notVerifyJWT(signedToken string) {
|
||||
fmt.Println("only decoding JWT")
|
||||
DecodedToken, _, err := jwt.NewParser().ParseUnverified(signedToken, &CustomerInfo{})
|
||||
if claims, ok := DecodedToken.Claims.(*CustomerInfo); ok {
|
||||
fmt.Printf("DecodedToken:%v\n", claims)
|
||||
} else {
|
||||
log.Fatal("error", err)
|
||||
}
|
||||
}
|
||||
func LoadJwtKey(token *jwt.Token) (interface{}, error) {
|
||||
return ARandomJwtKey, nil
|
||||
}
|
||||
func verifyJWT(signedToken string) {
|
||||
fmt.Println("verifying JWT")
|
||||
DecodedToken, err := jwt.ParseWithClaims(signedToken, &CustomerInfo{}, LoadJwtKey)
|
||||
if claims, ok := DecodedToken.Claims.(*CustomerInfo); ok && DecodedToken.Valid {
|
||||
fmt.Printf("NAME:%v ,ID:%v\n", claims.Name, claims.ID)
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
A JSON Web Token (JWT) is used for authenticating and managing users in an application.
|
||||
</p>
|
||||
<p>
|
||||
Only Decoding JWTs without checking if they have a valid signature or not can lead to security vulnerabilities.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Don't use methods that only decode JWT, Instead use methods that verify the signature of JWT.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
In the following code you can see an Example from a popular Library.
|
||||
</p>
|
||||
|
||||
<sample src="Example.go" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://github.com/argoproj/argo-cd/security/advisories/GHSA-q9hr-j4rf-8fjc">JWT audience claim is not verified</a>
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* @name Use of JWT Methods that only decode user provided Token
|
||||
* @description Using JWT methods without verification can cause to authorization or authentication bypass
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id go/parse-jwt-without-verification
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-321
|
||||
*/
|
||||
|
||||
import go
|
||||
import experimental.frameworks.JWT
|
||||
|
||||
module WithValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(JwtParse jwtParse).getTokenArg() or
|
||||
sink = any(JwtParseWithKeyFunction jwtParseWithKeyFunction).getTokenArg()
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
golangJwtIsAdditionalFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
goJoseIsAdditionalFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
module NoValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof UntrustedFlowSource and
|
||||
not WithValidation::flow(source, _)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = any(JwtUnverifiedParse parseUnverified).getTokenArg()
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
golangJwtIsAdditionalFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
goJoseIsAdditionalFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
module WithValidation = TaintTracking::Global<WithValidationConfig>;
|
||||
|
||||
module NoValidation = TaintTracking::Global<NoValidationConfig>;
|
||||
|
||||
import NoValidation::PathGraph
|
||||
|
||||
from NoValidation::PathNode source, NoValidation::PathNode sink
|
||||
where NoValidation::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"This JWT is parsed without verification and received from $@.", source.getNode(),
|
||||
"this user-controlled source"
|
|
@ -0,0 +1,265 @@
|
|||
import go
|
||||
|
||||
/**
|
||||
* A abstract class which responsible for parsing a JWT token.
|
||||
*/
|
||||
abstract class JwtParseBase extends Function {
|
||||
/**
|
||||
* Gets argument number that responsible for JWT
|
||||
*
|
||||
* `-1` means the receiver is a argument node that responsible for JWT.
|
||||
* In this case, we must declare some additional taint steps.
|
||||
*/
|
||||
abstract int getTokenArgNum();
|
||||
|
||||
/**
|
||||
* Gets Argument as DataFlow node that responsible for JWT
|
||||
*/
|
||||
DataFlow::Node getTokenArg() {
|
||||
this.getTokenArgNum() != -1 and result = this.getACall().getArgument(this.getTokenArgNum())
|
||||
or
|
||||
this.getTokenArgNum() = -1 and result = this.getACall().getReceiver()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A abstract class which responsible for parsing a JWT token which the key parameter is a function type.
|
||||
*
|
||||
* Extends this class for Jwt parsing methods that accepts a function type as key.
|
||||
*/
|
||||
abstract class JwtParseWithKeyFunction extends JwtParseBase {
|
||||
/**
|
||||
* Gets argument number that responsible for a function returning the secret key
|
||||
*/
|
||||
abstract int getKeyFuncArgNum();
|
||||
|
||||
/**
|
||||
* Gets Argument as DataFlow node that responsible for a function returning the secret key
|
||||
*/
|
||||
DataFlow::Node getKeyFuncArg() { result = this.getACall().getArgument(this.getKeyFuncArgNum()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A abstract class which responsible for parsing a JWT token which the key parameter can be a string or byte type.
|
||||
*
|
||||
* Extends this class for Jwt parsing methods that accepts a non-function type as key.
|
||||
*/
|
||||
abstract class JwtParse extends JwtParseBase {
|
||||
/**
|
||||
* Gets argument number that responsible for secret key
|
||||
*/
|
||||
abstract int getKeyArgNum();
|
||||
|
||||
/**
|
||||
* Gets Argument as DataFlow node that responsible for secret key
|
||||
*/
|
||||
DataFlow::Node getKeyArg() { result = this.getACall().getArgument(this.getKeyArgNum()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A abstract class which responsible for parsing a JWT without verifying it
|
||||
*
|
||||
* Extends this class for Jwt parsing methods that don't verify JWT signature
|
||||
*/
|
||||
abstract class JwtUnverifiedParse extends JwtParseBase { }
|
||||
|
||||
/**
|
||||
* Gets `github.com/golang-jwt/jwt` and `github.com/dgrijalva/jwt-go`(previous name of `golang-jwt`) JWT packages
|
||||
*/
|
||||
string golangJwtPackage() {
|
||||
result = package(["github.com/golang-jwt/jwt", "github.com/dgrijalva/jwt-go"], "")
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that contains the following function and method:
|
||||
*
|
||||
* func (p *Parser) Parse(tokenString string, keyFunc Keyfunc)
|
||||
*
|
||||
* func Parse(tokenString string, keyFunc Keyfunc)
|
||||
*/
|
||||
class GolangJwtParse extends JwtParseWithKeyFunction {
|
||||
GolangJwtParse() {
|
||||
exists(Function f | f.hasQualifiedName(golangJwtPackage(), "Parse") | this = f)
|
||||
or
|
||||
exists(Method f | f.hasQualifiedName(golangJwtPackage(), "Parser", "Parse") | this = f)
|
||||
}
|
||||
|
||||
override int getKeyFuncArgNum() { result = 1 }
|
||||
|
||||
override int getTokenArgNum() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that contains the following function and method:
|
||||
*
|
||||
* func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc)
|
||||
*
|
||||
* func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc)
|
||||
*/
|
||||
class GolangJwtParseWithClaims extends JwtParseWithKeyFunction {
|
||||
GolangJwtParseWithClaims() {
|
||||
exists(Function f | f.hasQualifiedName(golangJwtPackage(), "ParseWithClaims") | this = f)
|
||||
or
|
||||
exists(Method f | f.hasQualifiedName(golangJwtPackage(), "Parser", "ParseWithClaims") |
|
||||
this = f
|
||||
)
|
||||
}
|
||||
|
||||
override int getKeyFuncArgNum() { result = 2 }
|
||||
|
||||
override int getTokenArgNum() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that contains the following method:
|
||||
*
|
||||
* func (p *Parser) ParseUnverified(tokenString string, claims Claims)
|
||||
*/
|
||||
class GolangJwtParseUnverified extends JwtUnverifiedParse {
|
||||
GolangJwtParseUnverified() {
|
||||
exists(Method f | f.hasQualifiedName(golangJwtPackage(), "Parser", "ParseUnverified") |
|
||||
this = f
|
||||
)
|
||||
}
|
||||
|
||||
override int getTokenArgNum() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `github.com/golang-jwt/jwt` and `github.com/dgrijalva/jwt-go`(previous name of `golang-jwt`) JWT packages
|
||||
*/
|
||||
string golangJwtRequestPackage() {
|
||||
result = package(["github.com/golang-jwt/jwt", "github.com/dgrijalva/jwt-go"], "request")
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that contains the following function:
|
||||
*
|
||||
* func ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfunc, options ...ParseFromRequestOption)
|
||||
*/
|
||||
class GolangJwtParseFromRequest extends JwtParseWithKeyFunction {
|
||||
GolangJwtParseFromRequest() {
|
||||
exists(Function f | f.hasQualifiedName(golangJwtRequestPackage(), "ParseFromRequest") |
|
||||
this = f
|
||||
)
|
||||
}
|
||||
|
||||
override int getKeyFuncArgNum() { result = 2 }
|
||||
|
||||
override int getTokenArgNum() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that contains the following function:
|
||||
*
|
||||
* func ParseFromRequestWithClaims(req *http.Request, extractor Extractor, claims jwt.Claims, keyFunc jwt.Keyfunc)
|
||||
*/
|
||||
class GolangJwtParseFromRequestWithClaims extends JwtParseWithKeyFunction {
|
||||
GolangJwtParseFromRequestWithClaims() {
|
||||
exists(Function f |
|
||||
f.hasQualifiedName(golangJwtRequestPackage(), "ParseFromRequestWithClaims")
|
||||
|
|
||||
this = f
|
||||
)
|
||||
}
|
||||
|
||||
override int getKeyFuncArgNum() { result = 3 }
|
||||
|
||||
override int getTokenArgNum() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `gopkg.in/square/go-jose` and `github.com/go-jose/go-jose` jwt package
|
||||
*/
|
||||
string goJoseJwtPackage() {
|
||||
result = package(["gopkg.in/square/go-jose", "github.com/go-jose/go-jose"], "jwt")
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that contains the following method:
|
||||
*
|
||||
* func (t *JSONWebToken) Claims(key interface{}, dest ...interface{})
|
||||
*/
|
||||
class GoJoseParseWithClaims extends JwtParse {
|
||||
GoJoseParseWithClaims() {
|
||||
exists(Method f | f.hasQualifiedName(goJoseJwtPackage(), "JSONWebToken", "Claims") | this = f)
|
||||
}
|
||||
|
||||
override int getKeyArgNum() { result = 0 }
|
||||
|
||||
override int getTokenArgNum() { result = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that contains the following method:
|
||||
*
|
||||
* func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{})
|
||||
*/
|
||||
class GoJoseUnsafeClaims extends JwtUnverifiedParse {
|
||||
GoJoseUnsafeClaims() {
|
||||
exists(Method f |
|
||||
f.hasQualifiedName(goJoseJwtPackage(), "JSONWebToken", "UnsafeClaimsWithoutVerification")
|
||||
|
|
||||
this = f
|
||||
)
|
||||
}
|
||||
|
||||
override int getTokenArgNum() { result = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for general additional steps related to parsing the secret keys in `golang-jwt/jwt`,`dgrijalva/jwt-go` packages
|
||||
*/
|
||||
predicate golangJwtIsAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(Function f, DataFlow::CallNode call |
|
||||
(
|
||||
f.hasQualifiedName(package("github.com/golang-jwt/jwt", ""),
|
||||
[
|
||||
"ParseECPrivateKeyFromPEM", "ParseECPublicKeyFromPEM", "ParseEdPrivateKeyFromPEM",
|
||||
"ParseEdPublicKeyFromPEM", "ParseRSAPrivateKeyFromPEM", "ParseRSAPublicKeyFromPEM",
|
||||
"RegisterSigningMethod"
|
||||
]) or
|
||||
f.hasQualifiedName(package("github.com/dgrijalva/jwt-go", ""),
|
||||
[
|
||||
"ParseECPrivateKeyFromPEM", "ParseECPublicKeyFromPEM", "ParseRSAPrivateKeyFromPEM",
|
||||
"ParseRSAPrivateKeyFromPEMWithPassword", "ParseRSAPublicKeyFromPEM"
|
||||
])
|
||||
) and
|
||||
call = f.getACall() and
|
||||
nodeFrom = call.getArgument(0) and
|
||||
nodeTo = call.getResult(0)
|
||||
or
|
||||
(
|
||||
f instanceof GolangJwtParse
|
||||
or
|
||||
f instanceof GolangJwtParseWithClaims
|
||||
) and
|
||||
call = f.getACall() and
|
||||
nodeFrom = call.getArgument(0) and
|
||||
nodeTo = call.getResult(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for general additioanl steps related to parsing the secret keys in `go-jose` package
|
||||
*/
|
||||
predicate goJoseIsAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(Function f, DataFlow::CallNode call |
|
||||
f.hasQualifiedName(goJoseJwtPackage(), ["ParseEncrypted", "ParseSigned"]) and
|
||||
call = f.getACall() and
|
||||
nodeFrom = call.getArgument(0) and
|
||||
nodeTo = call.getResult(0)
|
||||
)
|
||||
or
|
||||
exists(Method m, DataFlow::CallNode call |
|
||||
m.hasQualifiedName(goJoseJwtPackage(), "NestedJSONWebToken", "ParseSignedAndEncrypted") and
|
||||
call = m.getACall() and
|
||||
nodeFrom = call.getArgument(0) and
|
||||
nodeTo = call.getResult(0)
|
||||
or
|
||||
m.hasQualifiedName(goJoseJwtPackage(), "NestedJSONWebToken", "Decrypt") and
|
||||
call = m.getACall() and
|
||||
nodeFrom = call.getReceiver() and
|
||||
nodeTo = call.getResult(0)
|
||||
)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
edges
|
||||
| go-jose.v3.go:13:14:13:34 | type conversion | go-jose.v3.go:24:32:24:37 | JwtKey |
|
||||
| go-jose.v3.go:13:21:13:33 | "AllYourBase" | go-jose.v3.go:13:14:13:34 | type conversion |
|
||||
| golang-jwt-v5.go:19:15:19:35 | type conversion | golang-jwt-v5.go:27:9:27:15 | JwtKey1 |
|
||||
| golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | golang-jwt-v5.go:19:15:19:35 | type conversion |
|
||||
nodes
|
||||
| go-jose.v3.go:13:14:13:34 | type conversion | semmle.label | type conversion |
|
||||
| go-jose.v3.go:13:21:13:33 | "AllYourBase" | semmle.label | "AllYourBase" |
|
||||
| go-jose.v3.go:24:32:24:37 | JwtKey | semmle.label | JwtKey |
|
||||
| golang-jwt-v5.go:19:15:19:35 | type conversion | semmle.label | type conversion |
|
||||
| golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | semmle.label | "AllYourBase" |
|
||||
| golang-jwt-v5.go:27:9:27:15 | JwtKey1 | semmle.label | JwtKey1 |
|
||||
subpaths
|
||||
#select
|
||||
| go-jose.v3.go:24:32:24:37 | JwtKey | go-jose.v3.go:13:21:13:33 | "AllYourBase" | go-jose.v3.go:24:32:24:37 | JwtKey | This $@. | go-jose.v3.go:13:21:13:33 | "AllYourBase" | Constant Key is used as JWT Secret key |
|
||||
| golang-jwt-v5.go:27:9:27:15 | JwtKey1 | golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | golang-jwt-v5.go:27:9:27:15 | JwtKey1 | This $@. | golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | Constant Key is used as JWT Secret key |
|
|
@ -0,0 +1 @@
|
|||
experimental/CWE-321-V2/HardCodedKeys.ql
|
|
@ -0,0 +1,28 @@
|
|||
package jwt
|
||||
|
||||
//go:generate depstubber -vendor github.com/go-jose/go-jose/v3/jwt JSONWebToken ParseSigned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
)
|
||||
|
||||
// NOT OK
|
||||
var JwtKey = []byte("AllYourBase")
|
||||
|
||||
func main2(r *http.Request) {
|
||||
signedToken := r.URL.Query().Get("signedToken")
|
||||
verifyJWT(signedToken)
|
||||
}
|
||||
|
||||
func verifyJWT(signedToken string) {
|
||||
fmt.Println("verifying JWT")
|
||||
DecodedToken, _ := jwt.ParseSigned(signedToken)
|
||||
out := CustomerInfo{}
|
||||
if err := DecodedToken.Claims(JwtKey, &out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%v\n", out)
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
module main
|
||||
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-jose/go-jose/v3 v3.0.0
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
)
|
|
@ -0,0 +1,38 @@
|
|||
package jwt
|
||||
|
||||
//go:generate depstubber -vendor github.com/golang-jwt/jwt/v5 RegisteredClaims,Parser,Token Parse,ParseWithClaims
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CustomerInfo struct {
|
||||
Name string
|
||||
ID int
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// BAD constant key
|
||||
var JwtKey1 = []byte("AllYourBase")
|
||||
|
||||
func main1(r *http.Request) {
|
||||
signedToken := r.URL.Query().Get("signedToken")
|
||||
verifyJWT_golangjwt(signedToken)
|
||||
}
|
||||
|
||||
func LoadJwtKey(token *jwt.Token) (interface{}, error) {
|
||||
return JwtKey1, nil
|
||||
}
|
||||
|
||||
func verifyJWT_golangjwt(signedToken string) {
|
||||
fmt.Println("verifying JWT")
|
||||
DecodedToken, err := jwt.ParseWithClaims(signedToken, &CustomerInfo{}, LoadJwtKey)
|
||||
if claims, ok := DecodedToken.Claims.(*CustomerInfo); ok && DecodedToken.Valid {
|
||||
fmt.Printf("NAME:%v ,ID:%v\n", claims.Name, claims.ID)
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
24
go/ql/test/experimental/CWE-321-V2/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go
сгенерированный
поставляемый
Normal file
24
go/ql/test/experimental/CWE-321-V2/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for github.com/go-jose/go-jose/v3/jwt, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: github.com/go-jose/go-jose/v3/jwt (exports: JSONWebToken; functions: ParseSigned)
|
||||
|
||||
// Package jwt is a stub of github.com/go-jose/go-jose/v3/jwt, generated by depstubber.
|
||||
package jwt
|
||||
|
||||
type JSONWebToken struct {
|
||||
Headers []interface{}
|
||||
}
|
||||
|
||||
func (_ *JSONWebToken) Claims(_ interface{}, _ ...interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *JSONWebToken) UnsafeClaimsWithoutVerification(_ ...interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseSigned(_ string) (*JSONWebToken, error) {
|
||||
return nil, nil
|
||||
}
|
306
go/ql/test/experimental/CWE-321-V2/vendor/github.com/golang-jwt/jwt/v5/stub.go
сгенерированный
поставляемый
Normal file
306
go/ql/test/experimental/CWE-321-V2/vendor/github.com/golang-jwt/jwt/v5/stub.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,306 @@
|
|||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for github.com/golang-jwt/jwt/v5, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: github.com/golang-jwt/jwt/v5 (exports: RegisteredClaims,Parser,Token; functions: Parse,ParseWithClaims)
|
||||
|
||||
// Package jwt is a stub of github.com/golang-jwt/jwt/v5, generated by depstubber.
|
||||
package jwt
|
||||
|
||||
import (
|
||||
time "time"
|
||||
)
|
||||
|
||||
type ClaimStrings []string
|
||||
|
||||
func (_ ClaimStrings) MarshalJSON() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *ClaimStrings) UnmarshalJSON(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Claims interface {
|
||||
GetAudience() (ClaimStrings, error)
|
||||
GetExpirationTime() (*NumericDate, error)
|
||||
GetIssuedAt() (*NumericDate, error)
|
||||
GetIssuer() (string, error)
|
||||
GetNotBefore() (*NumericDate, error)
|
||||
GetSubject() (string, error)
|
||||
}
|
||||
|
||||
type Keyfunc func(*Token) (interface{}, error)
|
||||
|
||||
type NumericDate struct {
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func (_ NumericDate) Add(_ time.Duration) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) AddDate(_ int, _ int, _ int) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) After(_ time.Time) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) AppendFormat(_ []byte, _ string) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) Before(_ time.Time) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) Clock() (int, int, int) {
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Compare(_ time.Time) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Date() (int, time.Month, int) {
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Day() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Equal(_ time.Time) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) Format(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ NumericDate) GoString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ NumericDate) GobEncode() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) Hour() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) ISOWeek() (int, int) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) In(_ *time.Location) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) IsDST() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) IsZero() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) Local() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) Location() *time.Location {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) MarshalBinary() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) MarshalJSON() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) MarshalText() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) Minute() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Month() time.Month {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Nanosecond() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Round(_ time.Duration) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) Second() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ NumericDate) Sub(_ time.Time) time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Truncate(_ time.Duration) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) UTC() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) Unix() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) UnixMicro() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) UnixMilli() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) UnixNano() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Weekday() time.Weekday {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Year() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) YearDay() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Zone() (string, int) {
|
||||
return "", 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) ZoneBounds() (time.Time, time.Time) {
|
||||
return time.Time{}, time.Time{}
|
||||
}
|
||||
|
||||
func (_ *NumericDate) GobDecode(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *NumericDate) UnmarshalBinary(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *NumericDate) UnmarshalJSON(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *NumericDate) UnmarshalText(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Parse(_ string, _ Keyfunc, _ ...ParserOption) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func ParseWithClaims(_ string, _ Claims, _ Keyfunc, _ ...ParserOption) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func (_ *Parser) DecodeSegment(_ string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *Parser) Parse(_ string, _ Keyfunc) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *Parser) ParseUnverified(_ string, _ Claims) (*Token, []string, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (_ *Parser) ParseWithClaims(_ string, _ Claims, _ Keyfunc) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type ParserOption func(*Parser)
|
||||
|
||||
type RegisteredClaims struct {
|
||||
Issuer string
|
||||
Subject string
|
||||
Audience ClaimStrings
|
||||
ExpiresAt *NumericDate
|
||||
NotBefore *NumericDate
|
||||
IssuedAt *NumericDate
|
||||
ID string
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetAudience() (ClaimStrings, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetExpirationTime() (*NumericDate, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetIssuedAt() (*NumericDate, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetIssuer() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetNotBefore() (*NumericDate, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetSubject() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type SigningMethod interface {
|
||||
Alg() string
|
||||
Sign(_ string, _ interface{}) ([]byte, error)
|
||||
Verify(_ string, _ []byte, _ interface{}) error
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
Raw string
|
||||
Method SigningMethod
|
||||
Header map[string]interface{}
|
||||
Claims Claims
|
||||
Signature []byte
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func (_ *Token) EncodeSegment(_ []byte) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ *Token) SignedString(_ interface{}) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (_ *Token) SigningString() (string, error) {
|
||||
return "", nil
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
# github.com/gin-gonic/gin v1.9.1
|
||||
## explicit
|
||||
github.com/gin-gonic/gin
|
||||
# github.com/go-jose/go-jose/v3 v3.0.0
|
||||
## explicit
|
||||
github.com/go-jose/go-jose/v3
|
||||
# github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
## explicit
|
||||
github.com/golang-jwt/jwt/v5
|
||||
# github.com/bytedance/sonic v1.9.1
|
||||
## explicit
|
||||
github.com/bytedance/sonic
|
||||
# github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
|
||||
## explicit
|
||||
github.com/chenzhuoyu/base64x
|
||||
# github.com/gabriel-vasile/mimetype v1.4.2
|
||||
## explicit
|
||||
github.com/gabriel-vasile/mimetype
|
||||
# github.com/gin-contrib/sse v0.1.0
|
||||
## explicit
|
||||
github.com/gin-contrib/sse
|
||||
# github.com/go-playground/locales v0.14.1
|
||||
## explicit
|
||||
github.com/go-playground/locales
|
||||
# github.com/go-playground/universal-translator v0.18.1
|
||||
## explicit
|
||||
github.com/go-playground/universal-translator
|
||||
# github.com/go-playground/validator/v10 v10.14.0
|
||||
## explicit
|
||||
github.com/go-playground/validator/v10
|
||||
# github.com/goccy/go-json v0.10.2
|
||||
## explicit
|
||||
github.com/goccy/go-json
|
||||
# github.com/json-iterator/go v1.1.12
|
||||
## explicit
|
||||
github.com/json-iterator/go
|
||||
# github.com/klauspost/cpuid/v2 v2.2.4
|
||||
## explicit
|
||||
github.com/klauspost/cpuid/v2
|
||||
# github.com/leodido/go-urn v1.2.4
|
||||
## explicit
|
||||
github.com/leodido/go-urn
|
||||
# github.com/mattn/go-isatty v0.0.19
|
||||
## explicit
|
||||
github.com/mattn/go-isatty
|
||||
# github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
|
||||
## explicit
|
||||
github.com/modern-go/concurrent
|
||||
# github.com/modern-go/reflect2 v1.0.2
|
||||
## explicit
|
||||
github.com/modern-go/reflect2
|
||||
# github.com/pelletier/go-toml/v2 v2.0.8
|
||||
## explicit
|
||||
github.com/pelletier/go-toml/v2
|
||||
# github.com/twitchyliquid64/golang-asm v0.15.1
|
||||
## explicit
|
||||
github.com/twitchyliquid64/golang-asm
|
||||
# github.com/ugorji/go/codec v1.2.11
|
||||
## explicit
|
||||
github.com/ugorji/go/codec
|
||||
# golang.org/x/arch v0.3.0
|
||||
## explicit
|
||||
golang.org/x/arch
|
||||
# golang.org/x/net v0.10.0
|
||||
## explicit
|
||||
golang.org/x/net
|
||||
# golang.org/x/sys v0.11.0
|
||||
## explicit
|
||||
golang.org/x/sys
|
||||
# golang.org/x/text v0.12.0
|
||||
## explicit
|
||||
golang.org/x/text
|
||||
# google.golang.org/protobuf v1.30.0
|
||||
## explicit
|
||||
google.golang.org/protobuf
|
||||
# gopkg.in/yaml.v3 v3.0.1
|
||||
## explicit
|
||||
gopkg.in/yaml.v3
|
||||
# github.com/google/go-cmp v0.5.9
|
||||
## explicit
|
||||
github.com/google/go-cmp
|
||||
# golang.org/x/crypto v0.12.0
|
||||
## explicit
|
||||
golang.org/x/crypto
|
|
@ -0,0 +1,32 @@
|
|||
edges
|
||||
| go-jose.v3.go:25:16:25:20 | selection of URL | go-jose.v3.go:25:16:25:28 | call to Query |
|
||||
| go-jose.v3.go:25:16:25:28 | call to Query | go-jose.v3.go:25:16:25:47 | call to Get |
|
||||
| go-jose.v3.go:25:16:25:47 | call to Get | go-jose.v3.go:26:15:26:25 | signedToken |
|
||||
| go-jose.v3.go:26:15:26:25 | signedToken | go-jose.v3.go:29:19:29:29 | definition of signedToken |
|
||||
| go-jose.v3.go:29:19:29:29 | definition of signedToken | go-jose.v3.go:31:37:31:47 | signedToken |
|
||||
| go-jose.v3.go:31:2:31:48 | ... := ...[0] | go-jose.v3.go:33:12:33:23 | DecodedToken |
|
||||
| go-jose.v3.go:31:37:31:47 | signedToken | go-jose.v3.go:31:2:31:48 | ... := ...[0] |
|
||||
| golang-jwt-v5.go:28:16:28:20 | selection of URL | golang-jwt-v5.go:28:16:28:28 | call to Query |
|
||||
| golang-jwt-v5.go:28:16:28:28 | call to Query | golang-jwt-v5.go:28:16:28:47 | call to Get |
|
||||
| golang-jwt-v5.go:28:16:28:47 | call to Get | golang-jwt-v5.go:29:25:29:35 | signedToken |
|
||||
| golang-jwt-v5.go:29:25:29:35 | signedToken | golang-jwt-v5.go:32:29:32:39 | definition of signedToken |
|
||||
| golang-jwt-v5.go:32:29:32:39 | definition of signedToken | golang-jwt-v5.go:34:58:34:68 | signedToken |
|
||||
nodes
|
||||
| go-jose.v3.go:25:16:25:20 | selection of URL | semmle.label | selection of URL |
|
||||
| go-jose.v3.go:25:16:25:28 | call to Query | semmle.label | call to Query |
|
||||
| go-jose.v3.go:25:16:25:47 | call to Get | semmle.label | call to Get |
|
||||
| go-jose.v3.go:26:15:26:25 | signedToken | semmle.label | signedToken |
|
||||
| go-jose.v3.go:29:19:29:29 | definition of signedToken | semmle.label | definition of signedToken |
|
||||
| go-jose.v3.go:31:2:31:48 | ... := ...[0] | semmle.label | ... := ...[0] |
|
||||
| go-jose.v3.go:31:37:31:47 | signedToken | semmle.label | signedToken |
|
||||
| go-jose.v3.go:33:12:33:23 | DecodedToken | semmle.label | DecodedToken |
|
||||
| golang-jwt-v5.go:28:16:28:20 | selection of URL | semmle.label | selection of URL |
|
||||
| golang-jwt-v5.go:28:16:28:28 | call to Query | semmle.label | call to Query |
|
||||
| golang-jwt-v5.go:28:16:28:47 | call to Get | semmle.label | call to Get |
|
||||
| golang-jwt-v5.go:29:25:29:35 | signedToken | semmle.label | signedToken |
|
||||
| golang-jwt-v5.go:32:29:32:39 | definition of signedToken | semmle.label | definition of signedToken |
|
||||
| golang-jwt-v5.go:34:58:34:68 | signedToken | semmle.label | signedToken |
|
||||
subpaths
|
||||
#select
|
||||
| go-jose.v3.go:33:12:33:23 | DecodedToken | go-jose.v3.go:25:16:25:20 | selection of URL | go-jose.v3.go:33:12:33:23 | DecodedToken | This JWT is parsed without verification and received from $@. | go-jose.v3.go:25:16:25:20 | selection of URL | this user-controlled source |
|
||||
| golang-jwt-v5.go:34:58:34:68 | signedToken | golang-jwt-v5.go:28:16:28:20 | selection of URL | golang-jwt-v5.go:34:58:34:68 | signedToken | This JWT is parsed without verification and received from $@. | golang-jwt-v5.go:28:16:28:20 | selection of URL | this user-controlled source |
|
|
@ -0,0 +1 @@
|
|||
experimental/CWE-347/ParseJWTWithoutVerification.ql
|
|
@ -0,0 +1,46 @@
|
|||
package jwt
|
||||
|
||||
//go:generate depstubber -vendor github.com/go-jose/go-jose/v3/jwt JSONWebToken ParseSigned
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CustomerInfo struct {
|
||||
Name string
|
||||
ID int
|
||||
}
|
||||
|
||||
var JwtKey = []byte("AllYourBase")
|
||||
|
||||
func jose(r *http.Request) {
|
||||
signedToken := r.URL.Query().Get("signedToken")
|
||||
// OK: first decode and then verify
|
||||
notVerifyJWT(signedToken)
|
||||
verifyJWT(signedToken)
|
||||
|
||||
// NOT OK: no verification
|
||||
signedToken = r.URL.Query().Get("signedToken")
|
||||
notVerifyJWT(signedToken)
|
||||
}
|
||||
|
||||
func notVerifyJWT(signedToken string) {
|
||||
fmt.Println("only decoding JWT")
|
||||
DecodedToken, _ := jwt.ParseSigned(signedToken)
|
||||
out := CustomerInfo{}
|
||||
if err := DecodedToken.UnsafeClaimsWithoutVerification(&out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%v\n", out)
|
||||
}
|
||||
func verifyJWT(signedToken string) {
|
||||
fmt.Println("verifying JWT")
|
||||
DecodedToken, _ := jwt.ParseSigned(signedToken)
|
||||
out := CustomerInfo{}
|
||||
if err := DecodedToken.Claims(JwtKey, &out); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%v\n", out)
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
module main
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-jose/go-jose/v3 v3.0.0
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
)
|
|
@ -0,0 +1,54 @@
|
|||
package jwt
|
||||
|
||||
//go:generate depstubber -vendor github.com/golang-jwt/jwt/v5 RegisteredClaims,Parser,Token ParseWithClaims,NewParser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CustomerInfo1 struct {
|
||||
Name string
|
||||
ID int
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// BAD constant key
|
||||
var JwtKey1 = []byte("AllYourBase")
|
||||
|
||||
func golangjwt(r *http.Request) {
|
||||
signedToken := r.URL.Query().Get("signedToken")
|
||||
// OK: first decode and then verify
|
||||
notVerifyJWT_golangjwt(signedToken)
|
||||
verifyJWT_golangjwt(signedToken)
|
||||
|
||||
// NOT OK: only unverified parse
|
||||
signedToken = r.URL.Query().Get("signedToken")
|
||||
notVerifyJWT_golangjwt(signedToken)
|
||||
}
|
||||
|
||||
func notVerifyJWT_golangjwt(signedToken string) {
|
||||
fmt.Println("only decoding JWT")
|
||||
DecodedToken, _, err := jwt.NewParser().ParseUnverified(signedToken, &CustomerInfo1{})
|
||||
if claims, ok := DecodedToken.Claims.(*CustomerInfo1); ok {
|
||||
fmt.Printf("DecodedToken:%v\n", claims)
|
||||
} else {
|
||||
log.Fatal("error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func LoadJwtKey(token *jwt.Token) (interface{}, error) {
|
||||
return JwtKey, nil
|
||||
}
|
||||
|
||||
func verifyJWT_golangjwt(signedToken string) {
|
||||
fmt.Println("verifying JWT")
|
||||
DecodedToken, err := jwt.ParseWithClaims(signedToken, &CustomerInfo1{}, LoadJwtKey)
|
||||
if claims, ok := DecodedToken.Claims.(*CustomerInfo1); ok && DecodedToken.Valid {
|
||||
fmt.Printf("NAME:%v ,ID:%v\n", claims.Name, claims.ID)
|
||||
} else {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
24
go/ql/test/experimental/CWE-347/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go
сгенерированный
поставляемый
Normal file
24
go/ql/test/experimental/CWE-347/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for github.com/go-jose/go-jose/v3/jwt, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: github.com/go-jose/go-jose/v3/jwt (exports: JSONWebToken; functions: ParseSigned)
|
||||
|
||||
// Package jwt is a stub of github.com/go-jose/go-jose/v3/jwt, generated by depstubber.
|
||||
package jwt
|
||||
|
||||
type JSONWebToken struct {
|
||||
Headers []interface{}
|
||||
}
|
||||
|
||||
func (_ *JSONWebToken) Claims(_ interface{}, _ ...interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *JSONWebToken) UnsafeClaimsWithoutVerification(_ ...interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseSigned(_ string) (*JSONWebToken, error) {
|
||||
return nil, nil
|
||||
}
|
306
go/ql/test/experimental/CWE-347/vendor/github.com/golang-jwt/jwt/v5/stub.go
сгенерированный
поставляемый
Normal file
306
go/ql/test/experimental/CWE-347/vendor/github.com/golang-jwt/jwt/v5/stub.go
сгенерированный
поставляемый
Normal file
|
@ -0,0 +1,306 @@
|
|||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for github.com/golang-jwt/jwt/v5, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: github.com/golang-jwt/jwt/v5 (exports: RegisteredClaims,Parser,Token; functions: ParseWithClaims,NewParser)
|
||||
|
||||
// Package jwt is a stub of github.com/golang-jwt/jwt/v5, generated by depstubber.
|
||||
package jwt
|
||||
|
||||
import (
|
||||
time "time"
|
||||
)
|
||||
|
||||
type ClaimStrings []string
|
||||
|
||||
func (_ ClaimStrings) MarshalJSON() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *ClaimStrings) UnmarshalJSON(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Claims interface {
|
||||
GetAudience() (ClaimStrings, error)
|
||||
GetExpirationTime() (*NumericDate, error)
|
||||
GetIssuedAt() (*NumericDate, error)
|
||||
GetIssuer() (string, error)
|
||||
GetNotBefore() (*NumericDate, error)
|
||||
GetSubject() (string, error)
|
||||
}
|
||||
|
||||
type Keyfunc func(*Token) (interface{}, error)
|
||||
|
||||
func NewParser(_ ...ParserOption) *Parser {
|
||||
return nil
|
||||
}
|
||||
|
||||
type NumericDate struct {
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func (_ NumericDate) Add(_ time.Duration) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) AddDate(_ int, _ int, _ int) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) After(_ time.Time) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) AppendFormat(_ []byte, _ string) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) Before(_ time.Time) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) Clock() (int, int, int) {
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Compare(_ time.Time) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Date() (int, time.Month, int) {
|
||||
return 0, 0, 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Day() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Equal(_ time.Time) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) Format(_ string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ NumericDate) GoString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ NumericDate) GobEncode() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) Hour() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) ISOWeek() (int, int) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) In(_ *time.Location) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) IsDST() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) IsZero() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ NumericDate) Local() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) Location() *time.Location {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) MarshalBinary() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) MarshalJSON() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) MarshalText() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ NumericDate) Minute() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Month() time.Month {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Nanosecond() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Round(_ time.Duration) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) Second() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ NumericDate) Sub(_ time.Time) time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Truncate(_ time.Duration) time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) UTC() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (_ NumericDate) Unix() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) UnixMicro() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) UnixMilli() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) UnixNano() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Weekday() time.Weekday {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Year() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) YearDay() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) Zone() (string, int) {
|
||||
return "", 0
|
||||
}
|
||||
|
||||
func (_ NumericDate) ZoneBounds() (time.Time, time.Time) {
|
||||
return time.Time{}, time.Time{}
|
||||
}
|
||||
|
||||
func (_ *NumericDate) GobDecode(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *NumericDate) UnmarshalBinary(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *NumericDate) UnmarshalJSON(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *NumericDate) UnmarshalText(_ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ParseWithClaims(_ string, _ Claims, _ Keyfunc, _ ...ParserOption) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type Parser struct{}
|
||||
|
||||
func (_ *Parser) DecodeSegment(_ string) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *Parser) Parse(_ string, _ Keyfunc) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *Parser) ParseUnverified(_ string, _ Claims) (*Token, []string, error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (_ *Parser) ParseWithClaims(_ string, _ Claims, _ Keyfunc) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type ParserOption func(*Parser)
|
||||
|
||||
type RegisteredClaims struct {
|
||||
Issuer string
|
||||
Subject string
|
||||
Audience ClaimStrings
|
||||
ExpiresAt *NumericDate
|
||||
NotBefore *NumericDate
|
||||
IssuedAt *NumericDate
|
||||
ID string
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetAudience() (ClaimStrings, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetExpirationTime() (*NumericDate, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetIssuedAt() (*NumericDate, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetIssuer() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetNotBefore() (*NumericDate, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ RegisteredClaims) GetSubject() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type SigningMethod interface {
|
||||
Alg() string
|
||||
Sign(_ string, _ interface{}) ([]byte, error)
|
||||
Verify(_ string, _ []byte, _ interface{}) error
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
Raw string
|
||||
Method SigningMethod
|
||||
Header map[string]interface{}
|
||||
Claims Claims
|
||||
Signature []byte
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func (_ *Token) EncodeSegment(_ []byte) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ *Token) SignedString(_ interface{}) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (_ *Token) SigningString() (string, error) {
|
||||
return "", nil
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
# github.com/gin-gonic/gin v1.9.1
|
||||
## explicit
|
||||
github.com/gin-gonic/gin
|
||||
# github.com/go-jose/go-jose/v3 v3.0.0
|
||||
## explicit
|
||||
github.com/go-jose/go-jose/v3
|
||||
# github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
## explicit
|
||||
github.com/golang-jwt/jwt/v5
|
||||
# github.com/bytedance/sonic v1.9.1
|
||||
## explicit
|
||||
github.com/bytedance/sonic
|
||||
# github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311
|
||||
## explicit
|
||||
github.com/chenzhuoyu/base64x
|
||||
# github.com/gabriel-vasile/mimetype v1.4.2
|
||||
## explicit
|
||||
github.com/gabriel-vasile/mimetype
|
||||
# github.com/gin-contrib/sse v0.1.0
|
||||
## explicit
|
||||
github.com/gin-contrib/sse
|
||||
# github.com/go-playground/locales v0.14.1
|
||||
## explicit
|
||||
github.com/go-playground/locales
|
||||
# github.com/go-playground/universal-translator v0.18.1
|
||||
## explicit
|
||||
github.com/go-playground/universal-translator
|
||||
# github.com/go-playground/validator/v10 v10.14.0
|
||||
## explicit
|
||||
github.com/go-playground/validator/v10
|
||||
# github.com/goccy/go-json v0.10.2
|
||||
## explicit
|
||||
github.com/goccy/go-json
|
||||
# github.com/json-iterator/go v1.1.12
|
||||
## explicit
|
||||
github.com/json-iterator/go
|
||||
# github.com/klauspost/cpuid/v2 v2.2.4
|
||||
## explicit
|
||||
github.com/klauspost/cpuid/v2
|
||||
# github.com/leodido/go-urn v1.2.4
|
||||
## explicit
|
||||
github.com/leodido/go-urn
|
||||
# github.com/mattn/go-isatty v0.0.19
|
||||
## explicit
|
||||
github.com/mattn/go-isatty
|
||||
# github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
|
||||
## explicit
|
||||
github.com/modern-go/concurrent
|
||||
# github.com/modern-go/reflect2 v1.0.2
|
||||
## explicit
|
||||
github.com/modern-go/reflect2
|
||||
# github.com/pelletier/go-toml/v2 v2.0.8
|
||||
## explicit
|
||||
github.com/pelletier/go-toml/v2
|
||||
# github.com/twitchyliquid64/golang-asm v0.15.1
|
||||
## explicit
|
||||
github.com/twitchyliquid64/golang-asm
|
||||
# github.com/ugorji/go/codec v1.2.11
|
||||
## explicit
|
||||
github.com/ugorji/go/codec
|
||||
# golang.org/x/arch v0.3.0
|
||||
## explicit
|
||||
golang.org/x/arch
|
||||
# golang.org/x/net v0.10.0
|
||||
## explicit
|
||||
golang.org/x/net
|
||||
# golang.org/x/sys v0.11.0
|
||||
## explicit
|
||||
golang.org/x/sys
|
||||
# golang.org/x/text v0.12.0
|
||||
## explicit
|
||||
golang.org/x/text
|
||||
# google.golang.org/protobuf v1.30.0
|
||||
## explicit
|
||||
google.golang.org/protobuf
|
||||
# gopkg.in/yaml.v3 v3.0.1
|
||||
## explicit
|
||||
gopkg.in/yaml.v3
|
||||
# github.com/google/go-cmp v0.5.9
|
||||
## explicit
|
||||
github.com/google/go-cmp
|
||||
# golang.org/x/crypto v0.12.0
|
||||
## explicit
|
||||
golang.org/x/crypto
|
Загрузка…
Ссылка в новой задаче