MRE tainted sql string in flask app
SAST Vulnerable MRE Addition or Enhancement
Background Information
Creating MRE for tainted sql string in flask app related for issue gitlab-org/gitlab#440407 (closed)
Changes Summary
TODO
Checklist
-
the MRE's dependencies are explicit and ideally pinned (e.g. using pipenv
in the case of Python) -
the MRE is dockerised and trivially runnable -
the MRE's directory in the sast-rules-apps
group (e.g.python-web-apps/django/security/injection/sql
) and the rule's directory in thesast-rules
repository are equivalent ( e.g.python/django/security/injection/sql
) -
the MRE contains a README.md which explains how to: -
run the application -
shows the normal execution path without exploitation -
shows the exploited execution path
-
Rule Evaluation
Test
$ semgrep test .
total mismatch: 0
1/1: ✓ All tests passed
No tests for fixes found.
Scan
% semgrep scan -q --config rule-flask-execute-used.yml rule-flask-execute-used.py
┌─────────────────┐
│ 6 Code Findings │
└─────────────────┘
rule-flask-execute-used.py
❯❯❱ flask-tainted-sql-string
SQL Injections are a critical type of vulnerability that can lead to data or system
compromise. By dynamically generating SQL query strings, user input may be able to
influence the logic of the SQL statement. This could lead to an adversary accessing
information they should not have access to, or in some circumstances, being able to
execute OS functionality or code.
Replace all dynamically generated SQL queries with parameterized queries. In situations
where dynamic queries must be created, never use direct user input, but instead use a map
or dictionary of valid values and resolve them using a user supplied key.
For example, some database drivers do not allow parameterized queries for `>` or `<`
comparison operators. In these cases, do not use a user supplied `>` or `<` value, but
rather have the user supply a `gt` or `lt` value. The alphabetical values are then used
to look up the `>` and `<` values to be used in the construction of the dynamic query.
The same goes for other queries where column or table names are required but cannot be
parameterized.
Data that is possible user-controlled from a python request is passed to `execute()`
function. To remediate this issue, use SQLAlchemy statements which are built with query
parameterization and therefore not vulnerable to sql injection.
If for some reason this is not feasible, ensure calls including user-supplied data pass
it in to the `params` parameter of the `execute()` method. Below is an example using
`execute()`, passing in user-supplied data as `params`. This will treat the query as a
parameterized query and `params` as strictly data, preventing any possibility of SQL
Injection.
``` name = request.args.get('name') req = text('SELECT * FROM student WHERE firstname =
:x') result = db.session.execute(req, {"x":name}) ``` For more information on QuerySets
see:
- https://docs.djangoproject.com/en/4.2/ref/models/querysets/#queryset-api
For more information on SQL Injections see OWASP:
-
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
79┆ age = db.session.execute(req)
⋮┆----------------------------------------
81┆ return db.session.execute(text(f'SELECT * FROM student WHERE age >
{list(age)[0][0]}'))
⋮┆----------------------------------------
104┆ result = db.session.execute(text("SELECT * FROM student WHERE firstname = '%s'"
% get_val(request.args)))
⋮┆----------------------------------------
125┆ result = db.session.execute(text(f'{pre_request} WHERE firstname =
\"{name}\"'))
⋮┆----------------------------------------
130┆ result = db.session.execute(text("SELECT * FROM student WHERE firstname = '%s'"
% request.args.get('name')))
⋮┆----------------------------------------
140┆ result = db.session.execute(text('SELECT * FROM student WHERE
lastname=\"{}\"'.format(sql_req)))
Edited by Vadym Riznyk