一、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>
注意事项:
- AJAX请求需要手动携带CSRF token
- 使用
@csrf_exempt装饰器时要非常谨慎 - 确保
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 }} <!-- 慎用!只有确定内容安全时才用 -->
防御组合拳:
- 使用
mark_safe()前必须确保内容可信 - 富文本内容用
django-ckeditor等专业编辑器 - 设置
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
特别注意:
- 即使使用
raw()方法也要用参数化查询 - 存储过程调用同样需要参数化
- 定期审计项目中的原生SQL语句
四、综合防护策略
实际项目中需要多管齐下:
中间件配置:确保以下中间件启用
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]安全头部:在settings.py中添加
SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_BROWSER_XSS_FILTER = True X_FRAME_OPTIONS = 'DENY'密码处理:
# 不要自己实现加密! from django.contrib.auth.hashers import make_password hashed_pwd = make_password('明文密码') # 自动加盐处理文件上传:限制文件类型和大小
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})
)
部署检查清单:
- 禁用DEBUG模式
- 使用单独的数据库用户并限制权限
- 定期更新Django版本
- 使用
python manage.py check --deploy检查安全问题
六、总结
安全防护就像给房子装防盗门——既不能完全不设防,也不能因为过度防护影响正常使用。Django已经提供了很好的基础防护,但需要开发者正确使用这些功能。记住三个原则:
- 永远不要信任用户输入
- 该用轮子时别自己造
- 安全配置宁可多检查一遍
最后提醒,没有任何系统是100%安全的,定期进行安全审计和渗透测试才能防患于未然。
评论