python-security

Python Security - Quick Reference

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "python-security" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-python-security

Python Security - Quick Reference

When NOT to Use This Skill

  • General OWASP concepts - Use owasp or owasp-top-10 skill

  • Node.js/TypeScript security - Use base security skills

  • Java security - Use java-security skill

  • Secrets management - Use secrets-management skill

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: fastapi or django for framework-specific security documentation.

Dependency Auditing

pip-audit - Official Python audit tool

pip-audit

Safety - Check for known vulnerabilities

safety check

pip-audit with requirements file

pip-audit -r requirements.txt

Snyk for Python

snyk test

Check outdated packages

pip list --outdated

CI/CD Integration

GitHub Actions

  • name: Security audit run: | pip install pip-audit safety pip-audit safety check

Bandit - Static Analysis

Run Bandit on project

bandit -r src/

Generate JSON report

bandit -r src/ -f json -o bandit-report.json

Skip specific tests

bandit -r src/ --skip B101,B601

High severity only

bandit -r src/ -ll

Bandit Configuration (.bandit)

.bandit

skips: ['B101'] # Skip assert warnings in tests exclude_dirs: ['tests', 'venv']

Common Bandit Warnings

Code Issue Fix

B101 assert used Use proper validation in production

B105 Hardcoded password Use environment variables

B301 Pickle usage Use JSON or safer serialization

B601 Shell injection Use subprocess with list args

B608 SQL injection Use parameterized queries

SQL Injection Prevention

SQLAlchemy - Safe

SAFE - Parameterized query

from sqlalchemy import text

result = db.execute( text("SELECT * FROM users WHERE email = :email"), {"email": email} )

SAFE - ORM query

user = db.query(User).filter(User.email == email).first()

SAFE - FastAPI with SQLAlchemy

@app.get("/users/{user_id}") async def get_user(user_id: int, db: Session = Depends(get_db)): return db.query(User).filter(User.id == user_id).first()

SQLAlchemy - UNSAFE

UNSAFE - String formatting

query = f"SELECT * FROM users WHERE email = '{email}'" # NEVER! db.execute(query)

UNSAFE - % formatting

query = "SELECT * FROM users WHERE email = '%s'" % email # NEVER!

Django ORM - Safe

SAFE - ORM querysets

User.objects.filter(email=email) User.objects.get(pk=user_id)

SAFE - Raw query with params

User.objects.raw("SELECT * FROM users WHERE email = %s", [email])

SAFE - Extra with params

User.objects.extra(where=["email = %s"], params=[email])

XSS Prevention

Django Templates (Auto-escaping)

<!-- SAFE - Auto-escaped --> {{ user_input }}

<!-- UNSAFE - Marked safe --> {{ user_input|safe }} <!-- Avoid if possible -->

FastAPI/Jinja2

Configure auto-escaping

from jinja2 import Environment, select_autoescape

env = Environment( autoescape=select_autoescape(['html', 'xml']) )

Manual Sanitization

Use bleach for HTML sanitization

import bleach

clean_html = bleach.clean( user_input, tags=['p', 'b', 'i', 'a'], attributes={'a': ['href']}, strip=True )

Authentication - FastAPI

JWT with python-jose

from datetime import datetime, timedelta from jose import JWTError, jwt from passlib.context import CryptContext

SECRET_KEY = os.environ["JWT_SECRET"] ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def create_access_token(data: dict) -> str: to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

def verify_password(plain: str, hashed: str) -> bool: return pwd_context.verify(plain, hashed)

def get_password_hash(password: str) -> str: return pwd_context.hash(password)

FastAPI OAuth2 Dependency

from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def get_current_user(token: str = Depends(oauth2_scheme)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception except JWTError: raise credentials_exception

user = get_user(username)
if user is None:
    raise credentials_exception
return user

Authentication - Django

Django Settings

settings.py

PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', ]

AUTH_PASSWORD_VALIDATORS = [ {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 12}}, {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'}, {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'}, ]

SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True CSRF_COOKIE_SECURE = True

Django Rate Limiting

Using django-ratelimit

from django_ratelimit.decorators import ratelimit

@ratelimit(key='ip', rate='5/m', method='POST', block=True) def login_view(request): # login logic pass

Input Validation

FastAPI with Pydantic

from pydantic import BaseModel, EmailStr, Field, validator import re

class CreateUserRequest(BaseModel): email: EmailStr password: str = Field(..., min_length=12, max_length=128) name: str = Field(..., min_length=2, max_length=100)

@validator('password')
def password_strength(cls, v):
    if not re.search(r'[A-Z]', v):
        raise ValueError('Password must contain uppercase')
    if not re.search(r'[a-z]', v):
        raise ValueError('Password must contain lowercase')
    if not re.search(r'\d', v):
        raise ValueError('Password must contain digit')
    if not re.search(r'[@$!%*?&#x26;]', v):
        raise ValueError('Password must contain special character')
    return v

@validator('name')
def name_alphanumeric(cls, v):
    if not re.match(r"^[a-zA-Z\s\-']+$", v):
        raise ValueError('Name contains invalid characters')
    return v

Django Forms

from django import forms from django.core.validators import RegexValidator

class UserRegistrationForm(forms.Form): email = forms.EmailField(max_length=255) password = forms.CharField( min_length=12, max_length=128, widget=forms.PasswordInput ) name = forms.CharField( min_length=2, max_length=100, validators=[RegexValidator(r"^[a-zA-Z\s-']+$")] )

Command Injection Prevention

import subprocess import shlex

SAFE - Use list arguments

subprocess.run(["ls", "-la", directory], check=True)

SAFE - Use shlex.split for shell-like parsing

subprocess.run(shlex.split(f"ls -la {shlex.quote(directory)}"), check=True)

UNSAFE - shell=True with user input

subprocess.run(f"ls -la {directory}", shell=True) # NEVER!

UNSAFE - os.system

os.system(f"ls -la {directory}") # NEVER!

Secure File Upload

FastAPI

from fastapi import UploadFile, HTTPException import uuid

ALLOWED_TYPES = {"image/jpeg", "image/png", "application/pdf"} MAX_SIZE = 10 * 1024 * 1024 # 10MB

@app.post("/upload") async def upload_file(file: UploadFile): # Validate content type if file.content_type not in ALLOWED_TYPES: raise HTTPException(400, "File type not allowed")

# Read and check size
contents = await file.read()
if len(contents) > MAX_SIZE:
    raise HTTPException(400, "File too large")

# Generate safe filename
ext = Path(file.filename).suffix if file.filename else ""
safe_name = f"{uuid.uuid4()}{ext}"

# Store outside web root
file_path = UPLOAD_DIR / safe_name
async with aiofiles.open(file_path, 'wb') as f:
    await f.write(contents)

return {"filename": safe_name}

Secrets Management

import os from functools import lru_cache from pydantic_settings import BaseSettings

class Settings(BaseSettings): database_url: str jwt_secret: str api_key: str

class Config:
    env_file = ".env"
    env_file_encoding = "utf-8"

@lru_cache def get_settings(): return Settings()

Usage

settings = get_settings() db_url = settings.database_url

NEVER hardcode secrets

JWT_SECRET = "hardcoded-secret" # NEVER!

Logging Security Events

import logging from datetime import datetime

logger = logging.getLogger("security")

def log_login_attempt(username: str, success: bool, ip: str): logger.info( "Login attempt", extra={ "user": username, "success": success, "ip": ip, "timestamp": datetime.utcnow().isoformat() } )

def log_access_denied(username: str, resource: str, ip: str): logger.warning( "Access denied", extra={ "user": username, "resource": resource, "ip": ip } )

NEVER log sensitive data

logger.info(f"Password: {password}") # NEVER!

logger.info(f"Token: {token}") # NEVER!

CORS Configuration

FastAPI

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware( CORSMiddleware, allow_origins=["https://myapp.com"], # Not ["*"] in production allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["Authorization", "Content-Type"], )

Django

settings.py with django-cors-headers

CORS_ALLOWED_ORIGINS = [ "https://myapp.com", ] CORS_ALLOW_CREDENTIALS = True

Anti-Patterns

Anti-Pattern Why It's Bad Correct Approach

shell=True in subprocess Command injection Use list arguments

f-string in SQL query SQL injection Use parameterized queries

pickle.loads(user_data)

Arbitrary code execution Use JSON or validate source

eval(user_input)

Code injection Never use eval on user input

Hardcoded secrets Secret exposure Use environment variables

DEBUG=True in production Info disclosure Set DEBUG=False

{{user_input|safe}}

XSS vulnerability Avoid unless sanitized

Quick Troubleshooting

Issue Likely Cause Solution

Bandit B105 warning Hardcoded password string Move to environment variable

pip-audit finds CVE Vulnerable package Update to patched version

CORS error in browser Origin not allowed Add origin to allowed list

JWT decode fails Wrong secret or expired Check secret and expiration

Password hash mismatch Different hashing algorithms Use same context for hash/verify

Rate limit not working Middleware order wrong Add rate limiter before routes

Security Scanning Commands

Static analysis

bandit -r src/

Dependency audit

pip-audit safety check

All-in-one with Snyk

snyk test

Check for secrets

gitleaks detect trufflehog git file://.

Django security check

python manage.py check --deploy

Related Skills

  • OWASP Top 10:2025

  • OWASP General

  • Secrets Management

  • Supply Chain Security

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

rust-security

No summary provided by upstream source.

Repository SourceNeeds Review
Security

dotnet-security

No summary provided by upstream source.

Repository SourceNeeds Review
Security

spring-security

No summary provided by upstream source.

Repository SourceNeeds Review
Security

go-security

No summary provided by upstream source.

Repository SourceNeeds Review