Django中通过GET请求存储搜索日期的安全性及合理性问询
问题解答
1. GET请求写入数据库是否合理?
完全不合理。HTTP协议里,GET的语义是获取资源,要求多次相同请求不会改变服务器状态(幂等)。你现在用GET创建Statistics记录,属于修改服务器状态的操作,既违反HTTP规范,还会带来实际问题:
- 浏览器会缓存GET请求,用户刷新页面就会重复创建统计记录,导致数据失真;
- 搜索引擎爬虫会抓取带参数的URL,生成大量无效的统计数据。
正确的做法要么把搜索表单改成POST提交(同时处理筛选和统计),要么保留GET的URL可分享性,用前端异步POST请求单独提交统计数据(比如用户点搜索时,发个POST到统计接口)。
2. 有没有安全漏洞?
有几个明显的风险:
- 垃圾数据攻击:攻击者可以用脚本批量发送带不同参数的GET请求,瞬间把Statistics表塞满无效数据,拖垮数据库;
- 无效输入导致服务器错误:如果传入的日期格式不合法,而你的Statistics模型用的是DateField,直接调用
create会抛出数据库异常,触发500错误; - 无验证的无效数据:比如用户传入
check_in晚于check_out的参数,筛选逻辑会失效,同时这种无效数据也会被存入统计,影响分析结果。
3. 数据类型与验证问题
- 不管表单是隐藏输入还是普通输入,GET请求的参数永远是字符串格式,HTTP请求里所有参数都是字符串,Django不会自动做类型转换;
- 仅检查数据类型远远不够,必须做完整的业务验证:
- 验证日期格式是否合法;
- 验证退房日期必须晚于入住日期;
- 可以额外限制日期范围(比如不能是3年前的日期,或者超过1年的未来日期)。
最稳妥的方式是用Django表单类来处理参数清洗和验证,示例代码如下:
先定义表单类
from django import forms from django.core.exceptions import ValidationError class SearchForm(forms.Form): check_in = forms.DateField(required=False, widget=forms.HiddenInput) check_out = forms.DateField(required=False, widget=forms.HiddenInput) def clean(self): cleaned_data = super().clean() check_in = cleaned_data.get('check_in') check_out = cleaned_data.get('check_out') if check_in and check_out: if check_in >= check_out: raise ValidationError("退房日期必须晚于入住日期") return cleaned_data
视图中使用表单
class ListingsView(ListView): model = Listing template_name = 'orders/listings.html' def get_queryset(self): form = SearchForm(self.request.GET) queryset = Listing.objects.all() if form.is_valid(): check_in = form.cleaned_data['check_in'] check_out = form.cleaned_data['check_out'] # 仅在参数有效且非重复请求时创建统计记录 if check_in and check_out: # 用session避免同一用户短时间重复搜索产生重复统计 last_search = self.request.session.get('last_search') current_search = (check_in.isoformat(), check_out.isoformat()) if last_search != current_search: Statistics.objects.create(check_in=check_in, check_out=check_out) self.request.session['last_search'] = current_search # 执行筛选逻辑 if check_in and check_out: available_calendars = Calendar.objects.filter( date__gte=check_in, date__lt=check_out, is_available=True ).values_list('listing_id', flat=True) queryset = queryset.filter(id__in=available_calendars) return queryset
另外注意:隐藏输入可以被用户通过浏览器开发者工具随意修改,绝对不能依赖前端来保证数据合法性,所有验证必须在后端完成。
内容的提问来源于stack exchange,提问作者Kevin D.
相关产品推荐
相关产品推荐

