mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #10943 from bananabr/main
Javascript/Python: Tokens built from predictable UUIDs
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import uuid
|
||||
|
||||
|
||||
class User:
|
||||
def __init__(self):
|
||||
self.token = None
|
||||
|
||||
def resetPassword(self):
|
||||
self.token = uuid.uuid1().hex
|
||||
|
||||
|
||||
user = User()
|
||||
user.resetPassword()
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
GUIDs (often called UUIDs) are widely used in modern web applications.
|
||||
One common use for UUIDs is the generation of one-time-use tokens.
|
||||
These can used for password reset, and e-mail confirmation routines, for example.
|
||||
</p>
|
||||
<p>
|
||||
There are five versions of UUIDs defined in RFC 4122.
|
||||
Out of the five, four are generated in a predictable manner.
|
||||
This means it is possible for someone to predict future UUIDs based on a sample
|
||||
generated by the target application.
|
||||
</p>
|
||||
<p>
|
||||
Version four is the only UUID version expected to be randomly generated.
|
||||
Therefore, for situations where predictable tokens are not desired (e.g. password reset tokens),
|
||||
all other versions should be avoided.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>When using GUIDs/UUIDs for generating tokens that should not be predictable, use version four.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>This example shows a UUID v1 being used for a password reset routine.
|
||||
</p>
|
||||
|
||||
<sample src="TokenBuiltFromUUID.py" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>UUID <a href="https://datatracker.ietf.org/doc/html/rfc4122">RFC</a>.</li>
|
||||
<li>Daniel Thatcher <i>In GUID We Trust</i> <a href="https://www.intruder.io/research/in-guid-we-trust">article</a>.</li>
|
||||
<li>UUID exploitation <a href="https://github.com/intruder-io/guidtool">tool</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @name Predictable token
|
||||
* @description Tokens used for sensitive tasks, such as, password recovery,
|
||||
* and email confirmation, should not use predictable values.
|
||||
* @kind path-problem
|
||||
* @precision medium
|
||||
* @problem.severity error
|
||||
* @security-severity 5
|
||||
* @id py/predictable-token
|
||||
* @tags security
|
||||
* external/cwe/cwe-340
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.ApiGraphs
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class PredictableResultSource extends DataFlow::Node {
|
||||
PredictableResultSource() {
|
||||
exists(API::Node uuidCallRet |
|
||||
uuidCallRet = API::moduleImport("uuid").getMember(["uuid1", "uuid3", "uuid5"]).getReturn()
|
||||
|
|
||||
this = uuidCallRet.asSource()
|
||||
or
|
||||
this = uuidCallRet.getMember(["hex", "bytes", "bytes_le"]).asSource()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TokenAssignmentValueSink extends DataFlow::Node {
|
||||
TokenAssignmentValueSink() {
|
||||
exists(string name | name.toLowerCase().matches(["%token", "%code"]) |
|
||||
exists(DefinitionNode n | n.getValue() = this.asCfgNode() | name = n.(NameNode).getId())
|
||||
or
|
||||
exists(DataFlow::AttrWrite aw | aw.getValue() = this | name = aw.getAttributeName())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TokenBuiltFromUuidConfig extends TaintTracking::Configuration {
|
||||
TokenBuiltFromUuidConfig() { this = "TokenBuiltFromUuidConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof PredictableResultSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof TokenAssignmentValueSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
call = API::builtin("str").getACall() and
|
||||
nodeFrom = call.getArg(0) and
|
||||
nodeTo = call
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, TokenBuiltFromUuidConfig config
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Token built from $@.", source.getNode(), "predictable value"
|
||||
Reference in New Issue
Block a user