Source profileQuality 79/100

affaan-m/ECC/docs/zh-CN/skills/django-security/SKILL.md

django-security

Use it for data analysis tasks; the detail page covers purpose, installation, and practical steps.

Source repository stars
234,327
Declared platforms
0
Static risk flags
1
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

保护 Django 应用程序免受常见漏洞侵害的全面安全指南。

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "docs/zh-CN/skills/django-security"
    Safe inspection promptEditorial

    Inspect the Agent Skill "django-security" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/zh-CN/skills/django-security/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Template usage

      {% csrftoken %} {{ form.asp }} Submit

      {% csrftoken %} {{ form.asp }} Submit
    2. 02

      何时启用

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

      设置 Django 认证和授权时实现用户权限和角色时配置生产环境安全设置时
    3. 03

      核心安全设置

      Review the “核心安全设置” section in the pinned source before continuing.

      Review and apply the “核心安全设置” source section.
    4. 04

      生产环境设置配置

      Review the “生产环境设置配置” section in the pinned source before continuing.

      Review and apply the “生产环境设置配置” source section.
    5. 05

      settings/production.py

      DEBUG = False CRITICAL: Never use True in production

      DEBUG = False CRITICAL: Never use True in productionALLOWEDHOSTS = os.environ.get('ALLOWEDHOSTS', '').split(',')

    Permission review

    Static risk signals and limitations

    Network access

    medium · line 336

    The documentation includes network, browsing, or remote request actions.

    CSRF_TRUSTED_ORIGINS = ['https://example.com'] # Trusted domains

    Network access

    medium · line 361

    The documentation includes network, browsing, or remote request actions.

    fetch('/api/endpoint/', {

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score79/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    docs/zh-CN/skills/django-security/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Django 安全最佳实践

    保护 Django 应用程序免受常见漏洞侵害的全面安全指南。

    何时启用

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

    核心安全设置

    生产环境设置配置

    # settings/production.py
    import os
    
    DEBUG = False  # CRITICAL: Never use True in production
    
    ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
    
    # Security headers
    SECURE_SSL_REDIRECT = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True
    SECURE_HSTS_SECONDS = 31536000  # 1 year
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True
    SECURE_HSTS_PRELOAD = True
    SECURE_CONTENT_TYPE_NOSNIFF = True
    SECURE_BROWSER_XSS_FILTER = True
    X_FRAME_OPTIONS = 'DENY'
    
    # HTTPS and Cookies
    SESSION_COOKIE_HTTPONLY = True
    CSRF_COOKIE_HTTPONLY = True
    SESSION_COOKIE_SAMESITE = 'Lax'
    CSRF_COOKIE_SAMESITE = 'Lax'
    
    # Secret key (must be set via environment variable)
    SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
    if not SECRET_KEY:
        raise ImproperlyConfigured('DJANGO_SECRET_KEY environment variable is required')
    
    # Password validation
    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):
        """Custom user model for better security."""
    
        email = models.EmailField(unique=True)
        phone = models.CharField(max_length=20, blank=True)
    
        USERNAME_FIELD = 'email'  # Use email as username
        REQUIRED_FIELDS = ['username']
    
        class Meta:
            db_table = 'users'
            verbose_name = 'User'
            verbose_name_plural = 'Users'
    
        def __str__(self):
            return self.email
    
    # settings/base.py
    AUTH_USER_MODEL = 'users.User'
    

    密码哈希

    # Django uses PBKDF2 by default. For stronger security:
    PASSWORD_HASHERS = [
        'django.contrib.auth.hashers.Argon2PasswordHasher',
        'django.contrib.auth.hashers.PBKDF2PasswordHasher',
        'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
        'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
    ]
    

    会话管理

    # Session configuration
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # Or 'db'
    SESSION_CACHE_ALIAS = 'default'
    SESSION_COOKIE_AGE = 3600 * 24 * 7  # 1 week
    SESSION_SAVE_EVERY_REQUEST = False
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # Better UX, but less secure
    

    授权

    权限

    # 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 publish posts'),
                ('can_edit_others', 'Can edit posts of others'),
            ]
    
        def user_can_edit(self, user):
            """Check if user can edit this post."""
            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  # Return 403 instead of redirect
    
        def get_queryset(self):
            """Only allow users to edit their own posts."""
            return Post.objects.filter(author=self.request.user)
    

    自定义权限

    # permissions.py
    from rest_framework import permissions
    
    class IsOwnerOrReadOnly(permissions.BasePermission):
        """Allow only owners to edit objects."""
    
        def has_object_permission(self, request, view, obj):
            # Read permissions allowed for any request
            if request.method in permissions.SAFE_METHODS:
                return True
    
            # Write permissions only for owner
            return obj.author == request.user
    
    class IsAdminOrReadOnly(permissions.BasePermission):
        """Allow admins to do anything, others read-only."""
    
        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):
        """Allow only verified users."""
    
        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', 'Administrator'),
            ('moderator', 'Moderator'),
            ('user', 'Regular 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']
    
    # Mixins
    class AdminRequiredMixin:
        """Mixin to require admin role."""
    
        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 保护

    # GOOD: Django ORM automatically escapes parameters
    def get_user(username):
        return User.objects.get(username=username)  # Safe
    
    # GOOD: Using parameters with raw()
    def search_users(query):
        return User.objects.raw('SELECT * FROM users WHERE username = %s', [query])
    
    # BAD: Never directly interpolate user input
    def get_user_bad(username):
        return User.objects.raw(f'SELECT * FROM users WHERE username = {username}')  # VULNERABLE!
    
    # GOOD: Using filter with proper escaping
    def get_users_by_email(email):
        return User.objects.filter(email__iexact=email)  # Safe
    
    # GOOD: Using Q objects for complex queries
    from django.db.models import Q
    def search_users_complex(query):
        return User.objects.filter(
            Q(username__icontains=query) |
            Q(email__icontains=query)
        )  # Safe
    

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

    # If you must use raw SQL, always use parameters
    User.objects.raw(
        'SELECT * FROM users WHERE email = %s AND status = %s',
        [user_input_email, status]
    )
    

    XSS 防护

    模板转义

    {# Django auto-escapes variables by default - SAFE #}
    {{ user_input }}  {# Escaped HTML #}
    
    {# Explicitly mark safe only for trusted content #}
    {{ trusted_html|safe }}  {# Not escaped #}
    
    {# Use template filters for safe HTML #}
    {{ user_input|escape }}  {# Same as default #}
    {{ user_input|striptags }}  {# Remove all HTML tags #}
    
    {# JavaScript escaping #}
    <script>
        var username = {{ username|escapejs }};
    </script>
    

    安全字符串处理

    from django.utils.safestring import mark_safe
    from django.utils.html import escape
    
    # BAD: Never mark user input as safe without escaping
    def render_bad(user_input):
        return mark_safe(user_input)  # VULNERABLE!
    
    # GOOD: Escape first, then mark safe
    def render_good(user_input):
        return mark_safe(escape(user_input))
    
    # GOOD: Use format_html for HTML with variables
    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  # Prevent MIME sniffing
    SECURE_BROWSER_XSS_FILTER = True  # Enable XSS filter
    X_FRAME_OPTIONS = 'DENY'  # Prevent clickjacking
    
    # Custom middleware
    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 is enabled by default
    CSRF_COOKIE_SECURE = True  # Only send over HTTPS
    CSRF_COOKIE_HTTPONLY = True  # Prevent JavaScript access
    CSRF_COOKIE_SAMESITE = 'Lax'  # Prevent CSRF in some cases
    CSRF_TRUSTED_ORIGINS = ['https://example.com']  # Trusted domains
    
    # Template usage
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Submit</button>
    </form>
    
    # AJAX requests
    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  # Only use when absolutely necessary!
    def webhook_view(request):
        # Webhook from external service
        pass
    

    文件上传安全

    文件验证

    import os
    from django.core.exceptions import ValidationError
    
    def validate_file_extension(value):
        """Validate file extension."""
        ext = os.path.splitext(value.name)[1]
        valid_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.pdf']
        if not ext.lower() in valid_extensions:
            raise ValidationError('Unsupported file extension.')
    
    def validate_file_size(value):
        """Validate file size (max 5MB)."""
        filesize = value.size
        if filesize > 5 * 1024 * 1024:
            raise ValidationError('File too large. Max size is 5MB.')
    
    # models.py
    class Document(models.Model):
        file = models.FileField(
            upload_to='documents/',
            validators=[validate_file_extension, validate_file_size]
        )
    

    安全的文件存储

    # settings.py
    MEDIA_ROOT = '/var/www/media/'
    MEDIA_URL = '/media/'
    
    # Use a separate domain for media in production
    MEDIA_DOMAIN = 'https://media.example.com'
    
    # Don't serve user uploads directly
    # Use whitenoise or a CDN for static files
    # Use a separate server or S3 for media files
    

    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',
        }
    }
    
    # Custom throttle
    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 protected_view(request):
        return Response({'message': 'You are authenticated'})
    

    安全头部

    内容安全策略

    # settings.py
    CSP_DEFAULT_SRC = "'self'"
    CSP_SCRIPT_SRC = "'self' https://cdn.example.com"
    CSP_STYLE_SRC = "'self' 'unsafe-inline'"
    CSP_IMG_SRC = "'self' data: https:"
    CSP_CONNECT_SRC = "'self' https://api.example.com"
    
    # Middleware
    class CSPMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            response = self.get_response(request)
            response['Content-Security-Policy'] = (
                f"default-src {CSP_DEFAULT_SRC}; "
                f"script-src {CSP_SCRIPT_SRC}; "
                f"style-src {CSP_STYLE_SRC}; "
                f"img-src {CSP_IMG_SRC}; "
                f"connect-src {CSP_CONNECT_SRC}"
            )
            return response
    

    环境变量

    管理密钥

    # Use python-decouple or django-environ
    import environ
    
    env = environ.Env(
        # set casting, default value
        DEBUG=(bool, False)
    )
    
    # reading .env file
    environ.Env.read_env()
    
    SECRET_KEY = env('DJANGO_SECRET_KEY')
    DATABASE_URL = env('DATABASE_URL')
    ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
    
    # .env file (never commit this)
    DEBUG=False
    SECRET_KEY=your-secret-key-here
    DATABASE_URL=postgresql://user:password@localhost:5432/dbname
    ALLOWED_HOSTS=example.com,www.example.com
    

    记录安全事件

    # settings.py
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'file': {
                'level': 'WARNING',
                'class': 'logging.FileHandler',
                'filename': '/var/log/django/security.log',
            },
            'console': {
                'level': 'INFO',
                'class': 'logging.StreamHandler',
            },
        },
        'loggers': {
            'django.security': {
                'handlers': ['file', 'console'],
                'level': 'WARNING',
                'propagate': True,
            },
            'django.request': {
                'handlers': ['file'],
                'level': 'ERROR',
                'propagate': False,
            },
        },
    }
    

    快速安全检查清单

    检查项描述
    DEBUG = False切勿在生产环境中启用 DEBUG
    仅限 HTTPS强制 SSL,使用安全 Cookie
    强密钥对 SECRET_KEY 使用环境变量
    密码验证启用所有密码验证器
    CSRF 防护默认启用,不要禁用
    XSS 防护Django 自动转义,不要在用户输入上使用 &#124;safe
    SQL 注入使用 ORM,切勿在查询中拼接字符串
    文件上传验证文件类型和大小
    速率限制限制 API 端点访问频率
    安全头部CSP、X-Frame-Options、HSTS
    日志记录记录安全事件
    更新保持 Django 及其依赖项为最新版本

    请记住:安全是一个过程,而非产品。请定期审查并更新您的安全实践。

    Alternatives

    Compare before choosing