Communitygithub.com

django-security

Django 安全最佳实践,涵盖身份验证、授权、CSRF 保护、SQL 注入防护、XSS 防护以及安全部署配置。在审查 Django 身份验证、授权、输入处理或部署设置时使用。

django-security 是什么?

该技能提供了一套全面的 Django 安全最佳实践指南,旨在帮助开发者保护其 Django 应用程序免受常见漏洞的攻击。它涵盖了从开发到生产部署的各个环节,包括核心安全设置(如生产环境配置、密码哈希和会话管理)、身份验证和授权机制(自定义用户模型、权限控制和基于角色的访问控制),以及关键漏洞的防护(SQL 注入、XSS 和 CSRF 攻击)。此外,还提供了文件上传安全、API 安全(包括速率限制和身份验证)以及安全日志记录和部署配置的详细代码示例和指导。该技能适用于任何使用 Python Django 框架进行开发的团队,支持在代码审查、安全审计和项目初始化阶段激活,以确保应用程序的安全性。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/django-security

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Django 安全最佳实践

全面的 Django 应用程序安全指南,帮助防范常见漏洞。

何时激活

  • 设置 Django 身份验证和授权
  • 实现用户权限和角色
  • 配置生产环境安全设置
  • 审查 Django 应用程序的安全问题
  • 将 Django 应用程序部署到生产环境

核心安全设置

生产环境设置配置

# settings/production.py
import os

DEBUG = False  # 关键:在生产环境中切勿设为 True

ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')

# 安全标头
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000  # 1 年
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'

# HTTPS 和 Cookie
SESSION_COOKIE_HTTPONLY = True
CSRF_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_SAMESITE = 'Lax'

# 密钥(必须通过环境变量设置)
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
if not SECRET_KEY:
    raise ImproperlyConfigured('需要设置 DJANGO_SECRET_KEY 环境变量')

# 密码验证
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        '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',
    },
]

身份验证

自定义用户模型

# apps/users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    """用于更好安全性的自定义用户模型。"""

    email = models.EmailField(unique=True)
    phone = models.CharField(max_length=20, blank=True)

    USERNAME_FIELD = 'email'  # 使用邮箱作为用户名
    REQUIRED_FIELDS = ['username']

    class Meta:
        db_table = 'users'
        verbose_name = '用户'
        verbose_name_plural = '用户'

    def __str__(self):
        return self.email

# settings/base.py
AUTH_USER_MODEL = 'users.User'

密码哈希

# Django 默认使用 PBKDF2。为了更强的安全性:
PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.Argon2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2PasswordHasher',
    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

会话管理

# 会话配置
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 或 'db'
SESSION_CACHE_ALIAS = 'default'
SESSION_COOKIE_AGE = 3600 * 24 * 7  # 1 周
SESSION_SAVE_EVERY_REQUEST = False
SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 更好的用户体验,但安全性较低

授权

权限

# models.py
from django.db import models
from django.contrib.auth.models import Permission

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    class Meta:
        permissions = [
            ('can_publish', '可以发布帖子'),
            ('can_edit_others', '可以编辑他人的帖子'),
        ]

    def user_can_edit(self, user):
        """检查用户是否可以编辑此帖子。"""
        return self.author == user or user.has_perm('app.can_edit_others')

# views.py
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import UpdateView

class PostUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
    model = Post
    permission_required = 'app.can_edit_others'
    raise_exception = True  # 返回 403 而不是重定向

    def get_queryset(self):
        """仅允许用户编辑自己的帖子。"""
        return Post.objects.filter(author=self.request.user)

自定义权限

# permissions.py
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """仅允许所有者编辑对象。"""

    def has_object_permission(self, request, view, obj):
        # 读取权限允许任何请求
        if request.method in permissions.SAFE_METHODS:
            return True

        # 写入权限仅限所有者
        return obj.author == request.user

class IsAdminOrReadOnly(permissions.BasePermission):
    """允许管理员执行任何操作,其他人只读。"""

    def has_permission(self, request, view):
        if request.method in permissions.SAFE_METHODS:
            return True
        return request.user and request.user.is_staff

class IsVerifiedUser(permissions.BasePermission):
    """仅允许已验证的用户。"""

    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated and request.user.is_verified

基于角色的访问控制 (RBAC)

# models.py
from django.contrib.auth.models import AbstractUser, Group

class User(AbstractUser):
    ROLE_CHOICES = [
        ('admin', '管理员'),
        ('moderator', '版主'),
        ('user', '普通用户'),
    ]
    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user')

    def is_admin(self):
        return self.role == 'admin' or self.is_superuser

    def is_moderator(self):
        return self.role in ['admin', 'moderator']

# 混入类
class AdminRequiredMixin:
    """需要管理员角色的混入类。"""

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated or not request.user.is_admin():
            from django.core.exceptions import PermissionDenied
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)

SQL 注入防护

Django ORM 保护

# 良好:Django ORM 自动转义参数
def get_user(username):
    return User.objects.get(username=username)  # 安全

# 良好:对 raw() 使用参数
def search_users(query):
    return User.objects.raw('SELECT * FROM users WHERE username = %s', [query])

# 糟糕:切勿直接内插用户输入
def get_user_bad(username):
    return User.objects.raw(f'SELECT * FROM users WHERE username = {username}')  # 存在漏洞!

# 良好:使用带有适当转义的 filter
def get_users_by_email(email):
    return User.objects.filter(email__iexact=email)  # 安全

# 良好:使用 Q 对象进行复杂查询
from django.db.models import Q
def search_users_complex(query):
    return User.objects.filter(
        Q(username__icontains=query) |
        Q(email__icontains=query)
    )  # 安全

使用 raw() 时的额外安全措施

# 如果必须使用原生 SQL,始终使用参数
User.objects.raw(
    'SELECT * FROM users WHERE email = %s AND status = %s',
    [user_input_email, status]
)

XSS 防护

模板转义

{# Django 默认自动转义变量 - 安全 #}
{{ user_input }}  {# 转义后的 HTML #}

{# 仅对受信任的内容明确标记为安全 #}
{{ trusted_html|safe }}  {# 未转义 #}

{# 使用模板过滤器实现安全的 HTML #}
{{ user_input|escape }}  {# 与默认相同 #}
{{ user_input|striptags }}  {# 移除所有 HTML 标签 #}

{# JavaScript 转义 #}
<script>
    var username = {{ username|escapejs }};
</script>

安全字符串处理

from django.utils.safestring import mark_safe
from django.utils.html import escape

# 糟糕:切勿在未转义的情况下将用户输入标记为安全
def render_bad(user_input):
    return mark_safe(user_input)  # 存在漏洞!

# 良好:先转义,再标记为安全
def render_good(user_input):
    return mark_safe(escape(user_input))

# 良好:对包含变量的 HTML 使用 format_html
from django.utils.html import format_html

def greet_user(username):
    return format_html('<span class="user">{}</span>', escape(username))

HTTP 标头

# settings.py
SECURE_CONTENT_TYPE_NOSNIFF = True  # 防止 MIME 嗅探
SECURE_BROWSER_XSS_FILTER = True  # 启用 XSS 过滤器
X_FRAME_OPTIONS = 'DENY'  # 防止点击劫持

# 自定义中间件
from django.conf import settings

class SecurityHeaderMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response['X-Content-Type-Options'] = 'nosniff'
        response['X-Frame-Options'] = 'DENY'
        response['X-XSS-Protection'] = '1; mode=block'
        response['Content-Security-Policy'] = "default-src 'self'"
        return response

CSRF 保护

默认 CSRF 保护

# settings.py - CSRF 默认启用
CSRF_COOKIE_SECURE = True  # 仅通过 HTTPS 发送
CSRF_COOKIE_HTTPONLY = True  # 防止 JavaScript 访问
CSRF_COOKIE_SAMESITE = 'Lax'  # 在某些情况下防止 CSRF
CSRF_TRUSTED_ORIGINS = ['https://example.com']  # 受信任的域名

# 模板用法
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">提交</button>
</form>

# AJAX 请求
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

fetch('/api/endpoint/', {
    method: 'POST',
    headers: {
        'X-CSRFToken': getCookie('csrftoken'),
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(data)
});

豁免视图(谨慎使用)

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt  # 仅在绝对必要时使用!
def webhook_view(request):
    # 来自外部服务的 Webhook
    pass

文件上传安全

文件验证

import os
import magic  # pip install python-magic
from django.core.exceptions import ValidationError

ALLOWED_MIMES = {
    'image/jpeg', 'image/png', 'image/gif', 'application/pdf',
}

MIME_TO_EXTENSIONS = {
    'image/jpeg': {'.jpg', '.jpeg'},
    'image/png': {'.png'},
    'image/gif': {'.gif'},
    'application/pdf': {'.pdf'},
}

def validate_file_type(value):
    """使用魔数验证文件类型并交叉检查扩展名。"""
    mime = magic.from_buffer(value.read(2048), mime=True)
    value.seek(0)

    if mime not in ALLOWED_MIMES:
        raise ValidationError('不支持的文件类型。')

    ext = os.path.splitext(value.name)[1].lower()
    if ext not in MIME_TO_EXTENSIONS.get(mime, set()):
        raise ValidationError('文件扩展名与文件内容不匹配。')

def validate_file_size(value):
    """验证文件大小(最大 5MB)。"""
    if value.size > 5 * 1024 * 1024:
        raise ValidationError('文件太大。最大大小为 5MB。')

# models.py
class Document(models.Model):
    file = models.FileField(
        upload_to='documents/',
        validators=[validate_file_type, validate_file_size]
    )

对于安装 libmagic 困难的环境(例如最小化容器), 可以使用纯 Python 的 filetype 包作为替代:

import os
from django.core.exceptions import ValidationError

import filetype  # pip install filetype

ALLOWED_MIMES = {
    'image/jpeg', 'image/png', 'image/gif', 'application/pdf',
}

MIME_TO_EXTENSIONS = {
    'image/jpeg': {'.jpg', '.jpeg'},
    'image/png': {'.png'},
    'image/gif': {'.gif'},
    'application/pdf': {'.pdf'},
}

def validate_file_type(value):
    """使用魔数验证文件类型。"""
    kind = filetype.guess(value.read(2048))
    value.seek(0)

    if kind is None or kind.mime not in ALLOWED_MIMES:
        raise ValidationError('不支持的文件类型。')

    ext = os.path.splitext(value.name)[1].lower()
    if ext not in MIME_TO_EXTENSIONS.get(kind.mime, set()):
        raise ValidationError('文件扩展名与文件内容不匹配。')

安全的文件存储

# settings.py
MEDIA_ROOT = '/var/www/media/'
MEDIA_URL = '/media/'

# 在生产环境中使用单独的媒体域名
MEDIA_DOMAIN = 'https://media.example.com'

# 不要直接提供用户上传文件
# 对静态文件使用 whitenoise 或 CDN
# 对媒体文件使用单独的服务器或 S3

API 安全

速率限制

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day',
        'upload': '10/hour',
    }
}

# 自定义节流器
from rest_framework.throttling import UserRateThrottle

class BurstRateThrottle(UserRateThrottle):
    scope = 'burst'
    rate = '60/min'

class SustainedRateThrottle(UserRateThrottle):
    scope = 'sustained'
    rate = '1000/day'

API 身份验证

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
}

# views.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated

@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def secure_api_view(request):
    # 需要身份验证的端点
    pass

CORS 配置

# settings.py
INSTALLED_APPS = [
    ...
    'corsheaders',
]

CORS_ALLOWED_ORIGINS = [
    'https://example.com',
    'https://sub.example.com',
]

# 或者使用正则表达式
CORS_ALLOWED_ORIGIN_REGEXES = [
    r'^https://\w+\.example\.com$',
]

CORS_ALLOW_CREDENTIALS = True  # 如果需要 cookie

安全日志记录

# settings.py
import logging.config

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
        'security': {
            'format': '[SECURITY] {levelname} {asctime} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'verbose',
        },
        'security_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/django/security.log',
            'maxBytes': 1024 * 1024 * 10,  # 10MB
            'backupCount': 5,
            'formatter': 'security',
        },
        'auth_log': {
            'class': 'logging.handlers.WatchedFileHandler',
            'filename': '/var/log/django/auth.log',
            'formatter': 'security',
        },
    },
    'loggers': {
        'django.security': {
            'handlers': ['security_file', 'console'],
            'level': 'INFO',
            'propagate': False,
        },
        'django.auth': {
            'handlers': ['auth_log', 'console'],
            'level': 'INFO',
            'propagate': False,
        },
    },
}

安全审计日志

import logging
from functools import wraps
from django.core.exceptions import PermissionDenied

logger = logging.getLogger('django.security')

def audit_logged(view_func):
    @wraps(view_func)
    def _wrapped_view(request, *args, **kwargs):
        response = view_func(request, *args, **kwargs)
        if hasattr(request, 'user') and request.user.is_authenticated:
            logger.info(
                f"用户 {request.user.username} 访问了 {request.path}",
                extra={
                    'user_id': request.user.id,
                    'ip': request.META.get('REMOTE_ADDR'),
                    'user_agent': request.META.get('HTTP_USER_AGENT'),
                }
            )
        return response
    return _wrapped_view

部署安全

Web 服务器配置

# nginx.conf
server {
    listen 443 ssl http2;
    server_name example.com;

    # SSL 配置
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # 安全标头
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;

    # 限制请求大小
    client_max_body_size 10M;

    location /static/ {
        alias /var/www/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    location /media/ {
        alias /var/www/media/;
        expires 7d;
        add_header Cache-Control "public, immutable";
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Docker 安全

# Dockerfile
FROM python:3.11-slim

# 以非 root 用户运行
RUN useradd -m -u 1000 appuser

WORKDIR /app

# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用程序代码
COPY --chown=appuser:appuser . .

# 设置安全相关的环境变量
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=config.settings.production

USER appuser

EXPOSE 8000

CMD ["gunicorn", "config.wsgi:application", "--bind", "0.0.0.0:8000"]
# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    environment:
      - DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
      - DATABASE_URL=postgres://user:password@db:5432/dbname
      - ALLOWED_HOSTS=example.com,www.example.com
    secrets:
      - django_secret_key
    networks:
      - internal
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 512M

  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - static_volume:/app/static
      - media_volume:/app/media
    depends_on:
      - web
    ports:
      - "80:80"
      - "443:443"
    networks:
      - internal
      - external

secrets:
  django_secret_key:
    file: ./secrets/django_secret_key.txt

networks:
  internal:
    external: false
  external:
    external: true

volumes:
  static_volume:
  media_volume:

电子邮件安全性

安全电子邮件配置

# settings.py

# 使用 TLS 发送电子邮件
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')

# 设置默认发件人地址
DEFAULT_FROM_EMAIL = '[email protected]'
SERVER_EMAIL = '[email protected]'

# 对错误报告设置电子邮件
ADMINS = [('Admin', '[email protected]')]
MANAGERS = [('Manager', '[email protected]')]

# 不要在生产环境的错误电子邮件中包含敏感信息
# 使用自定义错误报告中间件
def sanitize_exception_for_email(exc_info):
    """从电子邮件报告中移除敏感信息。"""
    exc_type, exc_value, tb = exc_info
    # 安全地处理异常,不泄露路径、变量等。
    return exc_type, str(exc_value)[:100], tb

邮件中继保护

# 如果使用自定义邮件发送功能,限制其速率和内容
from django.core.mail import send_mail
from django.core.cache import cache

class EmailRateLimiter:
    """限制从特定用户发送电子邮件的速率。"""

    def __init__(self, max_emails=10, period_seconds=3600):
        self.max_emails = max_emails
        self.period_seconds = period_seconds

    def can_send(self, user_id):
        key = f'email_limit_{user_id}'
        current = cache.get(key, 0)
        if current >= self.max_emails:
            return False
        cache.set(key, current + 1, self.period_seconds)
        return True

依赖安全

安全依赖管理

# requirements.txt
# 固定版本以确保安全补丁
Django==5.0.2
psycopg2-binary==2.9.9
gunicorn==21.2.0

# 可选:使用 pip-audit 进行依赖安全审计
# pip install pip-audit
# pip-audit
# .github/workflows/security.yml
name: 安全审计

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0'  # 每周一次

jobs:
  security-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: 设置 Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - name: 安装依赖
        run: |
          python -m pip install --upgrade pip
          pip install pip-audit
      - name: 运行安全审计
        run: pip-audit

环境变量管理

# settings.py
import os
from pathlib import Path

# 安全加载环境变量
# 对于开发:使用 python-decouple 或 python-dotenv
# 对于生产:使用 Docker secrets、Kubernetes secrets 或 HashiCorp Vault

def get_env_variable(var_name, default=None):
    """安全获取环境变量,提供默认值。"""
    value = os.environ.get(var_name)
    if value is None and default is None:
        raise ImproperlyConfigured(f"环境变量 {var_name} 未设置")
    return value or default

# 保持敏感变量不可被模板系统访问
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

# 不要在模板上下文中暴露 SECRET_KEY

安全审计清单

部署前检查

  • DEBUG 设置为 False
  • SECRET_KEY 已设置且安全(建议长度 50+ 字符)
  • ALLOWED_HOSTS 已正确配置
  • HTTPS 已启用并强制使用
  • 安全标头已正确设置
  • 密码验证器已配置
  • 会话设置正确
  • CSRF 保护已启用
  • 文件上传限制已到位
  • API 速率限制已配置
  • 日志记录已配置
  • 数据库使用安全连接
  • 缓存服务器配置安全
  • 环境变量已安全存储
  • 依赖项已审计
  • CORS 已正确配置
  • CSP (内容安全策略) 已设置

定期审计项目

  • 审查并轮换 API 密钥
  • 检查并更新依赖项
  • 审查访问日志中的可疑活动
  • 测试安全标头
  • 进行渗透测试
  • 审查用户权限
  • 检查未使用的功能/端点
  • 监控异常故障模式

常见安全反模式

要避免的做法

# 反模式 1:硬编码密钥
SECRET_KEY = '让我猜猜...'  # 危险!

# 反模式 2:在生产环境中显示调试信息
if settings.DEBUG:
    return sensitive_data  # 危险!

# 反模式 3:在错误中暴露内部路径
def my_view(request):
    try:
        # ...
    except Exception as e:
        return HttpResponse(f"错误: {e}")  # 危险!

# 反模式 4:不检查权限
@login_required
def admin_panel(request):
    # 忘记检查管理员状态!
    pass

# 反模式 5:使用 pickle 进行序列化
import pickle
def load_session(request):
    data = pickle.loads(request.session.get('data'))  # 危险!

# 反模式 6:使用 eval
from django.http import HttpRequest
def eval_view(request: HttpRequest):
    result = eval(request.GET.get('expression', ''))  # 危险!
    return JsonResponse({'result': result})

替代方案

# 良好:使用环境变量
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')

# 良好:在生产环境中记录错误但不显示
import logging
logger = logging.getLogger(__name__)

def my_view(request):
    try:
        # ...
    except Exception as e:
        logger.exception("视图 my_view 中发生错误")
        return HttpResponse("发生内部错误。", status=500)

# 良好:检查权限
from django.contrib.auth.mixins import UserPassesTestMixin

class AdminRequiredView(UserPassesTestMixin, TemplateView):
    def test_func(self):
        return self.request.user.is_staff

# 良好:使用 json 代替 pickle
import json

def load_session(request):
    try:
        data = json.loads(request.session.get('data', '{}'))
    except json.JSONDecodeError:
        data = {}

# 良好:使用安全替代方案
# 将 eval 替换为 AST 解析或其他安全评估
import ast
def safe_eval(expression):
    tree = ast.parse(expression, mode='eval')
    # 仅允许安全节点
    allowed_nodes = (ast.Constant, ast.Name, ast.Expression)
    if not all(isinstance(node, allowed_nodes) for node in ast.walk(tree)):
        raise ValueError("不允许的表达式")
    return eval(compile(tree, '<string>', 'eval'))

额外资源

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

相关技能