Django:模板template(二)
将跨站请求伪造和验证码的东西记一下
CSRF
Cross Site Request Forgery。跨站请求伪造
链接:GET请求;表单:POST请求
某些恶意的网站上,包含链接、表单、按钮、JavaScript。利用用户在浏览器上的认证信息试图在网站上完成某些操作,称为CSRF(跨站请求伪造)
例子:
设计两个页面,一个用于提交请求(POST),一个用于展示请求提交的数据
booktest/urls.py
urlpatterns = [ url('^csrf1$',views.csrf1, name="csrf1"), url('^csrf2$',views.csrf2, name="csrf2"), ]
booktest/views.py
def csrf1(request): context = {} return render(request, 'booktest/csrf1.html', context) def csrf2(request): uname = request.POST['uname'] context = {'uname': uname} return render(request, 'booktest/csrf2.html', context)
templates/booktest/csrf1.html
<body> <form method="post" action="/booktest/csrf2"> <input type="text" name="uname"/> <input type="submit" value="提交"/> </form> </body>
templates/booktest/csrf2.html
<body> {{ uname }} </body>
输入姓名,点击提交按钮,会出现如下效果
出现这个是因为django默认使用了CSRF的认证。把Django中的CSRF功能关闭
django4/settings.py
MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', #'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', )
改成发布模式
django4/settings.py
DEBUG = False ALLOWED_HOSTS = ['*']
然后,启动服务器。需要带上IP地址
python manage.py runserver 192.168.30.2:8080
此时,在浏览器上访问服务器的时候,输入的URL是 http://192.168.30.2:8080/booktest/csrf1
注意:现在是真实访问服务器的场景。
任意提交数据,都可以在csrf2中显示(比如嵌入一段html代码)
<script> for(;;) alert(123); </script>
另外,还有一种可能。现在访问服务器上的URL http://192.168.30.2:8080/booktest/csrf1 获得一个网页的时候,可以把网页的源码抠下来。
MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', )
但是禁止了CSRF结果就会连自己都无法访问了(http://192.168.30.2:8080/booktest/csrf1 )所以不能这样做。
防止跨域攻击的方法:在csrf1.html中,在表单中添加一个标签
<body> <form method="post" action="/booktest/csrf2"> {% csrf_token %} <input type="text" name="uname"/> <input type="submit" value="提交"/> </form> </body>
此时,访问http://192.168.30.2:8080/booktest/csrf1 的时候,查看源码,会发现表单中多了一项
这个就是CSRF的保护措施了
此时,只有在服务器的URL http://192.168.30.2:8080/booktest/csrf1 给 http://192.168.30.2:8080/booktest/csrf2 发送请求,才能接收。而在本地的URL file:///D:/gz1833_python/20180918/test.html 给http://192.168.30.2:8080/booktest/csrf2 发送请求,则会有403的错误(Forbidden CSRF verification failed)。
验证码
验证码的作用:防爬虫、减轻服务器的压力、防CSRF
原理:画一张图。图片上随机输出几个字。要求登录的人输入值要和图片随机生成的值一致
首先,我们要让服务器返回一张图片,需要安装一个库pillow
pip install pillow
安装后在python终端尝试:
from PIL import Image, ImageDraw, ImageFont # Image:画布 # ImageDraw:画笔 # ImageFont:字体(推荐使用一个字体库FreeFont) # 注意:访问服务器的图片,响应头中的MIME类型必须为image/png或image/jpeg(之前都是text/html)
编写返回图片验证码的代码
booktest/urls.py
urlpatterns = [ url('^verifyCode$',views.verifyCode, name="verifyCode"), ]
booktest/views.py
def verifyCode(request): from PIL import Image, ImageDraw, ImageFont import random # 规定宽高 宽和高的比例决定验证码的长度是多少 这里是100/25=4 width = 100 height = 25 # 背景色 bgColor = (63,63,63) # 较深 # 创建画布 # 参数1 mode 模式 RGB # 参数2 size 长度为2的元组 (width, height) # 参数3 color 画布的背景色 长度为3的元组 (R,G,B) image = Image.new('RGB', (width, height), bgColor) # 创建画笔 # 参数1 im 画布对象 # 参数2 mode 模式 默认为None draw = ImageDraw.Draw(image) # 创建字体 # 参数1 font 字体文件 # 参数2 字体大小 font = ImageFont.truetype('FreeMono.ttf', 24) # 文字 text = '0123456789' textTemp = '' # 逐个描绘字符 for i in range(4): textTemp1 = text[random.randrange(0,len(text))] textTemp += textTemp1 # 使用画笔对象在画布上描绘字符 # 参数1 xy 要描绘的字符左上角的坐标 长度为2的元组 # 参数2 text 要描述的字符 # 参数3 fill 要描述的字符的颜色 # 参数4 font 字体 draw.text((i*25,0), textTemp1, (255,255,255), font) print(textTemp) # 把画布保存到内存中 #import cStringIO # python2的内存库 import io # python3中的内存库 buf = io.BytesIO() image.save(buf, 'png') # 将内存流中的内容输出到客户端 return HttpResponse(buf.getvalue(), 'image/png')
接下来在网页上显示验证码
booktest/urls.py
urlpatterns = [ url('^verifyCode$',views.verifyCode, name="verifyCode"), url('^verifyTest$',views.verifyTest, name="verifyTest"), ]
booktest/views.py
def verifyTest(request): context = {} return render(request, 'booktest/verifyTest.html', context)
templates/booktest/verifyTest.html
<body> <form method="post" action="/booktest/verifyTest2"> {% csrf_token %} 姓名:<input type="text" name="uname"/><br/> 验证码:<input type="text" name="verifycode"/><img src="/booktest/verifyCode"><br/> <input type="submit" value="提交"/> </form> </body>
在verifyCode中,把随机产生的验证码保存到session中
booktest/views.py
def verifyCode(request): from PIL import Image, ImageDraw, ImageFont ... ... print(textTemp) # 保存到session中 request.session['code'] = textTemp # 把画布保存到内存中 ... ...
添加路由
booktest/urls.py
urlpatterns = [ url('^verifyTest2$',views.verifyTest2, name="verifyTest2"), ]
booktest/views.py
def verifyTest2(request): ori_code = request.session['code'] post_code = request.POST['verifycode'] print('ori_code:%s, post_code:%s' % (ori_code, post_code)) if ori_code == post_code: return HttpResponse('ok') else: return HttpResponse('fail')
访问 http://127.0.0.1:8080/booktest/verifyTest 提交正确/错误验证码,后台可以自行检查了。