Add query for detecting empty JWT token secrets#458
Conversation
|
This is not a CWE-347 vulnerability. I need to research the appropriate CWE for this PR. Will update this PR as soon as I find the right CWE. |
|
I believe this is a CWE-327 vulnerability. If that's what your team agrees with I'll go ahead and make the change in the PR. No need to do that just yet if proposed updated CWE designation is incorrect. Sorry for the confusion. |
|
In my haste I have misunderstood this vulnerability. I'm going to update the qhelp file and PR description. The misunderstanding is that when a secret that is an empty string, manipulated JWT tokens can be easily forged. This has nothing to do with keeping the JWT contents secret. Again my apologizes. UPDATE: I've updated both the PR description and qhelp file. |
smowton
left a comment
There was a problem hiding this comment.
Notes:
- Sink should be an argument, not the function itself
- Source should be the empty string expression itself, not some intermediate point on the flow path.
| exists(DataFlow::CallNode call | | ||
| call.getTarget().getPackage().getPath() = "github.com/dgrijalva/jwt-go" and | ||
| this.(DataFlow::MethodCallNode).getCalleeName() = "SignedString" | ||
| ) |
There was a problem hiding this comment.
| exists(DataFlow::CallNode call | | |
| call.getTarget().getPackage().getPath() = "github.com/dgrijalva/jwt-go" and | |
| this.(DataFlow::MethodCallNode).getCalleeName() = "SignedString" | |
| ) | |
| exists(DataFlow::MethodCallNode call | | |
| call.getTarget().hasQualifiedName("github.com/dgrijalva/jwt-go", "Token", "SignedString") | | |
| this = call.getArgument(0) | |
| ) |
|
|
||
| class JWTWeakSecretKeyVerificationSource extends DataFlow::Node { | ||
| JWTWeakSecretKeyVerificationSource() { | ||
| this.(DataFlow::MethodCallNode).getArgument(0).getAPredecessor*().getStringValue().length() = 0 |
There was a problem hiding this comment.
| this.(DataFlow::MethodCallNode).getArgument(0).getAPredecessor*().getStringValue().length() = 0 | |
| this.getStringValue().length() = 0 |
|
CWEs: this is kind of CWE-330 (insufficiently-random value), in that it's fixed when it should be generated from a cryptographically secure RNG! To get started writing tests: add a corresponding |
|
Sorry for the delay here. It's still going to take some time for me to get the testing working on my end. I have not been able to find any repositories yet that are affected by this vulnerability. |
|
I am sorry for dropping the ball on this. I am starting work on this now. |
|
This query is "unfortunately" only useful, because the go library did NOT follow the spec. Per the JWT spec, these keys should NOT even be accepted...
or this issue in a JWT library for Java: So ideally one should report this to the authors of the go library. |
|
I have the stub file generated but am unsure of where I should place the stub file. |
|
Tests and stubbed vendor packages should go to the corresponding test
folder.
- ql/test/experimental/CWE-330/JWTWeakSecretKeyVerification.qlref
- ql/test/experimental/CWE-330/JWTWeakSecretKeyVerification.go
- ql/test/experimental/CWE-330/JWTWeakSecretKeyVerification.expected
- ql/test/experimental/CWE-330/go.mod
- ql/test/experimental/CWE-330/vendor/
|
|
Closing this for now due to lack of activity; feel free to reopen if you want to get back to this. |
What Does This PR Do
When signing JWT tokens, it's important to have strong secrets. This "secret" is used to encrypt the sensitive data encoded into the JWT token. With a strong secret, JWT tokens can't easily be forged. However, if the secret is an empty string, this makes it trivially easy for a third-party actor to forget JWT tokens that will be accepted by the application as real JWT tokens.
This PR introduces a query that looks for instances of passing an empty string into jwt-go's SignedString function. If an empty string is passed into SignedString, then a JWT token will be created that can be easily forged by a third-party actor.
Anything else we should know