如果你的网站中允许匿名用户通过POST方式提交表单, 比如用户注册表, 评论表或者留下用户联系方式的表单,你一定要防止机器人或爬虫程序恶意提交大量的垃圾数据到你的数据库中。这种情况不是可能会发生,而是一定会发生。一种解决这种问题的方式就是在表单中加入人机交互验证码(CAPTCHA), 另一种方式就是在表单中加入honeypot隐藏字段,然后在视图中对隐藏字段的值进行验证。两种验证方式的目的都是一样,防止机器人或程序通过伪装成人来提交数据。今天我们就来详细介绍下如何在表单中添加honeypot增加安全性。

Honeypot的工作原理

Honeypot又名蜜罐,其实本质上是种陷阱。我们在表单中故意通过CSS隐藏一些字段, 这些字段一般人是不可见的。然而机器人或程序会以为这些字段也是必需的字段(required), 所以会补全后提交表单,这就中了我们的陷阱。在视图中我们可以通过装饰器对用户提交的表单数据进行判断,来验证表单的合法性。比如honeypot字段本来应该为空的,现在居然有内容了,显然这是机器人或程序提交的数据,我们可以拒绝其请求。

Django中如何实现表单honeypot验证?

Django表单中添加honeypot,一共分两步:

1. 编写模板标签(templatetags),在包含模板的表单中生成honeypot字段。

2. 编写装饰器(decorators.py), 对POST请求发送来的表单数据进行验证。

由于honeypot的功能所有app都可以用到,我们创建了一个叫common的app。整个项目的目录结构如下所示。只有标蓝色的4个文件,是与honeypot相关的。

编写模板标签

我们在common目录下新建templatetags目录(包含一个空的__init__.py),然后在新建common_tags_filters.py, 添加如下代码。

from django import template
from django.conf import settings
from django.template.defaultfilters import stringfilterregister = template.Library()# used to render honeypot field
@register.inclusion_tag('common/snippets/honeypot_field.html')
def render_honeypot_field(field_name=None):"""Renders honeypot field named field_name (defaults to HONEYPOT_FIELD_NAME)."""if not field_name:field_name = getattr(settings, 'HONEYPOT_FIELD_NAME', 'name1')value = getattr(settings, 'HONEYPOT_VALUE', '')if callable(value):value = value()return {'fieldname': field_name, 'value': value}

我们现在来看下上面这段代码如何工作的。我们创建了一个名为render_honeypot_field的模板标签,用于在模板中生成honeypot字段。honeypot字段名是settings.py里HONEYPOT_FIELD_NAME,如果没有此项设置,默认值为name1。honeypot字段的默认值是HONEYPOT_VALUE, 如果没有此项设置,默认值为空字符串''。然后这个函数将fieldname和value传递给如下模板片段。

# common/snippets/honeypot_field.html

<div class="form-control" style="display: none;"><label><input type="text" name="{{ fieldname }}" value="{{ value }}" /></label>
</div>

在Django模板的表单中生成honeypot字段只需按如下操作:

{% load common_tags_filters %}
{% load static %}<form method="post" action="">{% csrf_token %}{% render_honeypot_field %}{% form.as_p %}
</form>

相关阅读

Django基础(16): 模板标签(tags)的分类及如何自定义模板标签

编写装饰器

在common文件下新建decorators.py, 添加如下代码。我们编写了check_honeypot和honeypot_exempt两个装饰器,前者给需要对honeypot字段进行验证的视图函数使用,后者给不需要对honeypot字段进行验证的视图函数使用。

#common/decorators.py

from functools import wraps
from django.conf import settings
from django.http import HttpResponseBadRequest, HttpResponseForbidden, HttpResponseRedirect
from django.template.loader import render_to_string
from django.contrib.auth.decorators import user_passes_testdef honeypot_equals(val):"""Default verifier used if HONEYPOT_VERIFIER is not specified.Ensures val == HONEYPOT_VALUE or HONEYPOT_VALUE() if it's a callable."""expected = getattr(settings, 'HONEYPOT_VALUE', '')if callable(expected):expected = expected()return val == expecteddef verify_honeypot_value(request, field_name):"""Verify that request.POST[field_name] is a valid honeypot.Ensures that the field exists and passes verification according toHONEYPOT_VERIFIER."""verifier = getattr(settings, 'HONEYPOT_VERIFIER', honeypot_equals)if request.method == 'POST':field = field_name or settings.HONEYPOT_FIELD_NAMEif field not in request.POST or not verifier(request.POST[field]):response = render_to_string('common/snippets/honeypot_error.html',{'fieldname': field})return HttpResponseBadRequest(response)def check_honeypot(func=None, field_name=None):"""Check request.POST for valid honeypot field.Takes an optional field_name that defaults to HONEYPOT_FIELD_NAME ifnot specified."""# hack to reverse arguments if called with str paramif isinstance(func, str):func, field_name = field_name, funcdef wrapper(func):@wraps(func)def inner(request, *args, **kwargs):response = verify_honeypot_value(request, field_name)if response:return responseelse:return func(request, *args, **kwargs)return innerif func is None:def decorator(func):return wrapper(func)return decoratorreturn wrapper(func)def honeypot_exempt(func):"""Mark view as exempt from honeypot validation"""# borrowing liberally from django's csrf_exempt@wraps(func)def wrapper(*args, **kwargs):return func(*args, **kwargs)wrapper.honeypot_exempt = Truereturn wrapper

上面代码最重要的就是verify_honeypot_value函数了。如果用户通过POST方式提交的表单里没有honeypot字段或该字段的值不等于settings.py中的默认值,则验证失败并返回如下错误:

# common/snippets/honeypot_error.html

<!DOCTYPE html>
<html lang="en"><body><h1>400 Bad POST Request</h1><p>We have detected a suspicious request. Your request is aborted.</p></body>
</html>

定义好装饰器后,我们对需要处理POST表单的视图函数加上@check_honeypot就行了,是不是很简单?

from common.decorators import check_honeypot@check_honeypot
def signup(request):if request.method == "POST":form = SignUpForm(request.POST)if form.is_valid():user = form.save()login(request, user)return HttpResponseRedirect(reverse('users:profile'))else:form = SignUpForm()return render(request, "users/signup.html", {"form": form, })

相关阅读

一文看懂Python系列之装饰器(decorator)(工作面试必读)

Django基础(26): 常用装饰器应用场景及正确使用方法

参考

本文核心代码参考了James Sturk的Django-honeypot项目。原项目地址如下所示:

https://github.com/jamesturk/django-honeypot/

大江狗

2020.9.24

喜欢我们的公众号就留言或点个赞吧?

Django实战:给表单添加honeypot验证增加安全性相关推荐

  1. vue表单的数据验证

    1.在el-form中通过属性:rules进行绑定,它的值为验证规则对象rules <el-form :model="ruleForm" :rules="rules ...

  2. Django(part46)--form表单验证

    学习笔记,仅供参考 文章目录 form表单验证 举个例子 form表单验证 form提供表单和字段验证,我们可以使用form.is_valid() 方法进行表单验证,若该方法返回值为True,则表示当 ...

  3. jQuery框架学习第十一天:实战jQuery表单验证及jQuery自动完成提示插件

    jQuery框架学习第一天:开始认识jQuery jQuery框架学习第二天:jQuery中万能的选择器 jQuery框架学习第三天:如何管理jQuery包装集  jQuery框架学习第四天:使用jQ ...

  4. Django中的表单如何使用? Django如何验证前端发来的数据? ✧*。٩(ˊᗜˋ*)و✧*。 Django初体验

    文章目录 前期准备 前端准备 表单基础使用 创建表单 表单类型 Field CharField(Field) IntergerField(Field)与 FloatField(Filed) Decim ...

  5. Django基础(11): 表单集合Formset的高级用法详解

    Formset(表单集)是多个表单的集合.Formset在Web开发中应用很普遍,它可以让用户在同一个页面上提交多张表单,一键添加多个数据,比如一个页面上添加多个用户信息.今天小编我就介绍下Djang ...

  6. Django的Form表单

    Django的Form表单 Django Form  Form介绍 Form 表单是在前端向后端提交数据最常用的方式,同时在好多场景下都需要对用户的输入进行校验. 以注册为例,Form 需要的三件事: ...

  7. html表单输入框添加验证码,织梦Dedecms为自定义表单添加验证码功能

    使用织梦Dedecms自定义表单的时候,即使你做了字段的验证,也很有可能被人刷很多垃圾的内容,更加安全的一个方法是为自定义表单添加上验证码功能.今天我就来为大家分享一下怎样给自定义表单添加验证码! 一 ...

  8. glassfish hk2_使用GlassFish 3.1.2.2和Primefaces 3.4的JDBC领域和基于表单的身份验证

    glassfish hk2 我的博客上最受欢迎的帖子之一是有关JDBC安全领域和带有Primefaces的GlassFish上基于表单的身份验证的简短教程. 在收到有关它不再适用于最新的GlassFi ...

  9. java jaas_基于Java JAAS表单的身份验证

    java jaas 使用JAAS实现登录模块是一个高级主题,而且大多数开发人员也很少有机会参与这种开发. 但是JAAS登录模块的基本实现不是那么难实现,这是因为我打算将其发布. 在这里,我正在解释如何 ...

最新文章

  1. Knockout 新版应用开发教程之visible绑定
  2. list steam_在 Steam 中国版上玩单机游戏也会受到防沉迷系统管控
  3. python技能(1)-map函数
  4. san分布式共享文件系统_【最强科普】一文读懂分布式存储
  5. java web初级面试题_Java Web应用程序初学者教程
  6. android 支付宝 地图,利用百度地图实现支付宝“到位”功能(地图模式)
  7. 报错:1130-host ... is not allowed to connect to this MySql server
  8. mysql 2008 教程_sql 2008 视频教程数据库从入门到精通自学视频教程_IT教程网
  9. 图像标签制作工具之labelImg-windows的安装与使用
  10. 显著性 / 注意力机制
  11. 树莓派linux led字符设备驱动(新字符设备)
  12. Python常用英文单词【最强总结】
  13. 天然气阶梯是按年还是按月_天然气阶梯价是按年算还是月算
  14. 国产操作系统 UOS 安装教程
  15. 2020/5/19 微机原理
  16. 减少数据库死锁的8种方法
  17. matlab线性预测函数,线性预测及其Matlab实现
  18. MQTTX与阿里云IOT平台交互,发布订阅消息,以及将设备属性发送到云平台
  19. ansys建钢管混凝土模型
  20. TCP/IP协议学习记录之九:Traceroute程序

热门文章

  1. centos共享文件夹_文件共享总结中篇Linux服务器文件共享
  2. FileStream读写文件
  3. gb28181提供的功能
  4. 【Python网络蜘蛛 · 4】:代理ip的了解和基本使用、查看代理ip的有效性(附源代码)
  5. 愚人节,我为你准备了活动
  6. 学游戏开发,从客户端还是服务端开始?
  7. 探索云原生技术之基石-Docker容器高级篇(3)
  8. 关于外企IT的发展职位(抬头)和薪水
  9. java四个数打擂_Java语言逻辑 数组
  10. 最近的工作心得与反思