通过服务器端令牌阻止形式双重提交(Django)

时间:2017-07-20 13:49:35

标签: python django django-forms django-views

我正在尝试实施服务器端检查,以防止用户双重提交我的表单(Django网络应用)。

我正在尝试的一种技巧是:

1)创建表单时,在会话中保存唯一ID,并将唯一ID值传递给模板。

2)提交表单时,从会话中弹出唯一ID,并将其与从表单中检索到的相同唯一ID进行比较。

3)如果值相同,则允许处理,否则不允许。

这些SO answers为我制定了这一点。

以下是我的通用代码的快速浏览:

def my_view(request):
    if request.method == 'POST':
        secret_key_from_form = request.POST.get('sk','0')
        secret_key_from_session = request.session.pop('secret_key','1')
        if secret_key_from_form != secret_key_from_session:
            return render(request,"404.html",{})
        else:
            # process the form normally
            form = MyForm(request.POST,request.FILES)                
            if form.is_valid():
                # do something
            else:
                # do something else
    else:
        f = MyForm()
        secret_key = uuid.uuid4()
        request.session["secret_key"] = secret_key
        request.session.modified = True 
        return render(request,"my_form.html",{'form':f,'sk':secret_key})

这是一个示例模板:

<form action="{% url 'my_view' %}" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="hidden" name="sk" value="{{ sk }}">
    {{ form.my_data }}
    <button type="submit">OK</button>
</form>

此设置有失败以停止双重提交。

即,一个人可以继续点击狂热,最终仍然会提交大量的副本。此外,如果我打印secret_key_from_formsecret_key_from_session,我会看到它们被多次打印,即使第一次尝试后secret_key_from_session应该弹出

这有什么用?我该如何解决?

UPDATE:当我使用redis缓存来保存特殊键的值时,这种安排非常有效。因此,似乎罪魁祸首是我无法更新request.session值(即使尝试request.session.modified=True)。我对可能出现问题的建议持开放态度。

请注意,这个问题专门针对双重提交的服务器端解决方案 - 我的JS措施是独立的,并且是这个问题所独有的。

2 个答案:

答案 0 :(得分:0)

您可能只需要request.session.modified = True。如果您想确保会话正在删除,您也可以使用del。

secret_key_from_session = request.session.get('secret_key','1')
del request.session['secret_key']
request.session.modified = True

答案 1 :(得分:0)

我无法弄清楚导致问题的原因,但通过用Redis缓存替换每个request.session来电,我能够得到我想要的结果。我愿意接受建议。