Skip to content

MRE tainted sql string in flask app

Vadym Riznyk requested to merge flask_raw_sql into main

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 the sast-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

Merge request reports