Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add go generate support, upgrade JWT.qll
  • Loading branch information
am0o0 committed Sep 27, 2023
commit c78f3901287364b51d35870e144e80a49c84bf18
288 changes: 150 additions & 138 deletions go/ql/lib/semmle/go/security/JWT.qll

Large diffs are not rendered by default.

29 changes: 14 additions & 15 deletions go/ql/src/experimental/CWE-321-V2/HardCodedKeys.ql
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* @name Decoding JWT with hardcoded key
* @description Decoding JWT Secrect with a Constant value lead to authentication or authorization bypass
* @description Decoding JWT Secret with a Constant value lead to authentication or authorization bypass
* @kind path-problem
* @problem.severity error
* @id go/hardcoded-key
* @id go/parse-jwt-with-hardcoded-key
* @tags security
* experimental
* external/cwe/cwe-321
Expand All @@ -12,10 +12,13 @@
import go
import semmle.go.security.JWT

module JwtConfig implements DataFlow::ConfigSig {
module JwtPaseWithConstantKeyConfig implements DataFlow::ConfigSig {
Comment thread
am0o0 marked this conversation as resolved.
Outdated
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 |
GolangJwtKeyFunc::flow(n, _) and fd = n.asExpr()
|
Expand All @@ -31,6 +34,9 @@ module JwtConfig implements DataFlow::ConfigSig {
rn.getRoot() = fd.getFuncDecl() and
rn.getIndex() = 0
)
Comment on lines +22 to +31
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These two exists have a huge amount in common. It would be better to combine them into one exists.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think If I merge them it can be much longer that it is.

or
// second part is the JWT Parsing Functions that get a string or byte as an argument
sink = any(JwtParse jp).getKeyArg()
}
}

Expand All @@ -42,24 +48,17 @@ module GolangJwtKeyFuncConfig implements DataFlow::ConfigSig {
}

predicate isSink(DataFlow::Node sink) {
sink =
[
any(GolangJwtParse parseWithClaims).getKeyFuncArg(),
any(GolangJwtParseWithClaims parseWithClaims).getKeyFuncArg(),
any(GolangJwtParseFromRequest parseWithClaims).getKeyFuncArg(),
any(GolangJwtParseFromRequestWithClaims parseWithClaims).getKeyFuncArg(),
any(GoJoseClaims parseWithClaims).getKeyFuncArg(),
]
sink = any(JwtParseWithKeyFunction parseJWT).getKeyFuncArg()
Comment thread Fixed
}
}

module Jwt = TaintTracking::Global<JwtConfig>;
module JwtPaseWithConstantKey = TaintTracking::Global<JwtPaseWithConstantKeyConfig>;
Comment thread
am0o0 marked this conversation as resolved.
Outdated

module GolangJwtKeyFunc = TaintTracking::Global<GolangJwtKeyFuncConfig>;

import Jwt::PathGraph
import JwtPaseWithConstantKey::PathGraph

from Jwt::PathNode source, Jwt::PathNode sink
where Jwt::flowPath(source, sink)
from JwtPaseWithConstantKey::PathNode source, JwtPaseWithConstantKey::PathNode sink
where JwtPaseWithConstantKey::flowPath(source, sink)
select sink.getNode(), source, sink, "This $@.", source.getNode(),
"Constant Key is used as JWT Secret key"
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @description Using JWT methods without verification can cause to authorization or authentication bypass
* @kind path-problem
* @problem.severity error
* @id go/hardcoded-key
* @id go/parse-jwt-without-verification
* @tags security
* experimental
* external/cwe/cwe-321
Expand All @@ -16,8 +16,8 @@ module WithValidationConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }

predicate isSink(DataFlow::Node sink) {
sink = any(GolangJwtValidField parse) or
sink = any(GoJoseClaims parse).getACall().getReceiver()
sink = any(JwtParse parseUnverified).getTokenArg() or
sink = any(JwtParseWithKeyFunction parseUnverified).getTokenArg()
Comment thread
am0o0 marked this conversation as resolved.
Outdated
}

predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
Expand All @@ -34,9 +34,7 @@ module NoValidationConfig implements DataFlow::ConfigSig {
}

predicate isSink(DataFlow::Node sink) {
sink = any(GolangJwtParseUnverified parseunverified).getACall().getArgument(0)
or
sink = any(GoJoseUnsafeClaims parse).getACall().getReceiver()
sink = any(JwtUnverifiedParse parseUnverified).getTokenNode()
}

predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
Expand Down
18 changes: 12 additions & 6 deletions go/ql/test/experimental/CWE-321-V2/HardCodedKeys.expected
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
edges
| golang-jwt-v5/golang-jwt-v5.go:19:14:19:34 | type conversion | golang-jwt-v5/golang-jwt-v5.go:37:9:37:14 | JwtKey |
| golang-jwt-v5/golang-jwt-v5.go:19:21:19:33 | "AllYourBase" | golang-jwt-v5/golang-jwt-v5.go:19:14:19:34 | type conversion |
| go-jose.v3.go:11:14:11:34 | type conversion | go-jose.v3.go:23:32:23:37 | JwtKey |
| go-jose.v3.go:11:21:11:33 | "AllYourBase" | go-jose.v3.go:11:14:11: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
| golang-jwt-v5/golang-jwt-v5.go:19:14:19:34 | type conversion | semmle.label | type conversion |
| golang-jwt-v5/golang-jwt-v5.go:19:21:19:33 | "AllYourBase" | semmle.label | "AllYourBase" |
| golang-jwt-v5/golang-jwt-v5.go:37:9:37:14 | JwtKey | semmle.label | JwtKey |
| go-jose.v3.go:11:14:11:34 | type conversion | semmle.label | type conversion |
| go-jose.v3.go:11:21:11:33 | "AllYourBase" | semmle.label | "AllYourBase" |
| go-jose.v3.go:23:32:23: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
| golang-jwt-v5/golang-jwt-v5.go:37:9:37:14 | JwtKey | golang-jwt-v5/golang-jwt-v5.go:19:21:19:33 | "AllYourBase" | golang-jwt-v5/golang-jwt-v5.go:37:9:37:14 | JwtKey | This $@. | golang-jwt-v5/golang-jwt-v5.go:19:21:19:33 | "AllYourBase" | Constant Key is used as JWT Secret key |
| go-jose.v3.go:23:32:23:37 | JwtKey | go-jose.v3.go:11:21:11:33 | "AllYourBase" | go-jose.v3.go:23:32:23:37 | JwtKey | This $@. | go-jose.v3.go:11:21:11: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 |
27 changes: 27 additions & 0 deletions go/ql/test/experimental/CWE-321-V2/go-jose.v3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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"
)

var JwtKey = []byte("AllYourBase")

func main2(r *http.Request) {
// NOT OK
Comment thread
am0o0 marked this conversation as resolved.
Outdated
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)
}
39 changes: 0 additions & 39 deletions go/ql/test/experimental/CWE-321-V2/go-jose.v3/go-jose.v3.go

This file was deleted.

2 changes: 1 addition & 1 deletion go/ql/test/experimental/CWE-321-V2/go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module main

go 1.18
go 1.21

require (
github.com/gin-gonic/gin v1.9.1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package main
package jwt

//go:generate depstubber -vendor github.com/golang-jwt/jwt/v5 RegisteredClaims,Parser,Token Parse,ParseWithClaims

import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"log"
"net/http"
"os"
)

type CustomerInfo struct {
Expand All @@ -16,27 +16,18 @@ type CustomerInfo struct {
}

// BAD constant key
var JwtKey = []byte("AllYourBase")

func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
// https://pkg.go.dev/github.com/go-jose/go-jose/v3/jwt
var unsignedToken = c.Param("customerName")
signedToken := c.Param("signedToken")
VerifyJWT(signedToken)
var JwtKey1 = []byte("AllYourBase")

c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
_ = router.Run()
func main1(r *http.Request) {
signedToken := r.URL.Query().Get("signedToken")
verifyJWT_golangjwt(signedToken)
}

func LoadJwtKey(token *jwt.Token) (interface{}, error) {
return JwtKey, nil
return JwtKey1, nil
}
func verifyJWT(signedToken string) {

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 {
Expand All @@ -45,4 +36,3 @@ func verifyJWT(signedToken string) {
log.Fatal(err)
}
}

Loading