Dynamically building SQL query strings can result in broken SQL syntax and open SQL injection attacks.
When SQL queries are constructed by concatenating or formatting user-supplied values directly into the query string, the structure of the query itself can be altered by a malicious input. This rule flags calls to SQL execution functions where the query string is built using string concatenation or format operators rather than parameterized queries or prepared statements. Unlike rule {rule:python:S3649}, this rule does not perform taint analysis — it flags all dynamically formatted SQL queries as a potential risk regardless of the data source.
If any part of a dynamically formatted query string originates from untrusted input, an attacker can manipulate the query to read, modify, or delete data they should not have access to, bypass authentication checks, or in some configurations execute operating system commands.
The following code builds a SQL query by concatenating a value directly into the query string.
from django.db import models
from django.db import connection
from django.db import connections
from django.db.models.expressions import RawSQL
value = input()
class MyUser(models.Model):
name = models.CharField(max_length=200)
def query_my_user(request, params, value):
with connection.cursor() as cursor:
cursor.execute("{0}".format(value)) # Noncompliant
# https://docs.djangoproject.com/en/2.1/ref/models/expressions/#raw-sql-expressions
RawSQL("select col from %s where mycol = %s and othercol = " + value, ("test",)) # Noncompliant
# https://docs.djangoproject.com/en/2.1/ref/models/querysets/#extra
MyUser.objects.extra(
select={
'mycol': "select col from sometable here mycol = %s and othercol = " + value}, # Noncompliant
select_params=(someparam,),
},
)
from django.db import models
from django.db import connection
from django.db import connections
from django.db.models.expressions import RawSQL
value = input()
class MyUser(models.Model):
name = models.CharField(max_length=200)
def query_my_user(request, params, value):
with connection.cursor() as cursor:
cursor.execute("SELECT * FROM mytable WHERE col = %s", [value])
RawSQL("select col from %s where mycol = %s and othercol = %s", ("test", value,))
MyUser.objects.extra(
select={
'mycol': "select col from sometable where mycol = %s and othercol = %s"},
select_params=(someparam, value),
)