Security

Python Security Mistakes Developers Make

Python's simplicity hides serious security traps. These 5 mistakes appear in production Python code every day — from Django apps to data pipelines — and each one can lead to data breaches, server compromise, or unauthorized access.

Affects: Django · Flask · FastAPI · Scripts · Data pipelines


1

Hardcoded Secrets

Hardcoding API keys, database passwords, or JWT secrets directly in source code is one of the most common — and most costly — Python security mistakes. Once code is committed to git, the secret is permanently in history even if deleted later.

❌ Vulnerable

import openai

openai.api_key = "sk-proj-abc123..."  # ← Exposed in git history forever

AWS_SECRET = "wJalrXUtnFEMI/K7MDENGbPxRfi..."
DB_PASSWORD = "supersecret123"

✅ Secure Fix

import os
import openai

openai.api_key = os.environ["OPENAI_API_KEY"]  # ← From environment

# Or use python-dotenv for local development:
from dotenv import load_dotenv
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")
💡

Pro tip: Add .env to .gitignore immediately. Use tools like Doppler, AWS Secrets Manager, or Vault in production. Run git secrets --scan before every commit.


2

Command Injection via subprocess

When Python code passes user input directly to a shell command, an attacker can inject arbitrary commands. This gives full access to the server — read files, exfiltrate data, install malware. It ranks #3 in OWASP Top 10 (Injection).

❌ Vulnerable

import subprocess

filename = request.args.get("file")  # User input
# Attacker sends: file=report.txt; cat /etc/passwd
result = subprocess.run(f"cat {filename}", shell=True, capture_output=True)

✅ Secure Fix

import subprocess
import os

filename = request.args.get("file")

# Pass as list — no shell interpretation
result = subprocess.run(["cat", filename], capture_output=True, text=True)

# Even better: validate the path first
safe_dir = "/var/app/reports"
safe_path = os.path.realpath(os.path.join(safe_dir, filename))
if not safe_path.startswith(safe_dir):
    raise ValueError("Path traversal detected")
result = subprocess.run(["cat", safe_path], capture_output=True)
💡

Rule: Never use shell=True with any user-controlled input. Pass command arguments as a list, not a string. See the full guide: Python Command Injection →


3

SQL Injection via String Formatting

Embedding user input directly in SQL queries via f-strings or % formatting is the #1 cause of data breaches. Attackers can bypass login, dump entire databases, or delete all data.

❌ Vulnerable

# Attacker sends: username = ' OR '1'='1
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)  # ← Full database exposed

✅ Secure Fix

# Parameterized query — always
cursor.execute(
    "SELECT * FROM users WHERE username = %s",
    (username,)  # ← Driver safely escapes all input
)

# SQLAlchemy ORM (preferred for complex apps):
user = db.session.query(User).filter_by(username=username).first()
💡

Golden rule: Never concatenate or format user input into SQL. Always use parameterized queries or an ORM. The %s placeholder works regardless of the value — even if it contains quotes or semicolons.


4

Path Traversal via User-Controlled File Paths

When a user can control a file path (download endpoint, file upload, log viewer), they can use ../ sequences to escape the intended directory and read arbitrary files like /etc/passwd or application secrets.

❌ Vulnerable

from flask import Flask, send_file, request
app = Flask(__name__)

@app.route('/download')
def download():
    filename = request.args.get('file')
    # Attacker sends: file=../../etc/passwd
    return send_file(f"/var/app/uploads/{filename}")

✅ Secure Fix

import os
from flask import abort

UPLOAD_DIR = "/var/app/uploads"

@app.route('/download')
def download():
    filename = request.args.get('file')
    # Resolve the real path and verify it's inside UPLOAD_DIR
    safe_path = os.path.realpath(os.path.join(UPLOAD_DIR, filename))
    if not safe_path.startswith(UPLOAD_DIR + os.sep):
        abort(403)  # Forbidden
    return send_file(safe_path)

5

Missing Input Validation on API Endpoints

Python APIs that accept JSON without validating structure, types, or bounds are vulnerable to unexpected data causing crashes, incorrect behavior, or business logic bypasses.

❌ Vulnerable

@app.route('/transfer', methods=['POST'])
def transfer():
    data = request.json
    amount = data['amount']   # Could be negative, string, or missing
    to_user = data['to']      # Could be any value
    # Attacker sends: {"amount": -9999, "to": "attacker"}

✅ Secure Fix

from pydantic import BaseModel, Field, validator

class TransferRequest(BaseModel):
    amount: float = Field(gt=0, le=10000)  # Must be > 0 and ≤ 10000
    to: str = Field(min_length=3, max_length=50)

    @validator('to')
    def to_must_be_valid_username(cls, v):
        if not v.isalnum():
            raise ValueError('Invalid username')
        return v

@app.route('/transfer', methods=['POST'])
def transfer():
    try:
        req = TransferRequest(**request.json)
    except ValidationError as e:
        return {"error": str(e)}, 400
    # Safe to use req.amount and req.to
💡

Use Pydantic for all API input validation — it handles type coercion, validation, and error messages automatically. FastAPI has it built in. For Django, use Django REST Framework serializers.


Python Security Review Checklist

Secrets: No API keys, passwords, or tokens in source code — use environment variables
subprocess: All subprocess calls use list form, never shell=True with user input
SQL: All database queries use parameterized queries or ORM — no string formatting
File paths: All user-controlled paths validated with os.path.realpath() + directory check
Input validation: All API inputs validated with Pydantic, DRF, or manual type/bounds checks
Dependencies: pip audit run regularly to catch vulnerable packages

Check your Python code for all of these issues

Paste any Python snippet — LearnCodeGuide detects security vulnerabilities, bugs, and quality issues automatically.

Detect Python Security Issues →

Related Guides

SQL Injection in PythonCommand Injection in PythonPath Traversal in PythonHardcoded Secrets in PythonPython Code Review Guide

Published by LearnCodeGuide Team · Last reviewed: October 2025