Files
codeql/python/ql/lib/semmle/python/security

Python Taint Tracking Library

The taint tracking library can be broken down into three parts.

  1. Specification of sources, sinks and flows.
  2. The high level query API
  3. The implementation.

Specification

There are five parts to the specification of a taint tracking query. These are:

  1. Kinds

    The Python taint tracking library supports arbitrary kinds of taint. This is useful where you want to track something related to "taint", but that is in itself not dangerous. For example, we might want to track the flow of requests objects. Request objects are not in themselves tainted, but they do contain tainted data. For example, the length or timestamp of a request may not pose a risk, but the GET or POST string probably do. So, we would want to track request objects distinctly from the request data in the GET or POST field.

  2. Sources

    Sources of taint can be added by importing a predefined sub-type of TaintSource, or defining new ones.

  3. Sinks (or vulnerabilities)

    Sinks can be add by importing a predefined sub-type of TaintSink or defining new ones.

  4. Data flow extensions

    Additional dataflow edges; node->node, node->var, var->var or var->node can be added by importing predefined extensions or by adding new ones. Additional edges can be specified by overriding DataFlowExtension::DataFlowNode or DataFlowExtension::DataFlowVariable.

  5. Taint tracking extensions

    Taint tracking extensions, where a only a particular kind of taint flows, can be added by overriding any or all of following the methods on TaintKind:

    The two general purpose extensions:

    predicate additionalTaintStep(ControlFlowNode fromnode, ControlFlowNode tonode)

    predicate additionalTaintStepVar(EssaVariable fromvar, EssaVariable var)

    And the two special purpose extensions for tainted methods or attributes. These allow simple taint-tracking extensions, without worrying about the underlying flow graph.

    TaintKind getTaintFromAttribute(string name)

    TaintKind getTaintFromMethod(string name)

The high-level query API

The TaintedNode fully describes the taint flow graph. The full graph can be expressed as:

from TaintedNode n, TaintedNode s
where s = n.getASuccessor()
select n, s

The source -> sink relation can be expressed either using TaintedNode:

from TaintedNode src, TaintedNode sink
where src.isSource() and sink.isSink() and src.getASuccessor*() = sink
select src, sink

or, using the specification API:

from TaintSource src, TaintSink sink
where src.flowsToSink(sink)
select src, sink

The implementation

The data-flow graph used by the taint-tracking library is the one created by the points-to analysis, and consists of the course data-flow graph produced by semmle/python/data-flow/SsaDefinitions.qll enhanced with precise variable flows, call graph and type information. This graph is then enhanced with additional flows specified in part 1 above. Since the call graph and points-to information is context sensitive, the taint graph must also be context sensitive.

The taint graph is a simple directed graph where each node consists of a (CFG node, context, taint) triple although it could be thought of more naturally as a number of distinct graphs, one for each input taint-kind consisting of data flow nodes, (CFG node, context) pairs, labelled with their taint.

The TrackedValue used in the implementation is not the taint kind specified by the user, but describes both the kind of taint and how that taint relates to any object referred to by a data-flow graph node or edge. Currently, only two types of taint are supported: simple taint, where the object is actually tainted; and attribute taint where a named attribute of the referred object is tainted.

Support for tainted members (both specific members of tuples and the like, and generic members for mutable collections) are likely to be added in the near future and others form are possible. The types of taints are hard-wired with no user-visible extension method at the moment.