[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JCWVMQABNN4ADNERE767ZQ":3,"skill-same:01M6JCWVMQABNN4ADNERE767ZQ":69},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":41,"linkOnly":42,"markdownContent":43,"markdownBody":44,"frontMatter":45,"refName":47,"commitSha":48,"skillDirectory":49,"forksCount":50,"repositoryHtmlUrl":51,"repositoryHomepageUrl":52,"ownerHtmlUrl":53,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":54,"install":55,"resources":68},"01M6JCWVMQABNN4ADNERE767ZQ","django-security",null,"Django security best practices, authentication, authorization, CSRF protection, SQL injection prevention, XSS prevention, and secure deployment configurations.","S3YED\u002Fappie-kit","S3YED","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F62743867?v=4","01M6JBKBRRVP6KMS0VWZ27T3R6","01M6JBKBX0KG30284PW1DBYJT1",14,"MIT","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fecc\u002Fdjango-security\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"django security","django","security","xss","xss prevention","csrf protection","sql injection","protection","csrf","prevention","injection","authorization","security best practices","best practices","authentication","deployment","secure","sql","2026-11-10 10:12:30",false,"---\nname: django-security\ndescription: Django security best practices, authentication, authorization, CSRF protection, SQL injection prevention, XSS prevention, and secure deployment configurations.\norigin: ECC\n---\n\n# Django Security Best Practices\n\nComprehensive security guidelines for Django applications to protect against common vulnerabilities.\n\n## When to Activate\n\n- Setting up Django authentication and authorization\n- Implementing user permissions and roles\n- Configuring production security settings\n- Reviewing Django application for security issues\n- Deploying Django applications to production\n\n## Core Security Settings\n\n### Production Settings Configuration\n\n```python\n# settings\u002Fproduction.py\nimport os\n\nDEBUG = False  # CRITICAL: Never use True in production\n\nALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')\n\n# Security headers\nSECURE_SSL_REDIRECT = True\nSESSION_COOKIE_SECURE = True\nCSRF_COOKIE_SECURE = True\nSECURE_HSTS_SECONDS = 31536000  # 1 year\nSECURE_HSTS_INCLUDE_SUBDOMAINS = True\nSECURE_HSTS_PRELOAD = True\nSECURE_CONTENT_TYPE_NOSNIFF = True\nSECURE_BROWSER_XSS_FILTER = True\nX_FRAME_OPTIONS = 'DENY'\n\n# HTTPS and Cookies\nSESSION_COOKIE_HTTPONLY = True\nCSRF_COOKIE_HTTPONLY = True\nSESSION_COOKIE_SAMESITE = 'Lax'\nCSRF_COOKIE_SAMESITE = 'Lax'\n\n# Secret key (must be set via environment variable)\nSECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')\nif not SECRET_KEY:\n    raise ImproperlyConfigured('DJANGO_SECRET_KEY environment variable is required')\n\n# Password validation\nAUTH_PASSWORD_VALIDATORS = [\n    {\n        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\n        'OPTIONS': {\n            'min_length': 12,\n        }\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\n    },\n]\n```\n\n## Authentication\n\n### Custom User Model\n\n```python\n# apps\u002Fusers\u002Fmodels.py\nfrom django.contrib.auth.models import AbstractUser\nfrom django.db import models\n\nclass User(AbstractUser):\n    \"\"\"Custom user model for better security.\"\"\"\n\n    email = models.EmailField(unique=True)\n    phone = models.CharField(max_length=20, blank=True)\n\n    USERNAME_FIELD = 'email'  # Use email as username\n    REQUIRED_FIELDS = ['username']\n\n    class Meta:\n        db_table = 'users'\n        verbose_name = 'User'\n        verbose_name_plural = 'Users'\n\n    def __str__(self):\n        return self.email\n\n# settings\u002Fbase.py\nAUTH_USER_MODEL = 'users.User'\n```\n\n### Password Hashing\n\n```python\n# Django uses PBKDF2 by default. For stronger security:\nPASSWORD_HASHERS = [\n    'django.contrib.auth.hashers.Argon2PasswordHasher',\n    'django.contrib.auth.hashers.PBKDF2PasswordHasher',\n    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',\n    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',\n]\n```\n\n### Session Management\n\n```python\n# Session configuration\nSESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # Or 'db'\nSESSION_CACHE_ALIAS = 'default'\nSESSION_COOKIE_AGE = 3600 * 24 * 7  # 1 week\nSESSION_SAVE_EVERY_REQUEST = False\nSESSION_EXPIRE_AT_BROWSER_CLOSE = False  # Better UX, but less secure\n```\n\n## Authorization\n\n### Permissions\n\n```python\n# models.py\nfrom django.db import models\nfrom django.contrib.auth.models import Permission\n\nclass Post(models.Model):\n    title = models.CharField(max_length=200)\n    content = models.TextField()\n    author = models.ForeignKey(User, on_delete=models.CASCADE)\n\n    class Meta:\n        permissions = [\n            ('can_publish', 'Can publish posts'),\n            ('can_edit_others', 'Can edit posts of others'),\n        ]\n\n    def user_can_edit(self, user):\n        \"\"\"Check if user can edit this post.\"\"\"\n        return self.author == user or user.has_perm('app.can_edit_others')\n\n# views.py\nfrom django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin\nfrom django.views.generic import UpdateView\n\nclass PostUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):\n    model = Post\n    permission_required = 'app.can_edit_others'\n    raise_exception = True  # Return 403 instead of redirect\n\n    def get_queryset(self):\n        \"\"\"Only allow users to edit their own posts.\"\"\"\n        return Post.objects.filter(author=self.request.user)\n```\n\n### Custom Permissions\n\n```python\n# permissions.py\nfrom rest_framework import permissions\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n    \"\"\"Allow only owners to edit objects.\"\"\"\n\n    def has_object_permission(self, request, view, obj):\n        # Read permissions allowed for any request\n        if request.method in permissions.SAFE_METHODS:\n            return True\n\n        # Write permissions only for owner\n        return obj.author == request.user\n\nclass IsAdminOrReadOnly(permissions.BasePermission):\n    \"\"\"Allow admins to do anything, others read-only.\"\"\"\n\n    def has_permission(self, request, view):\n        if request.method in permissions.SAFE_METHODS:\n            return True\n        return request.user and request.user.is_staff\n\nclass IsVerifiedUser(permissions.BasePermission):\n    \"\"\"Allow only verified users.\"\"\"\n\n    def has_permission(self, request, view):\n        return request.user and request.user.is_authenticated and request.user.is_verified\n```\n\n### Role-Based Access Control (RBAC)\n\n```python\n# models.py\nfrom django.contrib.auth.models import AbstractUser, Group\n\nclass User(AbstractUser):\n    ROLE_CHOICES = [\n        ('admin', 'Administrator'),\n        ('moderator', 'Moderator'),\n        ('user', 'Regular User'),\n    ]\n    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user')\n\n    def is_admin(self):\n        return self.role == 'admin' or self.is_superuser\n\n    def is_moderator(self):\n        return self.role in ['admin', 'moderator']\n\n# Mixins\nclass AdminRequiredMixin:\n    \"\"\"Mixin to require admin role.\"\"\"\n\n    def dispatch(self, request, *args, **kwargs):\n        if not request.user.is_authenticated or not request.user.is_admin():\n            from django.core.exceptions import PermissionDenied\n            raise PermissionDenied\n        return super().dispatch(request, *args, **kwargs)\n```\n\n## SQL Injection Prevention\n\n### Django ORM Protection\n\n```python\n# GOOD: Django ORM automatically escapes parameters\ndef get_user(username):\n    return User.objects.get(username=username)  # Safe\n\n# GOOD: Using parameters with raw()\ndef search_users(query):\n    return User.objects.raw('SELECT * FROM users WHERE username = %s', [query])\n\n# BAD: Never directly interpolate user input\ndef get_user_bad(username):\n    return User.objects.raw(f'SELECT * FROM users WHERE username = {username}')  # VULNERABLE!\n\n# GOOD: Using filter with proper escaping\ndef get_users_by_email(email):\n    return User.objects.filter(email__iexact=email)  # Safe\n\n# GOOD: Using Q objects for complex queries\nfrom django.db.models import Q\ndef search_users_complex(query):\n    return User.objects.filter(\n        Q(username__icontains=query) |\n        Q(email__icontains=query)\n    )  # Safe\n```\n\n### Extra Security with raw()\n\n```python\n# If you must use raw SQL, always use parameters\nUser.objects.raw(\n    'SELECT * FROM users WHERE email = %s AND status = %s',\n    [user_input_email, status]\n)\n```\n\n## XSS Prevention\n\n### Template Escaping\n\n```django\n{# Django auto-escapes variables by default - SAFE #}\n{{ user_input }}  {# Escaped HTML #}\n\n{# Explicitly mark safe only for trusted content #}\n{{ trusted_html|safe }}  {# Not escaped #}\n\n{# Use template filters for safe HTML #}\n{{ user_input|escape }}  {# Same as default #}\n{{ user_input|striptags }}  {# Remove all HTML tags #}\n\n{# JavaScript escaping #}\n\u003Cscript>\n    var username = {{ username|escapejs }};\n\u003C\u002Fscript>\n```\n\n### Safe String Handling\n\n```python\nfrom django.utils.safestring import mark_safe\nfrom django.utils.html import escape\n\n# BAD: Never mark user input as safe without escaping\ndef render_bad(user_input):\n    return mark_safe(user_input)  # VULNERABLE!\n\n# GOOD: Escape first, then mark safe\ndef render_good(user_input):\n    return mark_safe(escape(user_input))\n\n# GOOD: Use format_html for HTML with variables\nfrom django.utils.html import format_html\n\ndef greet_user(username):\n    return format_html('\u003Cspan class=\"user\">{}\u003C\u002Fspan>', escape(username))\n```\n\n### HTTP Headers\n\n```python\n# settings.py\nSECURE_CONTENT_TYPE_NOSNIFF = True  # Prevent MIME sniffing\nSECURE_BROWSER_XSS_FILTER = True  # Enable XSS filter\nX_FRAME_OPTIONS = 'DENY'  # Prevent clickjacking\n\n# Custom middleware\nfrom django.conf import settings\n\nclass SecurityHeaderMiddleware:\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        response = self.get_response(request)\n        response['X-Content-Type-Options'] = 'nosniff'\n        response['X-Frame-Options'] = 'DENY'\n        response['X-XSS-Protection'] = '1; mode=block'\n        response['Content-Security-Policy'] = \"default-src 'self'\"\n        return response\n```\n\n## CSRF Protection\n\n### Default CSRF Protection\n\n```python\n# settings.py - CSRF is enabled by default\nCSRF_COOKIE_SECURE = True  # Only send over HTTPS\nCSRF_COOKIE_HTTPONLY = True  # Prevent JavaScript access\nCSRF_COOKIE_SAMESITE = 'Lax'  # Prevent CSRF in some cases\nCSRF_TRUSTED_ORIGINS = ['https:\u002F\u002Fexample.com']  # Trusted domains\n\n# Template usage\n\u003Cform method=\"post\">\n    {% csrf_token %}\n    {{ form.as_p }}\n    \u003Cbutton type=\"submit\">Submit\u003C\u002Fbutton>\n\u003C\u002Fform>\n\n# AJAX requests\nfunction getCookie(name) {\n    let cookieValue = null;\n    if (document.cookie && document.cookie !== '') {\n        const cookies = document.cookie.split(';');\n        for (let i = 0; i \u003C cookies.length; i++) {\n            const cookie = cookies[i].trim();\n            if (cookie.substring(0, name.length + 1) === (name + '=')) {\n                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));\n                break;\n            }\n        }\n    }\n    return cookieValue;\n}\n\nfetch('\u002Fapi\u002Fendpoint\u002F', {\n    method: 'POST',\n    headers: {\n        'X-CSRFToken': getCookie('csrftoken'),\n        'Content-Type': 'application\u002Fjson',\n    },\n    body: JSON.stringify(data)\n});\n```\n\n### Exempting Views (Use Carefully)\n\n```python\nfrom django.views.decorators.csrf import csrf_exempt\n\n@csrf_exempt  # Only use when absolutely necessary!\ndef webhook_view(request):\n    # Webhook from external service\n    pass\n```\n\n## File Upload Security\n\n### File Validation\n\n```python\nimport os\nfrom django.core.exceptions import ValidationError\n\ndef validate_file_extension(value):\n    \"\"\"Validate file extension.\"\"\"\n    ext = os.path.splitext(value.name)[1]\n    valid_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.pdf']\n    if not ext.lower() in valid_extensions:\n        raise ValidationError('Unsupported file extension.')\n\ndef validate_file_size(value):\n    \"\"\"Validate file size (max 5MB).\"\"\"\n    filesize = value.size\n    if filesize > 5 * 1024 * 1024:\n        raise ValidationError('File too large. Max size is 5MB.')\n\n# models.py\nclass Document(models.Model):\n    file = models.FileField(\n        upload_to='documents\u002F',\n        validators=[validate_file_extension, validate_file_size]\n    )\n```\n\n### Secure File Storage\n\n```python\n# settings.py\nMEDIA_ROOT = '\u002Fvar\u002Fwww\u002Fmedia\u002F'\nMEDIA_URL = '\u002Fmedia\u002F'\n\n# Use a separate domain for media in production\nMEDIA_DOMAIN = 'https:\u002F\u002Fmedia.example.com'\n\n# Don't serve user uploads directly\n# Use whitenoise or a CDN for static files\n# Use a separate server or S3 for media files\n```\n\n## API Security\n\n### Rate Limiting\n\n```python\n# settings.py\nREST_FRAMEWORK = {\n    'DEFAULT_THROTTLE_CLASSES': [\n        'rest_framework.throttling.AnonRateThrottle',\n        'rest_framework.throttling.UserRateThrottle'\n    ],\n    'DEFAULT_THROTTLE_RATES': {\n        'anon': '100\u002Fday',\n        'user': '1000\u002Fday',\n        'upload': '10\u002Fhour',\n    }\n}\n\n# Custom throttle\nfrom rest_framework.throttling import UserRateThrottle\n\nclass BurstRateThrottle(UserRateThrottle):\n    scope = 'burst'\n    rate = '60\u002Fmin'\n\nclass SustainedRateThrottle(UserRateThrottle):\n    scope = 'sustained'\n    rate = '1000\u002Fday'\n```\n\n### Authentication for APIs\n\n```python\n# settings.py\nREST_FRAMEWORK = {\n    'DEFAULT_AUTHENTICATION_CLASSES': [\n        'rest_framework.authentication.TokenAuthentication',\n        'rest_framework.authentication.SessionAuthentication',\n        'rest_framework_simplejwt.authentication.JWTAuthentication',\n    ],\n    'DEFAULT_PERMISSION_CLASSES': [\n        'rest_framework.permissions.IsAuthenticated',\n    ],\n}\n\n# views.py\nfrom rest_framework.decorators import api_view, permission_classes\nfrom rest_framework.permissions import IsAuthenticated\n\n@api_view(['GET', 'POST'])\n@permission_classes([IsAuthenticated])\ndef protected_view(request):\n    return Response({'message': 'You are authenticated'})\n```\n\n## Security Headers\n\n### Content Security Policy\n\n```python\n# settings.py\nCSP_DEFAULT_SRC = \"'self'\"\nCSP_SCRIPT_SRC = \"'self' https:\u002F\u002Fcdn.example.com\"\nCSP_STYLE_SRC = \"'self' 'unsafe-inline'\"\nCSP_IMG_SRC = \"'self' data: https:\"\nCSP_CONNECT_SRC = \"'self' https:\u002F\u002Fapi.example.com\"\n\n# Middleware\nclass CSPMiddleware:\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        response = self.get_response(request)\n        response['Content-Security-Policy'] = (\n            f\"default-src {CSP_DEFAULT_SRC}; \"\n            f\"script-src {CSP_SCRIPT_SRC}; \"\n            f\"style-src {CSP_STYLE_SRC}; \"\n            f\"img-src {CSP_IMG_SRC}; \"\n            f\"connect-src {CSP_CONNECT_SRC}\"\n        )\n        return response\n```\n\n## Environment Variables\n\n### Managing Secrets\n\n```python\n# Use python-decouple or django-environ\nimport environ\n\nenv = environ.Env(\n    # set casting, default value\n    DEBUG=(bool, False)\n)\n\n# reading .env file\nenviron.Env.read_env()\n\nSECRET_KEY = env('DJANGO_SECRET_KEY')\nDATABASE_URL = env('DATABASE_URL')\nALLOWED_HOSTS = env.list('ALLOWED_HOSTS')\n\n# .env file (never commit this)\nDEBUG=False\nSECRET_KEY=your-secret-key-here\nDATABASE_URL=postgresql:\u002F\u002Fuser:password@localhost:5432\u002Fdbname\nALLOWED_HOSTS=example.com,www.example.com\n```\n\n## Logging Security Events\n\n```python\n# settings.py\nLOGGING = {\n    'version': 1,\n    'disable_existing_loggers': False,\n    'handlers': {\n        'file': {\n            'level': 'WARNING',\n            'class': 'logging.FileHandler',\n            'filename': '\u002Fvar\u002Flog\u002Fdjango\u002Fsecurity.log',\n        },\n        'console': {\n            'level': 'INFO',\n            'class': 'logging.StreamHandler',\n        },\n    },\n    'loggers': {\n        'django.security': {\n            'handlers': ['file', 'console'],\n            'level': 'WARNING',\n            'propagate': True,\n        },\n        'django.request': {\n            'handlers': ['file'],\n            'level': 'ERROR',\n            'propagate': False,\n        },\n    },\n}\n```\n\n## Quick Security Checklist\n\n| Check | Description |\n|-------|-------------|\n| `DEBUG = False` | Never run with DEBUG in production |\n| HTTPS only | Force SSL, secure cookies |\n| Strong secrets | Use environment variables for SECRET_KEY |\n| Password validation | Enable all password validators |\n| CSRF protection | Enabled by default, don't disable |\n| XSS prevention | Django auto-escapes, don't use `&#124;safe` with user input |\n| SQL injection | Use ORM, never concatenate strings in queries |\n| File uploads | Validate file type and size |\n| Rate limiting | Throttle API endpoints |\n| Security headers | CSP, X-Frame-Options, HSTS |\n| Logging | Log security events |\n| Updates | Keep Django and dependencies updated |\n\nRemember: Security is a process, not a product. Regularly review and update your security practices.\n","# Django Security Best Practices\n\nComprehensive security guidelines for Django applications to protect against common vulnerabilities.\n\n## When to Activate\n\n- Setting up Django authentication and authorization\n- Implementing user permissions and roles\n- Configuring production security settings\n- Reviewing Django application for security issues\n- Deploying Django applications to production\n\n## Core Security Settings\n\n### Production Settings Configuration\n\n```python\n# settings\u002Fproduction.py\nimport os\n\nDEBUG = False  # CRITICAL: Never use True in production\n\nALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')\n\n# Security headers\nSECURE_SSL_REDIRECT = True\nSESSION_COOKIE_SECURE = True\nCSRF_COOKIE_SECURE = True\nSECURE_HSTS_SECONDS = 31536000  # 1 year\nSECURE_HSTS_INCLUDE_SUBDOMAINS = True\nSECURE_HSTS_PRELOAD = True\nSECURE_CONTENT_TYPE_NOSNIFF = True\nSECURE_BROWSER_XSS_FILTER = True\nX_FRAME_OPTIONS = 'DENY'\n\n# HTTPS and Cookies\nSESSION_COOKIE_HTTPONLY = True\nCSRF_COOKIE_HTTPONLY = True\nSESSION_COOKIE_SAMESITE = 'Lax'\nCSRF_COOKIE_SAMESITE = 'Lax'\n\n# Secret key (must be set via environment variable)\nSECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')\nif not SECRET_KEY:\n    raise ImproperlyConfigured('DJANGO_SECRET_KEY environment variable is required')\n\n# Password validation\nAUTH_PASSWORD_VALIDATORS = [\n    {\n        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',\n        'OPTIONS': {\n            'min_length': 12,\n        }\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',\n    },\n    {\n        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',\n    },\n]\n```\n\n## Authentication\n\n### Custom User Model\n\n```python\n# apps\u002Fusers\u002Fmodels.py\nfrom django.contrib.auth.models import AbstractUser\nfrom django.db import models\n\nclass User(AbstractUser):\n    \"\"\"Custom user model for better security.\"\"\"\n\n    email = models.EmailField(unique=True)\n    phone = models.CharField(max_length=20, blank=True)\n\n    USERNAME_FIELD = 'email'  # Use email as username\n    REQUIRED_FIELDS = ['username']\n\n    class Meta:\n        db_table = 'users'\n        verbose_name = 'User'\n        verbose_name_plural = 'Users'\n\n    def __str__(self):\n        return self.email\n\n# settings\u002Fbase.py\nAUTH_USER_MODEL = 'users.User'\n```\n\n### Password Hashing\n\n```python\n# Django uses PBKDF2 by default. For stronger security:\nPASSWORD_HASHERS = [\n    'django.contrib.auth.hashers.Argon2PasswordHasher',\n    'django.contrib.auth.hashers.PBKDF2PasswordHasher',\n    'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',\n    'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',\n]\n```\n\n### Session Management\n\n```python\n# Session configuration\nSESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # Or 'db'\nSESSION_CACHE_ALIAS = 'default'\nSESSION_COOKIE_AGE = 3600 * 24 * 7  # 1 week\nSESSION_SAVE_EVERY_REQUEST = False\nSESSION_EXPIRE_AT_BROWSER_CLOSE = False  # Better UX, but less secure\n```\n\n## Authorization\n\n### Permissions\n\n```python\n# models.py\nfrom django.db import models\nfrom django.contrib.auth.models import Permission\n\nclass Post(models.Model):\n    title = models.CharField(max_length=200)\n    content = models.TextField()\n    author = models.ForeignKey(User, on_delete=models.CASCADE)\n\n    class Meta:\n        permissions = [\n            ('can_publish', 'Can publish posts'),\n            ('can_edit_others', 'Can edit posts of others'),\n        ]\n\n    def user_can_edit(self, user):\n        \"\"\"Check if user can edit this post.\"\"\"\n        return self.author == user or user.has_perm('app.can_edit_others')\n\n# views.py\nfrom django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin\nfrom django.views.generic import UpdateView\n\nclass PostUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):\n    model = Post\n    permission_required = 'app.can_edit_others'\n    raise_exception = True  # Return 403 instead of redirect\n\n    def get_queryset(self):\n        \"\"\"Only allow users to edit their own posts.\"\"\"\n        return Post.objects.filter(author=self.request.user)\n```\n\n### Custom Permissions\n\n```python\n# permissions.py\nfrom rest_framework import permissions\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n    \"\"\"Allow only owners to edit objects.\"\"\"\n\n    def has_object_permission(self, request, view, obj):\n        # Read permissions allowed for any request\n        if request.method in permissions.SAFE_METHODS:\n            return True\n\n        # Write permissions only for owner\n        return obj.author == request.user\n\nclass IsAdminOrReadOnly(permissions.BasePermission):\n    \"\"\"Allow admins to do anything, others read-only.\"\"\"\n\n    def has_permission(self, request, view):\n        if request.method in permissions.SAFE_METHODS:\n            return True\n        return request.user and request.user.is_staff\n\nclass IsVerifiedUser(permissions.BasePermission):\n    \"\"\"Allow only verified users.\"\"\"\n\n    def has_permission(self, request, view):\n        return request.user and request.user.is_authenticated and request.user.is_verified\n```\n\n### Role-Based Access Control (RBAC)\n\n```python\n# models.py\nfrom django.contrib.auth.models import AbstractUser, Group\n\nclass User(AbstractUser):\n    ROLE_CHOICES = [\n        ('admin', 'Administrator'),\n        ('moderator', 'Moderator'),\n        ('user', 'Regular User'),\n    ]\n    role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user')\n\n    def is_admin(self):\n        return self.role == 'admin' or self.is_superuser\n\n    def is_moderator(self):\n        return self.role in ['admin', 'moderator']\n\n# Mixins\nclass AdminRequiredMixin:\n    \"\"\"Mixin to require admin role.\"\"\"\n\n    def dispatch(self, request, *args, **kwargs):\n        if not request.user.is_authenticated or not request.user.is_admin():\n            from django.core.exceptions import PermissionDenied\n            raise PermissionDenied\n        return super().dispatch(request, *args, **kwargs)\n```\n\n## SQL Injection Prevention\n\n### Django ORM Protection\n\n```python\n# GOOD: Django ORM automatically escapes parameters\ndef get_user(username):\n    return User.objects.get(username=username)  # Safe\n\n# GOOD: Using parameters with raw()\ndef search_users(query):\n    return User.objects.raw('SELECT * FROM users WHERE username = %s', [query])\n\n# BAD: Never directly interpolate user input\ndef get_user_bad(username):\n    return User.objects.raw(f'SELECT * FROM users WHERE username = {username}')  # VULNERABLE!\n\n# GOOD: Using filter with proper escaping\ndef get_users_by_email(email):\n    return User.objects.filter(email__iexact=email)  # Safe\n\n# GOOD: Using Q objects for complex queries\nfrom django.db.models import Q\ndef search_users_complex(query):\n    return User.objects.filter(\n        Q(username__icontains=query) |\n        Q(email__icontains=query)\n    )  # Safe\n```\n\n### Extra Security with raw()\n\n```python\n# If you must use raw SQL, always use parameters\nUser.objects.raw(\n    'SELECT * FROM users WHERE email = %s AND status = %s',\n    [user_input_email, status]\n)\n```\n\n## XSS Prevention\n\n### Template Escaping\n\n```django\n{# Django auto-escapes variables by default - SAFE #}\n{{ user_input }}  {# Escaped HTML #}\n\n{# Explicitly mark safe only for trusted content #}\n{{ trusted_html|safe }}  {# Not escaped #}\n\n{# Use template filters for safe HTML #}\n{{ user_input|escape }}  {# Same as default #}\n{{ user_input|striptags }}  {# Remove all HTML tags #}\n\n{# JavaScript escaping #}\n\u003Cscript>\n    var username = {{ username|escapejs }};\n\u003C\u002Fscript>\n```\n\n### Safe String Handling\n\n```python\nfrom django.utils.safestring import mark_safe\nfrom django.utils.html import escape\n\n# BAD: Never mark user input as safe without escaping\ndef render_bad(user_input):\n    return mark_safe(user_input)  # VULNERABLE!\n\n# GOOD: Escape first, then mark safe\ndef render_good(user_input):\n    return mark_safe(escape(user_input))\n\n# GOOD: Use format_html for HTML with variables\nfrom django.utils.html import format_html\n\ndef greet_user(username):\n    return format_html('\u003Cspan class=\"user\">{}\u003C\u002Fspan>', escape(username))\n```\n\n### HTTP Headers\n\n```python\n# settings.py\nSECURE_CONTENT_TYPE_NOSNIFF = True  # Prevent MIME sniffing\nSECURE_BROWSER_XSS_FILTER = True  # Enable XSS filter\nX_FRAME_OPTIONS = 'DENY'  # Prevent clickjacking\n\n# Custom middleware\nfrom django.conf import settings\n\nclass SecurityHeaderMiddleware:\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        response = self.get_response(request)\n        response['X-Content-Type-Options'] = 'nosniff'\n        response['X-Frame-Options'] = 'DENY'\n        response['X-XSS-Protection'] = '1; mode=block'\n        response['Content-Security-Policy'] = \"default-src 'self'\"\n        return response\n```\n\n## CSRF Protection\n\n### Default CSRF Protection\n\n```python\n# settings.py - CSRF is enabled by default\nCSRF_COOKIE_SECURE = True  # Only send over HTTPS\nCSRF_COOKIE_HTTPONLY = True  # Prevent JavaScript access\nCSRF_COOKIE_SAMESITE = 'Lax'  # Prevent CSRF in some cases\nCSRF_TRUSTED_ORIGINS = ['https:\u002F\u002Fexample.com']  # Trusted domains\n\n# Template usage\n\u003Cform method=\"post\">\n    {% csrf_token %}\n    {{ form.as_p }}\n    \u003Cbutton type=\"submit\">Submit\u003C\u002Fbutton>\n\u003C\u002Fform>\n\n# AJAX requests\nfunction getCookie(name) {\n    let cookieValue = null;\n    if (document.cookie && document.cookie !== '') {\n        const cookies = document.cookie.split(';');\n        for (let i = 0; i \u003C cookies.length; i++) {\n            const cookie = cookies[i].trim();\n            if (cookie.substring(0, name.length + 1) === (name + '=')) {\n                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));\n                break;\n            }\n        }\n    }\n    return cookieValue;\n}\n\nfetch('\u002Fapi\u002Fendpoint\u002F', {\n    method: 'POST',\n    headers: {\n        'X-CSRFToken': getCookie('csrftoken'),\n        'Content-Type': 'application\u002Fjson',\n    },\n    body: JSON.stringify(data)\n});\n```\n\n### Exempting Views (Use Carefully)\n\n```python\nfrom django.views.decorators.csrf import csrf_exempt\n\n@csrf_exempt  # Only use when absolutely necessary!\ndef webhook_view(request):\n    # Webhook from external service\n    pass\n```\n\n## File Upload Security\n\n### File Validation\n\n```python\nimport os\nfrom django.core.exceptions import ValidationError\n\ndef validate_file_extension(value):\n    \"\"\"Validate file extension.\"\"\"\n    ext = os.path.splitext(value.name)[1]\n    valid_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.pdf']\n    if not ext.lower() in valid_extensions:\n        raise ValidationError('Unsupported file extension.')\n\ndef validate_file_size(value):\n    \"\"\"Validate file size (max 5MB).\"\"\"\n    filesize = value.size\n    if filesize > 5 * 1024 * 1024:\n        raise ValidationError('File too large. Max size is 5MB.')\n\n# models.py\nclass Document(models.Model):\n    file = models.FileField(\n        upload_to='documents\u002F',\n        validators=[validate_file_extension, validate_file_size]\n    )\n```\n\n### Secure File Storage\n\n```python\n# settings.py\nMEDIA_ROOT = '\u002Fvar\u002Fwww\u002Fmedia\u002F'\nMEDIA_URL = '\u002Fmedia\u002F'\n\n# Use a separate domain for media in production\nMEDIA_DOMAIN = 'https:\u002F\u002Fmedia.example.com'\n\n# Don't serve user uploads directly\n# Use whitenoise or a CDN for static files\n# Use a separate server or S3 for media files\n```\n\n## API Security\n\n### Rate Limiting\n\n```python\n# settings.py\nREST_FRAMEWORK = {\n    'DEFAULT_THROTTLE_CLASSES': [\n        'rest_framework.throttling.AnonRateThrottle',\n        'rest_framework.throttling.UserRateThrottle'\n    ],\n    'DEFAULT_THROTTLE_RATES': {\n        'anon': '100\u002Fday',\n        'user': '1000\u002Fday',\n        'upload': '10\u002Fhour',\n    }\n}\n\n# Custom throttle\nfrom rest_framework.throttling import UserRateThrottle\n\nclass BurstRateThrottle(UserRateThrottle):\n    scope = 'burst'\n    rate = '60\u002Fmin'\n\nclass SustainedRateThrottle(UserRateThrottle):\n    scope = 'sustained'\n    rate = '1000\u002Fday'\n```\n\n### Authentication for APIs\n\n```python\n# settings.py\nREST_FRAMEWORK = {\n    'DEFAULT_AUTHENTICATION_CLASSES': [\n        'rest_framework.authentication.TokenAuthentication',\n        'rest_framework.authentication.SessionAuthentication',\n        'rest_framework_simplejwt.authentication.JWTAuthentication',\n    ],\n    'DEFAULT_PERMISSION_CLASSES': [\n        'rest_framework.permissions.IsAuthenticated',\n    ],\n}\n\n# views.py\nfrom rest_framework.decorators import api_view, permission_classes\nfrom rest_framework.permissions import IsAuthenticated\n\n@api_view(['GET', 'POST'])\n@permission_classes([IsAuthenticated])\ndef protected_view(request):\n    return Response({'message': 'You are authenticated'})\n```\n\n## Security Headers\n\n### Content Security Policy\n\n```python\n# settings.py\nCSP_DEFAULT_SRC = \"'self'\"\nCSP_SCRIPT_SRC = \"'self' https:\u002F\u002Fcdn.example.com\"\nCSP_STYLE_SRC = \"'self' 'unsafe-inline'\"\nCSP_IMG_SRC = \"'self' data: https:\"\nCSP_CONNECT_SRC = \"'self' https:\u002F\u002Fapi.example.com\"\n\n# Middleware\nclass CSPMiddleware:\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        response = self.get_response(request)\n        response['Content-Security-Policy'] = (\n            f\"default-src {CSP_DEFAULT_SRC}; \"\n            f\"script-src {CSP_SCRIPT_SRC}; \"\n            f\"style-src {CSP_STYLE_SRC}; \"\n            f\"img-src {CSP_IMG_SRC}; \"\n            f\"connect-src {CSP_CONNECT_SRC}\"\n        )\n        return response\n```\n\n## Environment Variables\n\n### Managing Secrets\n\n```python\n# Use python-decouple or django-environ\nimport environ\n\nenv = environ.Env(\n    # set casting, default value\n    DEBUG=(bool, False)\n)\n\n# reading .env file\nenviron.Env.read_env()\n\nSECRET_KEY = env('DJANGO_SECRET_KEY')\nDATABASE_URL = env('DATABASE_URL')\nALLOWED_HOSTS = env.list('ALLOWED_HOSTS')\n\n# .env file (never commit this)\nDEBUG=False\nSECRET_KEY=your-secret-key-here\nDATABASE_URL=postgresql:\u002F\u002Fuser:password@localhost:5432\u002Fdbname\nALLOWED_HOSTS=example.com,www.example.com\n```\n\n## Logging Security Events\n\n```python\n# settings.py\nLOGGING = {\n    'version': 1,\n    'disable_existing_loggers': False,\n    'handlers': {\n        'file': {\n            'level': 'WARNING',\n            'class': 'logging.FileHandler',\n            'filename': '\u002Fvar\u002Flog\u002Fdjango\u002Fsecurity.log',\n        },\n        'console': {\n            'level': 'INFO',\n            'class': 'logging.StreamHandler',\n        },\n    },\n    'loggers': {\n        'django.security': {\n            'handlers': ['file', 'console'],\n            'level': 'WARNING',\n            'propagate': True,\n        },\n        'django.request': {\n            'handlers': ['file'],\n            'level': 'ERROR',\n            'propagate': False,\n        },\n    },\n}\n```\n\n## Quick Security Checklist\n\n| Check | Description |\n|-------|-------------|\n| `DEBUG = False` | Never run with DEBUG in production |\n| HTTPS only | Force SSL, secure cookies |\n| Strong secrets | Use environment variables for SECRET_KEY |\n| Password validation | Enable all password validators |\n| CSRF protection | Enabled by default, don't disable |\n| XSS prevention | Django auto-escapes, don't use `&#124;safe` with user input |\n| SQL injection | Use ORM, never concatenate strings in queries |\n| File uploads | Validate file type and size |\n| Rate limiting | Throttle API endpoints |\n| Security headers | CSP, X-Frame-Options, HSTS |\n| Logging | Log security events |\n| Updates | Keep Django and dependencies updated |\n\nRemember: Security is a process, not a product. Regularly review and update your security practices.\n",{"name":5,"origin":46,"description":7},"ECC","master","8ec37df6d71fb664ea19d32c079e7d3ff931a8e0","skills\u002Fecc\u002Fdjango-security",3,"https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit","https:\u002F\u002Fweblyfe.ai","https:\u002F\u002Fgithub.com\u002FS3YED","2026-08-27 01:54:02",{"source":8,"skillName":5,"commands":56,"prompt":66,"zipAvailable":67},[57,60,63],{"tool":58,"command":59},"npx","npx skills add S3YED\u002Fappie-kit --skill django-security",{"tool":61,"command":62},"bunx","bunx skills add S3YED\u002Fappie-kit --skill django-security",{"tool":64,"command":65},"pnpm","pnpm dlx skills add S3YED\u002Fappie-kit --skill django-security","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fecc\u002Fdjango-security\u002FSKILL.md",true,[],[70,104,139,165,195,220],{"skillNo":71,"name":72,"nameZh":6,"description":73,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":74,"categories":75,"keywords":85,"publishedAt":41},"01M6JFD4M6N4DSGC7TXEJ5R3J2","ai-music","AI music generation and audio tools: HeartMuLa lyrics-to-music generation, songwriting craft (+ Suno prompts), and audio spectrogram\u002Ffeature visualization.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fwethlete\u002Fai-music\u002FSKILL.md",[76,79,82],{"categoryNo":77,"categoryName":78,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000013","分析与可视化",{"categoryNo":80,"categoryName":81,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":83,"categoryName":84,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103],"music","ai music","humanizer","visualization","Tools","Audio","hermes","hermes agent","songwriting","suno","lyrics","prompts","heartmula","craft","generation","feature","ai music generation","music generation",{"skillNo":105,"name":106,"nameZh":6,"description":107,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":108,"categories":109,"keywords":118,"publishedAt":41},"01M6JFCZE4XNFQFMNZDHKHT5V5","agentic-webdesign-design-system-ingestion","Use v0 Design Systems 2.0, Bolt Design System Agents, or Lovable to generate production UI from a client's real design system components — not generic Tailwind. Use when building a client website where brand consistency matters.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fweb-design\u002Fagentic-webdesign-design-system-ingestion\u002FSKILL.md",[110,113],{"categoryNo":111,"categoryName":112,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":114,"categoryName":115,"taxonomyType":116,"taxonomyTypeDesc":117},"01JBSEEDCAT000000000000032","设计师","PROFESSION","适用职业",[119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138],"design","agentic","design system","system","ingestion","webdesign","agentic webdesign design system ingestion","components","generate","design systems","website","ui","Tailwind","bolt","lovable","v0","design-system","wed","design system components","appie",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":147,"publishedAt":41},"01M6JFCVPX3913CFAS6QQJ33ZB","agentic-webdesign-agent-elements","Use 21st.dev Agent Elements — 25 shadcn\u002Fui components purpose-built for AI agent UIs. Tool cards (Bash, Edit, Search, Plan, Subagent, MCP), chat, input, streaming. Open source, shadcn-registry install, no runtime lock-in.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fweb-design\u002Fagentic-webdesign-agent-elements\u002FSKILL.md",[145,146],{"categoryNo":83,"categoryName":84,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":114,"categoryName":115,"taxonomyType":116,"taxonomyTypeDesc":117},[120,148,124,149,150,151,126,152,153,154,155,156,157,158,159,160,161,162,163,164],"elements","agentic webdesign agent elements","MCP","ai agent","registry","search","streaming","open source","open","Bash","plan","subagent","edit","install","input","lock","source",{"skillNo":166,"name":167,"nameZh":6,"description":168,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":175,"publishedAt":41},"01M6JFCQC7H65M9QJ6TRE71BS1","vercel-deployment","Deploy static sites and frontends to Vercel — handles both CLI and REST API paths, including the CLI token sanitization workaround.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Fvercel-deployment\u002FSKILL.md",[171,174],{"categoryNo":172,"categoryName":173,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000011","运维与部署",{"categoryNo":83,"categoryName":84,"taxonomyType":20,"taxonomyTypeDesc":21},[38,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194],"vercel deployment","vercel","macos","cli","linux","windows","deploy","vercel deploy","REST","api","site","deploy to vercel","token","rest api","host","sites","paths","sanitization","static site",{"skillNo":196,"name":197,"nameZh":6,"description":198,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":199,"categories":200,"keywords":202,"publishedAt":41},"01M6JFCM4SMTXBM5A01BK2YQG1","software-quality-workflows","Umbrella workflow for TDD, debugging, code simplification, spikes, pre-commit review, and debugger-assisted investigation.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Fsoftware-quality-workflows\u002FSKILL.md",[201],{"categoryNo":83,"categoryName":84,"taxonomyType":20,"taxonomyTypeDesc":21},[203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219],"workflows","quality","software","software quality workflows","review","tdd","workflow","commit","debugging","code","debugger","pre-commit","investigation","code simplification","user","simplification","for",{"skillNo":221,"name":222,"nameZh":6,"description":223,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":224,"categories":225,"keywords":233,"publishedAt":41},"01M6JFCGD0M6CEATY01W7YN6DB","react-component-refactoring","Safely refactor large React components into smaller, maintainable pieces using proven patterns","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Freact-component-refactoring\u002FSKILL.md",[226,229,232],{"categoryNo":227,"categoryName":228,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":230,"categoryName":231,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":83,"categoryName":84,"taxonomyType":20,"taxonomyTypeDesc":21},[234,235,236,237,238,239,240,241,178,242,243,126,244,181,245,212,246,247,248,249],"react","component","refactoring","component refactoring","react component","react component refactoring","frontend","writing","safe","typescript","react components","modular","refactor","simplify","patterns","test-driven-development"]