Merge pull request #10943 from bananabr/main

Javascript/Python: Tokens built from predictable UUIDs
This commit is contained in:
Taus
2022-10-27 14:12:34 +02:00
committed by GitHub
6 changed files with 233 additions and 0 deletions

View File

@@ -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()

View File

@@ -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>

View File

@@ -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"