前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >密码安全与会话安全

密码安全与会话安全

作者头像
ThoughtWorks
发布2021-09-15 15:39:39
1.3K0
发布2021-09-15 15:39:39
举报
文章被收录于专栏:ThoughtWorks

对于登录大家并不陌生,访问系统几乎都需要登录。因为系统也不知道是谁在访问,所以需要你告诉系统你是谁,还需要证明你真的是你,如何证明?给系统展示你的密码,因为密码只有你才拥有,你有这个密码,你就能证明你真的是你,这就是一个登录。

看似简单的几个步骤,但里面涉及的安全问题却有很多。

密码储存安全

首先我们看关于密码存储安全的问题。系统服务器需要存储用户密码,才能在用户登录时验证密码的正确性,但存储就会有泄露的风险,比如数据库被偷,服务器被入侵,内部员工泄露数据,被撞库等风险。因此我们需要认真地考虑如何安全存储用户密码。

我认为作为一名软件开发工程师, 严禁明文存储密码是common sense。那该如何解决不能明文存储密码的问题?也许看官你会说,md5 ?没错,md5 可以。那么md5属于什么?它是一种单向散列算法,单向散列算法主要就是通过算法生成一个摘要,来解决密码不能明文化问题。比如:

md5(123456) = e10adc3949ba59abbe56e057f20f883e

这样虽然能解决密码不明文化,但是它还是会存在安全问题。因为现代计算机的计算能力实在太强了,一秒可以计算十亿次 ,可以通过穷举对比的方式破解密码。这也就是所谓的彩虹表攻击

解决被彩虹表攻击的问题对密码也有一定的要求,比如要求密码的复杂度,需要不同类型的字符进行组合,在生成摘要时加点盐来防止穷举破解密码。但这就安全了吗?还不够。一次算法远远不够满足安全要求,如md5(md5(md5(password+salt))),现在往往采用自适应的方式来存储密码,可以设置重复计算一万次,盐使用随机生成的16+位字符串。

这种方式虽然性能不会很好,但对于密码生成摘要存储来说,性能不好往往是好事,毕竟用户注册或修改密码只是一次操作,用户是可以接受的,但对于黑客来说,这是致命的,黑客从原来的一秒产生几百万甚至上千万的md5值,变成了一秒只能产生一个,黑客想要破解一个密码,从现代的计算机算力来看,需要上千年的时间。目前推荐的使用密码存储算法已不再推荐md5了,推荐采用Bcrypt Scrypt pdkdf2算法。

(很多可以通过MD5/SHA值进行反向查询,都是已经存储了大量的彩虹表)

密码传输安全

解决了密码存储安全,再来看密码传输安全。有人会说使用https就能解决网络传输的安全问题,但这还是不够。浏览器到认证服务器之间请求可能会经过一层或多层gateway,如nginx,zuul,spring cloud gateway等。很多系统都是在gateway处安装证书设置https协议,浏览器到gateway处是加密传输的,但gateway到认证服务器还是http协议。

攻击人可以在这条链路上窃取明文密码,那全链路https不就可以解决问题了?还不够,这些gateway内部都可能会接触到明文密码,都有密码泄露的风险,有些gateway不是我们负责的,无法保证他人会不会开个后门拿出明文密码,或者安全意识较低的程序员打印日志不小心把明文密码打印出来。那如何解决这个问题?我们可以采用浏览器传输密码之前就对密码先加密的方法。

加密方式分为对称加密与非对称加密。

对称加密:加密与解密用的是同一个密钥。如DES,AES非对称加密:加密与解密用的是不同的密钥,一个叫公钥,一个叫私钥,公钥加密的数据只能由对应的私钥才能解,如RSA。

如果采用对称加密方式,需要浏览器在调登录api之前,先获取认证服务器的密钥,拿到密钥后对密码进行加密传输,经过的gateway都只能获取密文,密码到了认证服务器,认证服务器再通过密钥对密文进行解密,获取到密码明文,就可以进行密码验证。但有一个安全问题,浏览器获取密钥也会经过gateway,如果gateway把密钥也打印到了日志中,密文也打印到了日志中,那攻击人同样可以通过日志获取明文密码。

既然对称加密不可取,我们来看看非对称加密。浏览器登录前经过gateway获取认证服务器的公钥,使用公钥进行加密,最终密文到认证服务器,再通过私钥解密拿到明文密码进行密码认证。这种方式gateway只能拿到公钥和密文,无法解密,就算打印到日志中,攻击人无法拿到明文密码了。

但这样就安全了吗?

如果攻击人拿gateway中的密文直接去调认证服务器中的登录api,认证服务器一样可以通过私钥进行解密,并登录成功。所以我们还需要加一些限制:保证密文有过期时间,并且是只能使用一次。

浏览器获取认证服务器公钥时,携带用户名到认证服务器,认证服务器生成随机数并与用户名关联,随机数只保存5分钟,随机数与公钥一起返回给浏览器。浏览器使用随机数加密码通过公钥一起加密调登录api,认证服务器通过私钥解密,获取到明文密码与随机数,验证随机数的有效性与合法性,都正常就进行正常登录,比较完随机数后立刻删除随机数,如不正常拒绝登录。

攻击人就算获取到了密码密文,公钥,随机数,也只能在5分钟之内赶在gateway正常请求登录之前,发起登录攻击,但这个难度太大。还有就是认证服务器保证客户端是gateway或可信的服务发起的请求,认证服务器可以对客户端做白名单限制,方式有很多种,在这就不一一赘述了。

但现在就安全了吗?还真不一定。如果攻击人攻破了gateway,在浏览器请求认证服务器获取公钥时,gateway返回攻击人颁发的公钥,待用户输入完账号密码后,浏览器虽然进行了加密,数据到了gateway,攻击人再通过自己的私钥进行解密拿到明文密码,再通过明文密码在登录页进行正常的登录,同样可以登录成功。因此浏览器也需要做安全验证,验证公钥的合法性。

认证服务器可以采用CA机构颁发的公钥,认证服务器与浏览器都相信CA机构(做安全总得相信点东西,如果什么都不信任就没法做安全了,有永无止境的安全问题),通过CA机构的方式验证公钥的合法性来避免中间人篡改公钥的问题(讲得不是很清楚,比如CA机构是个啥,为什么CA机构可信?这里面可聊的话题太多,有兴趣可以查看《密码学与网络安全》等书籍或一起探讨研究)。

那密码安全了吗?还是远远不够。比如黑客知道了你密码的长度,可以不断地调登录或修改密码的接口来试错,总会试出来正确的密码,因此需要对任何会验证密码合法性的接口都需要加频率限制。如登录连续错了5次锁5分钟,再错5次锁半小时,防止黑客试出密码。但这种方式也有问题。如竞争对手公司不断地使用用户的账号和错误的密码去登录,导致用户的账号一直处于被锁状态,正常用户也没法使用,这就违背了安全中的可用性。那就需要加ip限制和验证码机制了。为了用户的体验性,可以做成第一次登录用户可以正常登录,错误之后,就需要使用验证码的方式登录,超过5次锁定账号,同一ip登录错误次数过多,将ip加入黑名单中。

无密码安全

密码有很多安全问题,复杂密码对于用户来说也挺麻烦的,那采用无密码技术。没有密码是不是就安全了呢?虽然现在可以采用指纹登录与刷脸登录,但新的安全问题也随之而来。密码是需要私密性的,但指纹可以从照片中获取,美国国防部某个官员因在拍照时露出了大拇指,随后就有了这个大拇指的清晰指纹图(拍照的时候不要剪刀手或点赞了,最好指纹对准自己吧,手动狗头)。

还有就是存在不确定性,刷脸登录时,如果灯光太暗或太亮,脸部受伤了,化妆了,那登录能保证成功吗?脸部相似的人,登录时能保证区分开来吗?如果不能就违背了账号唯一性,日后审计也是个问题。还有一个问题就是不可修改。当密码泄露了可以修改密码,但你的指纹已经作为登录凭据了,换个手指头就好了,当十个手指头都用过了,那是不是该用脚指头了?当然无密码肯定是比有密码使用上更方便快捷,随着技术的发展,这些问题也都会解决,只是也会有更多的安全问题。

我们再来看会话安全(密码安全还有各种各样的问题,篇幅有限,不再聊了)。

会话标识储存安全

登录完成后,用户不可能每一次操作都需要输入密码。因此系统需要记录用户的登录状态,又称会话状态。常见的做法是系统保存session。session存入用户信息,生成随机数sessionId,将sessionId返回浏览器,并存入浏览器的cookie中,下一次用户访问系统,携带cookie,系统通过cookie找到session,就可以知道用户是谁

对于集群服务,用户首次登陆,访问的A服务器,A服务器存入session,下次访问到了B服务器,B服务没有session,认为用户没有登陆,提示用户需登陆,这是一个bug。我们将每台服务器都识别到有session就可以解决这个问题了。session存入redis,登陆时往redis存session,之后都从redis取session。或者每台服务器都有session,每台服务器的session同步也能解决这个问题。

不管采用哪种方式,都有一个安全风险,sessionId给出去了,不论sessionId是随机数生成还是加密算出来的字符串,黑客并不关心,黑客只关心这个字符串代表了用户的会话状态。黑客也不需要拿到密码只需要拿到这个字符串,就可以模拟用户进行诈骗,转账,发表非法政治评论等非法活动。

保护sessionId不被非法利用与保护密码同等重要。大多数情况下sessionId存储在cookie中,我们先了解cookie。

这是登录okta后生成的其中一个cookie,有name,value,domain,path,Expires/Max-Age,Httponly,Secure等属性,这里重点介绍其中几个。

  • Domain:cookie对于哪个域有效。这个cookie的域是thoughtworks.okta.com,则只有访问thoughtworks.okta.com下的api,浏览器才会将该cookie发送至后端服务器。这个值可以包含子域,如设置domain为okta.com时,访问thoughtworks.okta.com也会带上该cookie。
  • HttpOnly:当值为true时,告诉浏览器不能通过js访问到该cookie,只有在发送请求到后端时,才会携带该cookie。
  • Secure:当值为true时,告诉浏览器,只有访问协议问https的api时,才会携带该cookie。
  • Expires/Max-Age:cookie有两种,本地cookie与session cookie。如果设置了cookie的过期时间则为本地cookie,不设置为session cookie。session cookie的特点是没有具体的过期时间,随着浏览器关闭而清除。本地cookie即使浏览器关闭也不会清除,而是到了时间自动清除。这也是为什么关闭浏览器后再次打开浏览器有些系统需要重新登录,而有些不需要的原因。

知道cookie的几个特性后我们再来看看攻击人常用的几种攻击方式:XSS攻击,CSRF攻击。

会话标识传输安全

XSS攻击叫做跨站脚本攻击,指用户的输入拼接了正常的html+js+css,变成了带有攻击性的html+js+css。浏览器可能无法识别具有攻击性的html+js+css,按照正常的逻辑执行代码,这可能会导致攻击人偷走cookie(XSS还有其他的危害,但这里仅讨论与会话标识相关)。如果黑客在html中插入隐藏的form表单,通过document.cookie()获取到浏览器中cookie,作为参数并自动发送post请求到攻击人的后端api中,攻击人就可以拿到用户的cookie,也就可以拿到sessionId了。这种方式可以通过设置cookie的HttpOnly为true来防止js获取cookie值。从而避免通过XSS攻击获取sessionId。

CSRF攻击叫做跨站请求伪造。XSS攻击是指本网站的代码执行攻击脚本造成了对本网站的影响。CSRF攻击则是用户打开了其他网站,浏览器执行了其他网站的攻击脚本,却对本网站造成了伤害。举个例子,当我在浏览器中登录了某银行的网站,进行了转账操作,浏览器调用了https://www.xxx.com/transfer?toBankId=123456&money=100,我的账户少了100块,收到短信扣了100块。这时来了一封邮件,标题为你想得到力量吗?内容是一个链接,我点击这个链接,看到url是www.yyy.com/index.htm,立马又收到一个短信,我账号又少了1000块,我刷新下页面,又少1000块。打开页面查看源码,发现有个隐藏的标签,src=https://www.xxx.com/transfer?toBankId=123456&money=1000。也就意味着每次刷新页面,浏览器都会执行一次https://www.xxx.com/transfer?toBankId=123456&money=1000 GET请求。大多数浏览器有同源策略(协议\主机\端口组成源),其中一个限制是同源的网页才会共享cookie。但浏览器对html标签有白名单,img就是其中之一,通过img标签的src就可以发送get 请求,因访问的是xxx(银行)的域名,携带了cookie,银行认为是合法请求,转账成功。因img是get请求,那把转账等高危操作改成post接口不就可以了? 也不行,因为form表单的post请求也在白名单中。

CSRF攻击之所以成功,是因为攻击人可以完全伪造用户的请求,那让攻击人无法伪造就可以解决这个问题了。在转账时,要求用户再次输入密码或输入验证码,就可以解决CSRF攻击。转账操作可以这么做,发表评论这类的操作,每次都要求用户输入密码或验证码用户体验就很很差了。

还有Referer check,浏览器发送请求时,携带Referer header,值为网站url中的域名,异常转账时,虽然调用的www.xxx.com的api,但referer 值为www.yyy.com。在服务端只要验证Referer值就可以判断这是不是一个CSRF攻击。这种方式也有问题,就是完全相信了第三方(浏览器)。对于低版本的浏览器已经有办法可以篡改Referer值,高版本的浏览器目前无法篡改,如用户使用低版本的浏览器,Referer check将无法保证安全性。

那还有什么办法可以解决CSRF攻击的问题? 我们来看下okta是如何做到解决这个问题的。我们登陆okta成功后,打开网页源代码查看html,搜索token可以看到

在span中保存了一个token值 我们再创建一个tab页

打开浏览器的f12,查看网络请求,可以看到request header中有x-okta-xcrftoken这个header。

这就是为了解决CSRF攻击的方式:CSRF Token方式.

csrf token工作原理就是在用户登录成功后,服务端生成token并保存一段时间,返回给浏览器,浏览器保存在html标签中。当用户操作访问后端api时,将该token放入request header中。后端验证该token 的合法性即可判断是否是CSRF攻击。这种方式能生效的重点在于攻击人无法拿到目标网站的html。

最近在思考一个问题,就是如果黑客同时发起XSS攻击和CSRF攻击,这种方式是不是也失效了?黑客通过XSS攻击,获取到了CSRF token,攻击人立马发送钓鱼邮件给目标用户,目标用户点击了链接,网站打开时,先从黑客处获取CSRF Token,并携带CSRF Token发起了CSRF攻击,还有个前提是浏览器版本太低没有Referer,那不就可以攻击成功了?(我杞人忧天了吗?) cookie+session有这么多安全需要考虑,那不要cookie+session不就没这么多问题吗?现在流行的jwt就可以做到无session的登录认证,但jwt也有各种各样的安全问题。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ThoughtWorks洞见 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档