引言
SDL/SDLC, 全称Secure Software Development Lifecycle,中文翻译成安全开发流程或者安全开发生命周期,以下简称SDL。SDL中有个环节叫安全测试,这里所说的安全测试并不是大家熟知的入侵测试(Penetration Test),而是针对安全功能的测试,比如权限管理,登录,修改密码等等。对于此类测试并不严格要求测试者具备很深的安全背景,最小要求QA角色即可,当然如果测试者有开发经验和入侵测试经验就更好了,那样不仅能发现问题,还能比较准确的定位导致问题的原因,能够大大缩短问题的修复时间。
无意中发现公众号管理平台有个密码长度问题,这个问题正好对应到SDL中的安全测试,那我们就通过这个案例来了解一下安全测试。
图1
(一) 问题的表象
开始注册微信公众号,既然是推广安全的公众号,怎么也得设个安全些的密码吧,于是弄了一个19位的密码
图2
用19位的密码注册成功
既然注册成功了登录进去看看怎么玩,结果死活登不进去
图3
通过忘记密码功能把密码设成了9位的,能正常登录了。
(二) 问题分析及验证
1.查看登录过程中浏览器和公众号平台之间的通信
图4
红框里就是登录时输入的密码,根据长度能判断是MD5的哈希值,很容易验证,我使用的是苹果的笔记本电脑, 系统自带一个命令md5
当你输入 md5 -s ThisIsAPasswor
命令返回如下
MD5 ("ThisIsAPasswor") = 96c982f6d80cf4019baa80f2b73b54b5
96c开头的一串32位长的字符就是明文ThisIsAPasswor的哈希值,计算哈希时没加盐,这里也无需加盐,底层通信有https保护.
Ok, 现在我们知道了,开发团队的实现是在客户端用javascript(js文件貌似是在https://res.wx.qq.com/mpres/en_US/htmledition/3rd/md5/下)把密码的哈希值使用md5算出来,然后发送到服务器端来比较验证。
2.准备测试数据
那我们来准备一些测试数据,是不同长度的密码以及对应的md5哈希值:
20位密码
MD5 ("ThisIsAPassword12345") = ca509486f8e8056841e20493b7a429c8
19位密码
18位密码
MD5 ("ThisIsAPassword123") = 604dbca00aa545e5a518c91bde649d8c
17位密码
MD5 ("ThisIsAPassword12") = efb9e748abd2d2b3e4139de93cd0ba7f
16位密码
15位密码
MD5 ("ThisIsAPassword") = 96342e37c4862b4eb2227440d129325b
14位密码
MD5 ("ThisIsAPasswor") = 96c982f6d80cf4019baa80f2b73b54b5
3.使用准备好的数据进行测试
我们由长到短尝试使用这些密码来登录,测试的时候为防止服务器端的一些安全措施被触发影响测试我们可以只让客户端的计算发生,http request的发送动作也可以有,但不让request提交到服务器端,这个可以通过打开Burp Suite的intercept来实现,如下图,将intercept 设置成on
图5
也就是说当密码长度大于等于16位时客户端始终都是截取16位长的字符作为密码的,而当密码长度小于16位时,客户端就严格按照用户输入的长度来计算哈希值了
登录时出于安全考虑不管你输入任何密码都会给你相同的提示,也不会告诉你密码长度过长(这个后面再给大家稍微解释),那我们再去看一下修改密码的行为(注册的过程反复测太麻烦,还是用修改密码来测试吧)
修改密码的页面如下图所示
图6
当将新密码改为20位长度的ThisIsAPassword12345时Burp Suite 截取的http通信如下图
图7
这时发现当我们希望把密码改成20位长的ThisIsAPassword12345时,实际上客户端post给服务器端的哈希值是4d6f7a6b354703388cd5f284cf4cf380,也就是说客户端暗地里是把16位密码的哈希值ThisIsAPassword1发给服务器了,截掉了后面的4个字符.关键是这个时候客户端竟然提示我密码修改成功了。
虽然没有全部测试,但根据9位密码能修改并正常使用来推测,修改密码的程序逻辑可能是和登录是一样的:当密码长度大于等于16位时客户端始终都是截取16位长的字符作为密码的,而当密码长度小于16位时,客户端就严格按照用户输入的长度来计算哈希值了。
(三) 总结
微信公众号管理平台对密码的长度是有要求的(8-16位),在设置密码和使用密码时程序会判断,如果用户输入的长度大于等于16位,系统会自动截取至16位长度来计算md5哈希值,如果实际长度小于16位,会直接使用实际长度,当然了还有个8位长度的下限。那么问题是什么呢,问题是对于用户来讲用户会发现我成功设置一个很长的密码(被认为会很安全),但是登录却登录不进系统。
我本人倒觉得这个问题从安全角度来讲不算是一个安全问题,我更认为它是个用户体验的问题,但在实际运营中一旦有用户遇到这个问题并反馈到客服部门,通常这种投诉都是归类到账户安全上的,然后就是相关团队的一番折腾,客服找开发团队,开发找测试,测试找程序员,产品经理也会被牵扯进来。
现在回到主题,SDL中的安全测试所关注的内容刚好就是针对这个领域的,正常情况下测试人员会在测试前写好完善的测试用例,下面是可能的测试用例标题
用户在注册时可以使用最长8-16位的密码
用户可以使用8-16位密码登录系统
用户修改密码时新密码可以使用8-16位长度密码
实际执行这些测试用例时会进一步细化,选取不同密码长度,例如6,8,12,16,20,QA同学们会很熟悉为什么会这样选取。
那么通过这些测试用例测试人员是可以在安全测试这个阶段将问题发现并反馈给研发团队的,理想的解决方案是在注册和修改密码时明确提示并用户系统允许的密码长度,只有符合要求的密码才可以被使用。安全问题早些发现就能早些解决,既能省时间也能省钱。所以SDL既可以帮助在开发过程中将安全注入身边还有其他作用,比如提升用户体验 :)。
最后再说一下微信公众号管理平台的登录,登录的交互过程技术实现还是很不错的,不管账号错误还是密码错误,用户在页面上所能看到的信息都是
"您输入的帐号或者密码不正确,请重新输入"
并且http response 里的信息也都是
{"base_resp":{"err_msg":"acct/password error","ret":200023}}
做的很标准,相信在服务器端所记录的信息不会这么模糊滴,猜想会有例如哪个IP什么时间使用了那个账号和密码进行登录,是密码错误还是账号不存在,为基于日志的攻击分析提供有用信息.再想远一点大家会不会觉得最后这段的内容和OWASP Top10的A2: Broken Authentication关系紧密呢?
The End
如果你觉得这篇文章对周围人有用,请帮忙推荐,谢谢。
暂时还没有留言功能,想留言的可以在公众号里直接留言。
领取专属 10元无门槛券
私享最新 技术干货