logo

SQL Injection - Headers - Python


Need

Prevention of SQL injection in the idClient and application headers


Context

  1. Usage of Python 3.0+ for developing Python applications
  2. Usage of Django for building web applications in Python

Description

Insecure Code Example

from django.http import HttpResponse
from django.db import connection

def get_client(request):
    idClient = request.META['HTTP_IDCLIENT']
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM clients WHERE id = %s", [idClient])
    client = cursor.fetchone()
    return HttpResponse(client)

The above code is a Django view that gets a client's information from the database. The `idClient` is taken from the HTTP headers of the request, specifically the `idClient` header. This value is then directly used in a SQL query to fetch the client’s data from the database. The vulnerability lies in the line: ```python cursor.execute("SELECT * FROM clients WHERE id = %s", [idClient]) ``` This line of code is vulnerable to SQL Injection. The application includes the `idClient` header value directly into the SQL query without any form of sanitization or parameterization. An attacker could modify the `idClient` header to include SQL code, which would then be executed by the server. This could lead to unauthorized access to data, data manipulation, data loss, or even denial of service. For example, an attacker could set the `idClient` header to `1 OR 1=1`, which would return all clients, not just the one with the specified ID. Or they could use `1; DROP TABLE clients;` to delete the entire clients table. To exploit this vulnerability, an attacker would only need to modify the HTTP headers, which can be done using many common tools or even just a web browser with developer tools enabled.

Steps

  1. Use Django's built-in ORM to interact with the database instead of raw SQL queries. This will automatically escape any SQL special characters, preventing SQL injection.
  2. Validate the 'idClient' header to ensure it only contains expected values. For example, if 'idClient' should be an integer, convert the value to an integer and handle any exceptions that occur.
  3. Use parameterized queries or prepared statements instead of string formatting to include user input in SQL queries. This ensures that user input is always treated as data, not as part of the SQL command.
  4. Limit the permissions of the database user used by the application. This user should only have the minimum permissions necessary to perform its tasks. This way, even if an attacker manages to inject SQL, they won't be able to do much damage.

Secure Code Example

from django.http import HttpResponse
from django.core.exceptions import ValidationError
from django.db import models

def get_client(request):
    idClient = request.META.get('HTTP_IDCLIENT', None)
    if idClient is None:
        return HttpResponse("Missing idClient header", status=400)
    try:
        idClient = int(idClient)
    except ValueError:
        return HttpResponse("Invalid idClient header", status=400)
    try:
        client = models.Client.objects.get(id=idClient)
    except models.Client.DoesNotExist:
        return HttpResponse("Client not found", status=404)
    return HttpResponse(client)

The updated code uses Django's built-in ORM (Object-Relational Mapping) to interact with the database. This provides a layer of abstraction between the application and the database, and automatically escapes any SQL special characters, preventing SQL injection. The 'idClient' header is first retrieved from the request. If it is not present, a 400 (Bad Request) response is returned. The value of 'idClient' is then converted to an integer. If this fails (for example, if 'idClient' is not a valid integer), a 400 response is returned. The `models.Client.objects.get(id=idClient)` line attempts to retrieve a client with the given ID from the database. If no such client exists, a 404 (Not Found) response is returned. If a client is found, it is returned in the response. This code assumes that there is a `Client` model defined in the `models` module. If this is not the case, you will need to replace `models.Client` with the appropriate model class. This code also assumes that the database user used by the application has only the minimum necessary permissions. This is a good security practice, as it limits the potential damage if an attacker manages to inject SQL.


References

  • 155 - SQL Injection - Headers

  • Last updated

    2023/09/18