-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Python: Add cookie security-related queries #6360
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
RasmusWL
merged 33 commits into
github:main
from
jorgectf:jorgectf/python/insecure-cookie
May 11, 2022
Merged
Changes from all commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
0aaa9c1
Merge remote-tracking branch 'origin/jorgectf/python/headerInjection'…
jorgectf 8a3e4f1
Add tests and `.qlref`
jorgectf c8983be
Add query
jorgectf 4f68a17
Write documentation and example
jorgectf 66fdd53
Merge branch 'jorgectf/python/headerInjection' into jorgectf/python/i…
jorgectf 6504429
Add `CookieWrite` concept
jorgectf 9834659
Polish `CookieWrite`
jorgectf c8a7f48
Add `.expected`
jorgectf 54ed25a
Change `False` and `None` scopes
jorgectf 28ec8c9
Merge remote-tracking branch 'origin/main' into jorgectf/python/insec…
jorgectf 48c3c3d
Broaden scope
jorgectf 0f2b81e
Polish tests
jorgectf 5dc1ad6
Polish `.ql`
jorgectf 129edd6
Update `.expected`
jorgectf cf9e9f9
Add cookie injection query missing proper tests
jorgectf 4cb78ac
Fix typo
jorgectf d7a7946
Improve tests
jorgectf b3258ce
Add `CookieInjection` sample and `.qhelp`
jorgectf cf47e8e
Fix endpoints' naming
jorgectf a420e6e
Add `CookieInjection.qlref`
jorgectf 86aac7c
Add/Update `.expected` files.
jorgectf ed74bd6
Merge remote-tracking branch 'origin/main' into jorgectf/python/insec…
jorgectf 83e3de1
Polish documentation.
jorgectf e7d649f
Make `Cookie` concept extend `HTTP::Server::CookieWrite`
jorgectf 6ecb6d1
Adapt Django and Flask to their main modelings
jorgectf a4204cc
Avoid using `Str_` internal class
jorgectf 840cded
Avoid using `Str_` in `CookieHeader`
jorgectf d127d21
Merge branch 'main' into jorgectf/python/insecure-cookie
RasmusWL 84ad45c
Python: Fix Django import
RasmusWL a902d3d
Python: Add `security-severity` for `py/insecure-cookie`
RasmusWL 27b99c5
Python: Add placeholder precision for `py/insecure-cookie`
RasmusWL fc8633c
Python: Fix select for `py/cookie-injection`
RasmusWL cff950f
Python: Fix select of `py/insecure-cookie`
RasmusWL File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
python/ql/src/experimental/Security/CWE-614/CookieInjection.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from flask import request, make_response | ||
|
||
|
||
@app.route("/1") | ||
def true(): | ||
resp = make_response() | ||
resp.set_cookie(request.args["name"], | ||
value=request.args["name"]) | ||
return resp | ||
|
||
|
||
@app.route("/2") | ||
def flask_make_response(): | ||
resp = make_response("hello") | ||
resp.headers['Set-Cookie'] = f"{request.args['name']}={request.args['name']};" | ||
return resp |
28 changes: 28 additions & 0 deletions
28
python/ql/src/experimental/Security/CWE-614/CookieInjection.qhelp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<!DOCTYPE qhelp PUBLIC | ||
"-//Semmle//qhelp//EN" | ||
"qhelp.dtd"> | ||
<qhelp> | ||
|
||
<overview> | ||
<p>Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack. | ||
It is possible, however, to perform other parameter-like attacks through cookie poisoning techniques, | ||
such as SQL Injection, Directory Traversal, or Stealth Commanding, etc. Additionally, | ||
cookie injection may relate to attempts to perform Access of Administrative Interface. | ||
</p> | ||
</overview> | ||
|
||
<recommendation> | ||
<p>Do not use raw user input to construct cookies.</p> | ||
</recommendation> | ||
|
||
<example> | ||
<p>This example shows two ways of adding a cookie to a Flask response. The first way uses <code>set_cookie</code>'s | ||
and the second sets a cookie's raw value through a header, both using user-supplied input.</p> | ||
<sample src="CookieInjection.py" /> | ||
</example> | ||
|
||
<references> | ||
<li>Imperva: <a href="https://docs.imperva.com/bundle/on-premises-knowledgebase-reference-guide/page/cookie_injection.htm">Cookie injection</a>.</li> | ||
</references> | ||
|
||
</qhelp> |
28 changes: 28 additions & 0 deletions
28
python/ql/src/experimental/Security/CWE-614/CookieInjection.ql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/** | ||
* @name Construction of a cookie using user-supplied input. | ||
* @description Constructing cookies from user input may allow an attacker to perform a Cookie Poisoning attack. | ||
* @kind path-problem | ||
* @problem.severity error | ||
* @id py/cookie-injection | ||
* @tags security | ||
* external/cwe/cwe-614 | ||
*/ | ||
|
||
// determine precision above | ||
import python | ||
import semmle.python.dataflow.new.DataFlow | ||
import experimental.semmle.python.Concepts | ||
import experimental.semmle.python.CookieHeader | ||
import experimental.semmle.python.security.injection.CookieInjection | ||
import DataFlow::PathGraph | ||
|
||
from | ||
CookieInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, | ||
string insecure | ||
where | ||
config.hasFlowPath(source, sink) and | ||
if exists(sink.getNode().(CookieSink)) | ||
then insecure = ",and its " + sink.getNode().(CookieSink).getFlag() + " flag is not properly set." | ||
else insecure = "." | ||
select sink.getNode(), source, sink, "Cookie is constructed from a $@" + insecure, source.getNode(), | ||
"user-supplied input" |
15 changes: 15 additions & 0 deletions
15
python/ql/src/experimental/Security/CWE-614/InsecureCookie.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from flask import Flask, request, make_response, Response | ||
|
||
|
||
@app.route("/1") | ||
def true(): | ||
resp = make_response() | ||
resp.set_cookie("name", value="value", secure=True) | ||
return resp | ||
|
||
|
||
@app.route("/2") | ||
def flask_make_response(): | ||
resp = make_response("hello") | ||
resp.headers['Set-Cookie'] = "name=value; Secure;" | ||
return resp |
31 changes: 31 additions & 0 deletions
31
python/ql/src/experimental/Security/CWE-614/InsecureCookie.qhelp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<!DOCTYPE qhelp PUBLIC | ||
"-//Semmle//qhelp//EN" | ||
"qhelp.dtd"> | ||
<qhelp> | ||
|
||
<overview> | ||
<p>Setting the 'secure' flag on a cookie to <code>False</code> can cause it to be sent in cleartext. | ||
Setting the 'httponly' flag on a cookie to <code>False</code> may allow attackers access it via JavaScript. | ||
Setting the 'samesite' flag on a cookie to <code>'None'</code> will make the cookie to be sent in third-party | ||
contexts which may be attacker-controlled.</p> | ||
</overview> | ||
|
||
<recommendation> | ||
<p>Always set <code>secure</code> to <code>True</code> or add "; Secure;" to the cookie's raw value.</p> | ||
<p>Always set <code>httponly</code> to <code>True</code> or add "; HttpOnly;" to the cookie's raw value.</p> | ||
<p>Always set <code>samesite</code> to <code>Lax</code> or <code>Strict</code>, or add "; SameSite=Lax;", or | ||
"; Samesite=Strict;" to the cookie's raw header value.</p> | ||
</recommendation> | ||
|
||
<example> | ||
<p>This example shows two ways of adding a cookie to a Flask response. The first way uses <code>set_cookie</code>'s | ||
secure flag and the second adds the secure flag in the cookie's raw value.</p> | ||
<sample src="InsecureCookie.py" /> | ||
</example> | ||
|
||
<references> | ||
<li>Detectify: <a href="https://support.detectify.com/support/solutions/articles/48001048982-cookie-lack-secure-flag">Cookie lack Secure flag</a>.</li> | ||
<li>PortSwigger: <a href="https://portswigger.net/kb/issues/00500200_tls-cookie-without-secure-flag-set">TLS cookie without secure flag set</a>.</li> | ||
</references> | ||
|
||
</qhelp> |
30 changes: 30 additions & 0 deletions
30
python/ql/src/experimental/Security/CWE-614/InsecureCookie.ql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/** | ||
* @name Failure to use secure cookies | ||
* @description Insecure cookies may be sent in cleartext, which makes them vulnerable to | ||
* interception. | ||
* @kind problem | ||
* @problem.severity error | ||
* @security-severity 5.0 | ||
* @precision ??? | ||
* @id py/insecure-cookie | ||
* @tags security | ||
* external/cwe/cwe-614 | ||
*/ | ||
|
||
// TODO: determine precision above | ||
import python | ||
import semmle.python.dataflow.new.DataFlow | ||
import experimental.semmle.python.Concepts | ||
import experimental.semmle.python.CookieHeader | ||
|
||
from Cookie cookie, string alert | ||
where | ||
not cookie.isSecure() and | ||
alert = "secure" | ||
or | ||
not cookie.isHttpOnly() and | ||
alert = "httponly" | ||
or | ||
not cookie.isSameSite() and | ||
alert = "samesite" | ||
select cookie, "Cookie is added without the '" + alert + "' flag properly set." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/** | ||
* Temporary: provides a class to extend current cookies to header declarations | ||
*/ | ||
|
||
import python | ||
import semmle.python.dataflow.new.DataFlow | ||
import semmle.python.dataflow.new.TaintTracking | ||
import experimental.semmle.python.Concepts | ||
|
||
/** | ||
* Gets a header setting a cookie. | ||
* | ||
* Given the following example: | ||
* | ||
* ```py | ||
* @app.route("/") | ||
* def flask_make_response(): | ||
* resp = make_response("") | ||
* resp.headers['Set-Cookie'] = "name=value; Secure;" | ||
* return resp | ||
* ``` | ||
* | ||
* * `this` would be `resp.headers['Set-Cookie'] = "name=value; Secure;"`. | ||
* * `isSecure()` predicate would succeed. | ||
* * `isHttpOnly()` predicate would fail. | ||
* * `isSameSite()` predicate would fail. | ||
* * `getName()` and `getValue()` results would be `"name=value; Secure;"`. | ||
*/ | ||
class CookieHeader extends Cookie::Range instanceof HeaderDeclaration { | ||
CookieHeader() { | ||
this instanceof HeaderDeclaration and | ||
exists(StrConst str | | ||
str.getText() = "Set-Cookie" and | ||
DataFlow::exprNode(str) | ||
.(DataFlow::LocalSourceNode) | ||
.flowsTo(this.(HeaderDeclaration).getNameArg()) | ||
) | ||
} | ||
|
||
override predicate isSecure() { | ||
exists(StrConst str | | ||
str.getText().regexpMatch(".*; *Secure;.*") and | ||
DataFlow::exprNode(str) | ||
.(DataFlow::LocalSourceNode) | ||
.flowsTo(this.(HeaderDeclaration).getValueArg()) | ||
) | ||
} | ||
|
||
override predicate isHttpOnly() { | ||
exists(StrConst str | | ||
str.getText().regexpMatch(".*; *HttpOnly;.*") and | ||
DataFlow::exprNode(str) | ||
.(DataFlow::LocalSourceNode) | ||
.flowsTo(this.(HeaderDeclaration).getValueArg()) | ||
) | ||
} | ||
|
||
override predicate isSameSite() { | ||
exists(StrConst str | | ||
str.getText().regexpMatch(".*; *SameSite=(Strict|Lax);.*") and | ||
DataFlow::exprNode(str) | ||
.(DataFlow::LocalSourceNode) | ||
.flowsTo(this.(HeaderDeclaration).getValueArg()) | ||
) | ||
} | ||
|
||
override DataFlow::Node getNameArg() { result = this.(HeaderDeclaration).getValueArg() } | ||
|
||
override DataFlow::Node getValueArg() { result = this.(HeaderDeclaration).getValueArg() } | ||
|
||
override DataFlow::Node getHeaderArg() { none() } | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check warning
Code scanning / CodeQL
Class QLDoc style.