一、CSRF攻击:别让黑客帮你点按钮

想象这样一个场景:你正在登录银行网站,突然收到一封"中奖邮件",点开链接后什么都没发生。第二天却发现账户里的钱不见了——这可能就是CSRF(跨站请求伪造)攻击。黑客伪造你的身份,偷偷帮你完成转账操作。

Django自带CSRF防护机制,但要用对地方。比如在表单中忘记加{% csrf_token %}标签,防护就会失效。来看正确做法:

# 技术栈:Django 4.2
# views.py
from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect

@csrf_protect  # 双重保险
def transfer_money(request):
    if request.method == "POST":
        # 处理转账逻辑...
        return HttpResponse("操作成功")
    return render(request, "transfer.html")

<!-- transfer.html -->
<form method="post">
    {% csrf_token %}  <!-- 这个标签必须加 -->
    <input type="text" name="amount">
    <button type="submit">确认转账</button>
</form>

注意事项

  1. AJAX请求需要手动携带CSRF token
  2. 使用@csrf_exempt装饰器时要非常谨慎
  3. 确保django.middleware.csrf.CsrfViewMiddleware在中间件列表里

二、XSS攻击:别让用户变成脚本小子

有个论坛允许用户发帖,小明输入<script>alert('你的cookie是'+document.cookie)</script>,所有查看帖子的人都会弹出自己的cookie——这就是典型的XSS(跨站脚本)攻击。

Django的模板系统默认会转义HTML标签,但某些场景需要特别注意:

# 技术栈:Django 4.2
from django.utils.html import escape

def comment_view(request):
    unsafe_content = request.POST.get("comment")
    
    # 危险做法:直接返回未处理的内容
    # return HttpResponse(unsafe_content)  
    
    # 安全做法1:使用escape函数
    safe_content = escape(unsafe_content)
    
    # 安全做法2:模板自动转义
    return render(request, "comment.html", 
                 {"content": unsafe_content}) 

<!-- comment.html -->
{{ content|safe }}  <!-- 慎用!只有确定内容安全时才用 -->

防御组合拳

  1. 使用mark_safe()前必须确保内容可信
  2. 富文本内容用django-ckeditor等专业编辑器
  3. 设置Content-Security-Policy响应头

三、SQL注入:别让用户帮你写查询

当用户输入变成SQL查询的一部分时,黑客可能输入' OR '1'='1这样的字符串来获取所有数据。Django的ORM已经帮我们防范了这种攻击,但直接写原生SQL时要格外小心:

# 技术栈:Django 4.2
from django.db import connection

# 危险做法:字符串拼接SQL
def vulnerable_search(keyword):
    with connection.cursor() as cursor:
        cursor.execute(f"SELECT * FROM posts WHERE title LIKE '%{keyword}%'")  # 可能被注入
        return cursor.fetchall()

# 安全做法:使用参数化查询
def safe_search(keyword):
    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM posts WHERE title LIKE %s", [f"%{keyword}%"])  # 自动转义
        return cursor.fetchall()

# 最佳实践:直接用ORM
def best_search(keyword):
    return Post.objects.filter(title__contains=keyword)  # 完全避免手写SQL

特别注意

  1. 即使使用raw()方法也要用参数化查询
  2. 存储过程调用同样需要参数化
  3. 定期审计项目中的原生SQL语句

四、综合防护策略

实际项目中需要多管齐下:

  1. 中间件配置:确保以下中间件启用

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
  2. 安全头部:在settings.py中添加

    SECURE_CONTENT_TYPE_NOSNIFF = True
    SECURE_BROWSER_XSS_FILTER = True
    X_FRAME_OPTIONS = 'DENY'
    
  3. 密码处理

    # 不要自己实现加密!
    from django.contrib.auth.hashers import make_password
    hashed_pwd = make_password('明文密码')  # 自动加盐处理
    
  4. 文件上传:限制文件类型和大小

    from django.core.validators import FileExtensionValidator
    
    class DocumentForm(forms.Form):
        file = forms.FileField(
            validators=[FileExtensionValidator(allowed_extensions=['pdf', 'docx'])]
        )
    

五、实战演练

假设我们要开发一个博客系统,完整的安全配置如下:

# settings.py关键配置
SECURE_SSL_REDIRECT = True  # 强制HTTPS
SESSION_COOKIE_SECURE = True  # 仅HTTPS传输cookie
CSRF_COOKIE_SECURE = True  
SECURE_HSTS_SECONDS = 31536000  # 强制HTTPS一年

# 模型层示例
from django.db import models
from django.utils.html import strip_tags

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    
    def save(self, *args, **kwargs):
        self.content = strip_tags(self.content)  # 保存前去除HTML标签
        super().save(*args, **kwargs)

# 表单验证示例
from django import forms
from django.core.exceptions import ValidationError

def validate_no_html(value):
    if "<" in value or ">" in value:
        raise ValidationError("不能包含HTML标签")

class CommentForm(forms.Form):
    content = forms.CharField(
        validators=[validate_no_html],
        widget=forms.Textarea(attrs={"rows": 4})
    )

部署检查清单

  1. 禁用DEBUG模式
  2. 使用单独的数据库用户并限制权限
  3. 定期更新Django版本
  4. 使用python manage.py check --deploy检查安全问题

六、总结

安全防护就像给房子装防盗门——既不能完全不设防,也不能因为过度防护影响正常使用。Django已经提供了很好的基础防护,但需要开发者正确使用这些功能。记住三个原则:

  1. 永远不要信任用户输入
  2. 该用轮子时别自己造
  3. 安全配置宁可多检查一遍

最后提醒,没有任何系统是100%安全的,定期进行安全审计和渗透测试才能防患于未然。