Skip to content

Commit 83e3de1

Browse files
committed
Polish documentation.
1 parent ed74bd6 commit 83e3de1

File tree

5 files changed

+68
-6
lines changed

5 files changed

+68
-6
lines changed

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
/**
2-
* @name Failure to use secure cookies
3-
* @description Insecure cookies may be sent in cleartext, which makes them vulnerable to
4-
* interception.
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.
54
* @kind problem
65
* @problem.severity error
7-
* @id py/insecure-cookie
6+
* @id py/cookie-injection
87
* @tags security
98
* external/cwe/cwe-614
109
*/

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44
<qhelp>
55

66
<overview>
7-
<p>Failing to set the 'secure' flag on a cookie can cause it to be sent in cleartext.
8-
This makes it easier for an attacker to intercept.</p>
7+
<p>Setting the 'secure' flag on a cookie to <code>False</code> can cause it to be sent in cleartext.
8+
Setting the 'httponly' flag on a cookie to <code>False</code> may allow attackers access it via JavaScript.
9+
Setting the 'samesite' flag on a cookie to <code>'None'</code> will make the cookie to be sent in third-party
10+
contexts which may be attacker-controlled.</p>
911
</overview>
1012

1113
<recommendation>
1214
<p>Always set <code>secure</code> to <code>True</code> or add "; Secure;" to the cookie's raw value.</p>
15+
<p>Always set <code>httponly</code> to <code>True</code> or add "; HttpOnly;" to the cookie's raw value.</p>
16+
<p>Always set <code>samesite</code> to <code>Lax</code> or <code>Strict</code>, or add "; SameSite=Lax;", or
17+
"; Samesite=Strict;" to the cookie's raw header value.</p>
1318
</recommendation>
1419

1520
<example>

python/ql/src/experimental/semmle/python/CookieHeader.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ import semmle.python.dataflow.new.DataFlow
77
import semmle.python.dataflow.new.TaintTracking
88
import experimental.semmle.python.Concepts
99

10+
/**
11+
* Gets a header setting a cookie.
12+
*
13+
* Given the following example:
14+
*
15+
* ```py
16+
* @app.route("/")
17+
* def flask_make_response():
18+
* resp = make_response("")
19+
* resp.headers['Set-Cookie'] = "name=value; Secure;"
20+
* return resp
21+
* ```
22+
*
23+
* * `this` would be `resp.headers['Set-Cookie'] = "name=value; Secure;"`.
24+
* * `isSecure()` predicate would succeed.
25+
* * `isHttpOnly()` predicate would fail.
26+
* * `isSameSite()` predicate would fail.
27+
* * `getName()` and `getValue()` results would be `"name=value; Secure;"`.
28+
*/
1029
class CookieHeader extends HeaderDeclaration, Cookie::Range {
1130
CookieHeader() {
1231
this instanceof HeaderDeclaration and

python/ql/src/experimental/semmle/python/frameworks/Django.qll

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,25 @@ private module PrivateDjango {
8787
override DataFlow::Node getValueArg() { result = headerInput }
8888
}
8989

90+
/**
91+
* Gets a call to `set_cookie()`.
92+
*
93+
* Given the following example:
94+
*
95+
* ```py
96+
* def django_response(request):
97+
* resp = django.http.HttpResponse()
98+
* resp.set_cookie("name", "value", secure=True, httponly=True, samesite='Lax')
99+
* return resp
100+
* ```
101+
*
102+
* * `this` would be `resp.set_cookie("name", "value", secure=False, httponly=False, samesite='None')`.
103+
* * `getName()`'s result would be `"name"`.
104+
* * `getValue()`'s result would be `"value"`.
105+
* * `isSecure()` predicate would succeed.
106+
* * `isHttpOnly()` predicate would succeed.
107+
* * `isSameSite()` predicate would succeed.
108+
*/
90109
class DjangoSetCookieCall extends DataFlow::CallCfgNode, Cookie::Range {
91110
DjangoSetCookieCall() { this = baseClassRef().getMember("set_cookie").getACall() }
92111

python/ql/src/experimental/semmle/python/frameworks/Flask.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,26 @@ module ExperimentalFlask {
8282
override DataFlow::Node getValueArg() { result.asExpr() = item.getValue() }
8383
}
8484

85+
/**
86+
* Gets a call to `set_cookie()`.
87+
*
88+
* Given the following example:
89+
*
90+
* ```py
91+
* @app.route("/")
92+
* def false():
93+
* resp = make_response()
94+
* resp.set_cookie("name", value="value", secure=True, httponly=True, samesite='Lax')
95+
* return resp
96+
* ```
97+
*
98+
* * `this` would be `resp.set_cookie("name", value="value", secure=False, httponly=False, samesite='None')`.
99+
* * `getName()`'s result would be `"name"`.
100+
* * `getValue()`'s result would be `"value"`.
101+
* * `isSecure()` predicate would succeed.
102+
* * `isHttpOnly()` predicate would succeed.
103+
* * `isSameSite()` predicate would succeed.
104+
*/
85105
class FlaskSetCookieCall extends DataFlow::CallCfgNode, Cookie::Range {
86106
FlaskSetCookieCall() {
87107
this =

0 commit comments

Comments
 (0)