Research

Research to use

Technical articles on Python application security: how attacker-controlled data travels through a running program to a SQL query, a shell command or a deserializer, and what it takes to see it happen.

Atom feed
  1. Insecure deserialization beyond pickle: yaml.load, marshal, shelve, jsonpickle, Django signed cookies

    Pickle is the best-known unsafe deserializer in Python, and it is not the only one. yaml.load with a permissive loader, marshal, shelve and jsonpickle can all build objects or run code from bytes an attacker shaped, and a signed cookie is only as safe as the serializer behind the signature. The practical rule: for anything that crosses a trust boundary, use a format that can only carry data (JSON, or YAML through safe_load), validate its shape, and treat a signature as proof of who wrote the bytes, not as a reason to load them with a format that can run code. This article takes each loader in turn, with the documented warning and the replacement.

    • python
    • insecure-deserialization
    • code-injection
    • django
  2. Path traversal in Python file handling: os.path.join, pathlib, send_file and zip extraction

    Path traversal in Python comes from building a filesystem path out of a client-supplied name and trusting the result. os.path.join and pathlib both discard the base directory when the name is absolute, neither removes .. segments, and archive members bring their own names with them. The fix is the same everywhere: resolve the final path, confirm it is still inside the directory you meant, and only then open it. This article goes through the standard library joins, the Flask and Django file responses, upload names and zip and tar extraction, with code for each.

    • python
    • path-traversal
    • flask
    • django
  3. ReDoS in Python: catastrophic backtracking in re and how to find it at runtime

    Regular expression denial of service (ReDoS) in Python happens because the re module matches by backtracking: when a pattern can split the same text in many ways and the match finally does not succeed, the engine tries every split before giving up, and the work can double with each extra character. A short string sent to a validator can then hold a CPU core for seconds or minutes. The dependable fixes are to remove the ambiguity from the pattern, cap the input length, or use an engine that does not backtrack; and because the cost depends on the input as much as the pattern, the most reliable way to find it is to measure matches as they run.

    • python
    • redos
    • django
  4. Running your pytest suite under the EyalSec Python runtime: what it reports and how to read an event

    To run a pytest suite under the EyalSec Python runtime you change one word: es-python -m pytest instead of python -m pytest. The tests run as before, and every time data from a source you switched on reaches a risky operation (a query, a command, a file path, a deserializer), the run reports an event with the value, where it entered and the call path. This tutorial walks through the commands, which sources make sense for a test suite, how to mark test input as untrusted, how to read each block of an event, and how to make CI act on the result.

    • python
    • pytest
    • ci
    • sql-injection
    • command-injection
  5. Securing a Django app against SQL injection: raw(), extra(), RawSQL, cursor.execute and the ORM edge cases

    A Django application is safe from SQL injection as long as every value reaches the database as a bound parameter and every identifier (a column, a sort key, an alias, a keyword name) is chosen by your code rather than by the client. The four places where application code writes SQL text (raw(), extra(), RawSQL and cursor.execute()) break the first rule when they are fed formatted strings, and a handful of ORM methods break the second when a request field is passed through as a name. This guide covers each one with the rule from Django's own documentation, the review question to ask, a test that catches regressions, and what runtime evidence adds.

    • python
    • django
    • sql-injection
  6. Server-side template injection in Jinja2 and Django templates: where user input meets the renderer

    Server-side template injection (SSTI) happens when user input becomes part of a template's source instead of a value in its context, so the template engine evaluates what the user wrote. In Jinja2 that can lead to code running on the server; in Django's more restricted template language it exposes whatever objects the context carries. The rule that prevents both is one line long: template text is code, so it comes from your repository, and user input goes in the context. This article shows where the two meet by accident, what each engine allows once they do, and what to do when users really do need to write templates.

    • python
    • server-side-template-injection
    • code-injection
    • flask
    • django
  7. SSRF in Python services: requests, httpx, urllib and the cloud metadata endpoint

    Server-side request forgery (SSRF) happens when a Python service fetches a URL that a client chose, so the client borrows the server's network position: it can reach localhost, private addresses and the cloud metadata endpoint at 169.254.169.254, which hands out credentials. The defence that holds is to decide on the address the connection will actually dial, on every hop, and to back that with network controls, because checks on the URL string are easy to get around. This article shows how the three common HTTP clients differ, what the metadata endpoint changes, the bypasses that matter, and code you can adapt.

    • python
    • ssrf
    • flask
    • fastapi
  8. What a RASP, an IAST and a WAF each see of the same attack: a comparison by category

    A WAF sees the HTTP request before the application does. An IAST sensor sees data moving through the application code it instruments while tests or traffic exercise it. A RASP agent sees the application's calls from inside the process and can stop one. Each is a different vantage point on the same attack, and each answers a different question well; what they see depends on where they stand and on what they are scoped to follow. This article walks one SQL injection through three delivery routes and describes, by category and with no product named, what each kind of tool observes, the questions worth asking about the one you run, and what a view at the operation itself adds next to them.

    • python
    • waf
    • rasp
    • iast
    • sql-injection
    • command-injection
  9. Command injection through subprocess in Python: shell=True, shlex and argument injection

    Command injection in Python usually comes from one of three mistakes: running a command through a shell with shell=True and a string built from input, quoting that string incorrectly with shlex, or passing an attacker-controlled value as an argument to a program that treats a value beginning with a dash as an option. The first is well known; the second and third catch careful developers who thought they had avoided the problem. This article covers all three, plus cases that look safe and are not, with the fix for each.

    • python
    • command-injection
  10. The Django column-alias SQL injection story: CVE-2024-42005 and CVE-2025-57833

    CVE-2024-42005 and CVE-2025-57833 are two severe SQL injection vulnerabilities in Django, the most widely deployed Python web framework. Both share one root cause: a value that an application lets a client choose is used as a column alias, and column aliases are written into the SQL as identifiers, which the database driver's parameter binding cannot protect. This article walks through what each CVE is, how the injection reaches the query, why it lives in a dependency rather than in application code, and what that means for how teams watch the code they did not write.

    • python
    • django
    • sql-injection
  11. SQL injection in Django, Flask and FastAPI in 2026: the patterns that still ship

    SQL injection still ships in Python web backends in 2026, and almost never through the ORM's ordinary filter methods. It comes in through the escape hatches (raw(), extra(), RawSQL, cursor.execute() and SQLAlchemy text()) when a query is built with string formatting, and through identifiers such as column names, sort keys and aliases, which parameter binding cannot protect at all. This article lists the patterns that keep appearing in Django, Flask and FastAPI code, shows the fix for each, and explains how to find them in an existing codebase.

    • python
    • django
    • flask
    • fastapi
    • sql-injection
  12. Taint tracking explained for Python developers

    Taint tracking is a way of following untrusted data through a program: data that enters from an untrusted source is marked "tainted," the mark travels with it as the program copies, joins and transforms it, and the program checks whether tainted data has reached a dangerous operation, called a sink. It is how you answer the question static search cannot: not "does this code call execute," but "does attacker-controlled data actually reach that execute." This article defines the four moving parts (sources, propagation, sinks, sanitizers), explains why observing it at runtime gives stronger evidence than matching a pattern, and is honest about what taint tracking cannot see.

    • python
    • taint-tracking
    • sql-injection
    • command-injection
  13. Why pickle.loads is dangerous, and where it hides in Python apps

    Loading a pickle that an attacker could have influenced is unsafe: the Python documentation states that malicious pickle data can run code while it is being loaded, which is why the module's own warning is "only unpickle data you trust." The risk in a real application is rarely a visible call on a request body. It hides inside cache backends, task queues, session stores and machine-learning model files, where a developer never typed the word pickle. This article explains the risk at a level useful for defending against it, shows where it hides in first-party Python, lists safer formats, and gives a way to find every load.

    • python
    • insecure-deserialization
    • code-injection
  14. Why a WAF cannot see a SQL injection decoded inside the app

    A WAF cannot see a SQL injection that is decoded inside the application because it inspects the request as it crosses the network edge, and the injection only becomes SQL after the application decodes the payload, assembles it from harmless pieces, or reads it from somewhere that is not an HTTP request. Nothing on the wire matches a signature, so the request passes and the dangerous string exists only inside the process.

    • python
    • sql-injection
    • waf
EyalSec Pricing Docs Security Research Vulnerabilities Security glossary About Contact עברית Login Book a live demo