Skip to content

Commit 58689c9

Browse files
Merge pull request #16893 from joefarebrother/python-cookie-injectio-promote
Python: Promote cookie injection query from experimental
2 parents 0a7772d + ebeb187 commit 58689c9

File tree

14 files changed

+179
-153
lines changed

14 files changed

+179
-153
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for detecting
3+
* "cookie injection"
4+
* vulnerabilities, as well as extension points for adding your own.
5+
*/
6+
7+
private import python
8+
private import semmle.python.dataflow.new.DataFlow
9+
private import semmle.python.Concepts
10+
private import semmle.python.dataflow.new.RemoteFlowSources
11+
12+
/**
13+
* Provides default sources, sinks and sanitizers for detecting
14+
* "cookie injection"
15+
* vulnerabilities, as well as extension points for adding your own.
16+
*/
17+
module CookieInjection {
18+
/**
19+
* A data flow source for "cookie injection" vulnerabilities.
20+
*/
21+
abstract class Source extends DataFlow::Node { }
22+
23+
/**
24+
* A data flow sink for "cookie injection" vulnerabilities.
25+
*/
26+
abstract class Sink extends DataFlow::Node { }
27+
28+
/**
29+
* A sanitizer for "cookie injection" vulnerabilities.
30+
*/
31+
abstract class Sanitizer extends DataFlow::Node { }
32+
33+
/**
34+
* A source of remote user input, considered as a flow source.
35+
*/
36+
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
37+
38+
/**
39+
* A write to a cookie, considered as a sink.
40+
*/
41+
class CookieWriteSink extends Sink {
42+
CookieWriteSink() {
43+
exists(Http::Server::CookieWrite cw |
44+
this = [cw.getNameArg(), cw.getValueArg(), cw.getHeaderArg()]
45+
)
46+
}
47+
}
48+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Provides a taint-tracking configuration for detecting "cookie injection" vulnerabilities.
3+
*
4+
* Note, for performance reasons: only import this file if
5+
* `CookieInjectionFlow` is needed, otherwise
6+
* `CookieInjectionCustomizations` should be imported instead.
7+
*/
8+
9+
private import python
10+
import semmle.python.dataflow.new.DataFlow
11+
import semmle.python.dataflow.new.TaintTracking
12+
import CookieInjectionCustomizations::CookieInjection
13+
14+
/**
15+
* A taint-tracking configuration for detecting "cookie injection" vulnerabilities.
16+
*/
17+
module CookieInjectionConfig implements DataFlow::ConfigSig {
18+
predicate isSource(DataFlow::Node source) { source instanceof Source }
19+
20+
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
21+
22+
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
23+
}
24+
25+
/** Global taint-tracking for detecting "cookie injection" vulnerabilities. */
26+
module CookieInjectionFlow = TaintTracking::Global<CookieInjectionConfig>;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>Constructing cookies from user input can allow an attacker to control a user's cookie.
8+
This may lead to a session fixation attack. Additionally, client code may not expect a cookie to contain attacker-controlled data, and fail to sanitize it for common vulnerabilities such as Cross Site Scripting (XSS).
9+
An attacker manipulating the raw cookie header may additionally be able to set cookie attributes such as <code>HttpOnly</code> to insecure values.
10+
</p>
11+
</overview>
12+
13+
<recommendation>
14+
<p>Do not use raw user input to construct cookies.</p>
15+
</recommendation>
16+
17+
<example>
18+
<p>In the following cases, a cookie is constructed for a Flask response using user input. The first uses <code>set_cookie</code>,
19+
and the second sets a cookie's raw value through the <code>set-cookie</code> header.</p>
20+
<sample src="examples/CookieInjection.py" />
21+
</example>
22+
23+
<references>
24+
<li>Wikipedia - <a href="https://en.wikipedia.org/wiki/Session_fixation">Session Fixation</a>.</li>
25+
</references>
26+
27+
</qhelp>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Construction of a cookie using user-supplied input.
3+
* @description Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack.
4+
* @kind path-problem
5+
* @problem.severity warning
6+
* @precision high
7+
* @security-severity 5.0
8+
* @id py/cookie-injection
9+
* @tags security
10+
* external/cwe/cwe-20
11+
*/
12+
13+
import python
14+
import semmle.python.security.dataflow.CookieInjectionQuery
15+
import CookieInjectionFlow::PathGraph
16+
17+
from CookieInjectionFlow::PathNode source, CookieInjectionFlow::PathNode sink
18+
where CookieInjectionFlow::flowPath(source, sink)
19+
select sink.getNode(), source, sink, "Cookie is constructed from a $@.", source.getNode(),
20+
"user-supplied input"

python/ql/src/experimental/Security/CWE-614/CookieInjection.py renamed to python/ql/src/Security/CWE-020/examples/CookieInjection.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33

44
@app.route("/1")
5-
def true():
5+
def set_cookie():
66
resp = make_response()
7-
resp.set_cookie(request.args["name"],
7+
resp.set_cookie(request.args["name"], # BAD: User input is used to set the cookie's name and value
88
value=request.args["name"])
99
return resp
1010

1111

1212
@app.route("/2")
13-
def flask_make_response():
14-
resp = make_response("hello")
15-
resp.headers['Set-Cookie'] = f"{request.args['name']}={request.args['name']};"
13+
def set_cookie_header():
14+
resp = make_response()
15+
resp.headers['Set-Cookie'] = f"{request.args['name']}={request.args['name']};" # BAD: User input is used to set the raw cookie header.
1616
return resp
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: newQuery
3+
---
4+
* The `py/cookie-injection` query, originally contributed to the experimental query pack by @jorgectf, has been promoted to the main query pack. This query finds instances of cookies being constructed from user input.

python/ql/src/experimental/Security/CWE-614/CookieInjection.qhelp

Lines changed: 0 additions & 28 deletions
This file was deleted.

python/ql/src/experimental/Security/CWE-614/CookieInjection.ql

Lines changed: 0 additions & 27 deletions
This file was deleted.

python/ql/src/experimental/semmle/python/security/injection/CookieInjection.qll

Lines changed: 0 additions & 41 deletions
This file was deleted.

python/ql/test/experimental/query-tests/Security/CWE-614/CookieInjection.expected

Lines changed: 0 additions & 51 deletions
This file was deleted.

0 commit comments

Comments
 (0)