基于Django的电子商务网站开发(连载37)

  • 2019 年 12 月 11 日
  • 筆記

新年加入啄木鸟公众号,好运滚滚來!

顾翔老师开发的bugreport2script开源了,希望大家多提建议。文件在https://github.com/xianggu625/bug2testscript,

主文件是:zentao.py 。bugreport是禅道,script是python3+selenium 3,按照规则在禅道上书写的bugreport可由zentao.py程序生成py测试脚本。

第4章 构建安全的网站

4.1 密码的加密

在第2.3.2节中提醒过大家,前面的代码是明文存储密码的,其实这是很危险的,在这里将对密码进行M5加密,以保证信息的安全。在goods/util.py中定义加密方法md5()

…import hashlib…#MD5加密 def md5(self,mystr): if isinstance(mystr,str): m = hashlib.md5() m.update(mystr.encode('utf8')) return m.hexdigest() else: return ""…

注意:加密字符串mystr必须转为bytes,才可以被加密。然后在注册和登录代码中分别调用该方法。

…#用户注册def register(request):… #获取密码信息 #加密password password = util.md5(password)…#用户登录def login_action(request): … username = (request.POST.get('username')).strip() password = (request.POST.get('password')).strip() #加密password password = util.md5(password)…# 修改用户密码def change_password(request):… if request.method == "POST": #获取旧密码 oldpassword=util.md5((request.POST.get("oldpassword", "")).strip()) #获取新密码 newpassword=util.md5((request.POST.get("newpassword", "")).strip()) #获取新密码的确认密码 checkpassword=util.md5((request.POST.get("checkpassword", "")).strip())…

由于使用MD5对密码进行加密了,同样也需要对测试程序interface/util.py进行下调整。

…#初始化信息 def inivalue(self,dataBase,ordertable,sign): … #建立记录 if(sign!="0"):#sign=0,密码需要加密,否则需要加密 self.insertTable(dataBase,ordertable,values) #处理在用户注册的时候,需要将密码MD5处理 else: dom = minidom.parse("initInfo.xml") self.root = dom.documentElement password = self.root.getElementsByTagName('password') password = str(password[0].firstChild.data).strip() md5password = self.md5(password) newvalues = values.replace(password,md5password) self.insertTable(dataBase,ordertable,newvalues) return values…

4.2 防止CSRF攻击

4.2.2 CSRF攻击介绍

跨站请求伪造(Cross-siterequest forgery: CSRF),也被称为One Click Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。听起来有点像跨站脚本(在第4.4中进行介绍),但它与XSS是不同的,XSS利用的是站点内信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任网站。与XSS攻击相比,CSRF不是流行的攻击方式,对其进行防范的资源也相当稀少,且难以防范,所以业界认为其比XSS更具危险性。

用一个POST请求做个比方,黑客可以构建自己的网页form界面,form的action指向要攻击的网站,form中元素的name与攻击网站的值保持一致,从而达到CSRF攻击的目的。

比如被攻击的网站是http://www.a.com,页面提交网站是http://www.a.com/input.html,提交后处理的网站是http://www.a.com/display.jsp,input.html的网页内容如下。

…<form action="display.jsp" method="post" >地址:<input type="text" name="address" id="id_address" size="20" maxlength="100" required />电话:<input type="text" name="phone" id="id_ phone" size="20" maxlength="100" required /></form>…

现在在本地构造一个界面来冒充input.html。

…<form action="http://www.a.com/display.jsp" method="post" >地址:<input type="text" name="address" id="id_address" size="20" required />电话:<input type="text" name="phone" id="id_ phone" size="20"required /></form>…

这样黑客就可以用自己的页面向http://www.a.com/display.jsp发起攻击了。在作者著作《软件测试技术实战 设计、工具及管理》一书中序言中曾经提及这么一件事情:

“2000年我所在的公司与CCTV‘开心辞典’目组合作开发网上答题的项目,这是一个智力娱乐性节目,我编写了前端的答题代码,考虑到可能有人用计算机程序来答题,比如编写一个死循环,一直选择B(或A或 C或 D),这可以使答题的速度很快,命中率也非常高,为此我选用JavaScript过滤了使用死循环的答题者。可是到了开心辞典’正式使用这个软件的时候,发现仍然有人使用死循环来答题,可我的程序是正确的。后来在一次聊天模块中,通过登录账号找到了这位‘达人’,他说我们前端的确没有漏洞,他是通过自己编写的程序绕过我们前端进入到系统后端的,而我们的后端并没有进行校验。当初如果有专业的测试人员,这个Bug是有可能避免的。”其实这就是一个很典型的CSRF攻击。

4.2.3 Django是如何防范CSRF攻击的

在第2.3.2节就介绍过Django是如何防范CSRF攻击机制的,而且Django默认是启动CSRF攻击机制的,在本书前几个章介绍的重点不在这里,所以把setting.py中的这个开关给关了。现在进入setting.py打开这个开关。

…MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',]…

见粗体字部分。然后在所有模板有表单提交(<form>…</form>)的地方都加上一个{% csrf_token%}这个标记。最后把views.py中所有render_to_response()方法用render()方法替换(csrf不支持render_to_response()方法,正如2.4.2中所述render_to_response()逐步被render()取代)。现在以登录模块来分析Django是如何防范CSRF攻击的。在此之前,打开一个HTTP抓包工具,作者这里用的是Fiddle 4,然后进入登录界面,查看网页源代码会发现。

…<input type='hidden' name='csrfmiddlewaretoken' value='XltpK31i171tGLIH2leLWio0xM5TY8NC56oaU58CiIc5xLfqSiiehfJDSEnZesrX ' />…

<input type='hidden' name='csrfmiddlewaretoken' value='XltpK31i171tGLIH2leLWio0xM5TY8NC56oaU58CiIc5xLfqSiiehfJDSEnZesrX ' />…

<input type='hidden' name='csrfmiddlewaretoken' value='XltpK31i171tGLIH2leLWio0xM5TY8NC56oaU58CiIc5xLfqSiiehfJDSEnZesrX ' />…

也就是说{%csrf_token%}被一个名为csrfmiddlewaretoken的hidden类型给取代了。其值为XltpK31i171tGLIH2leLWio0xM5TY8NC56oaU58CiIc5xLfqSiiehfJDSEnZesrX一个一百位的字符串,然后查看Fiddle 4,会看见页面产生了一个名为csrftoken的cookie,其值也为XltpK31i171tGLIH2leLWio0xM5TY8NC56oaU58CiIc5xLfqSiiehfJDSEnZesrX,如图4-1所示。

图4-1 产生的cookie

如果刷新这个登录页面,发现这100个字符串会发生相应地变化,但是cookie的值与hidden中的值是永远保持一致的。后来作者查询了一些资料,发现不仅仅是Django是用这种方式处理CSRF注入的,其他大部分系统都是使用这种方法处理CSRF注入的。即在用户登录这个网站的时候产生一个叫做csrf token(csrf令牌)的随机字符串,即前面提到的100位会发生随机变化的字符串,然后把这csrftoken放入到cookie中(所以要是用CSRF防御机制,必须打开浏览器的cookie),并且放到页面的form表单中,产生一个类似于<input type='hidden' name='csrfmiddlewaretoken' value='csrf token'的表单,然后在提交表单的时候验证cookie中的值是不是与hidden的值保持一致,如果保持一致,则返回200代码,否则返回403拒绝访问代码。参见图4-2。

图4-2 CSRF防范示意图

星云测试

http://www.teststars.cc

奇林软件

http://www.kylinpet.com

联合通测

http://www.quicktesting.net