web安全防范

版权声明:如需转载请著明出处 https://blog.csdn.net/qq_37653449/article/details/86391058

XSS 跨站脚本攻击

简单的说,就是攻击者想尽一切办法将可以执行的代码注入到网页中。

持久型也就是攻击的代码被服务端写入进数据库中,这种攻击危害性很大,因为如果网站访问量很大的话,就会导致大量正常访问页面的用户都受到攻击。

场景就是在做表单提交的时候  写入一个script标签执行一些恶意代码

这种情况如果前后端没有做好防御的话,这段评论就会被存储到数据库中,这样每个打开该页面的用户都会被攻击到。

非持久型相比于前者危害就小的多了,一般通过修改 URL 参数的方式加入攻击代码,诱导用户访问链接从而进行攻击。

场景:如果页面需要从 URL 中获取某些参数作为内容进行相应操作的话 ,不经过过滤就会导致攻击代码被执行。

但是对于这种攻击方式来说,如果用户使用 Chrome 这类浏览器的话,浏览器就能自动帮助用户防御攻击。但是我们不能因此就不防御此类攻击了,因为我不能确保用户都使用了该类浏览器。

对于 XSS 攻击来说,通常有两种方式可以用来防御。

1转义字符

首先,对于用户的输入应该是永远不信任的。最普遍的做法就是转义输入输出的内容,对于引号、尖括号、斜杠进行转义

通过转义可以将攻击代码 <script>alert(1)</script> 变成 &lt;script&gt;alert(1)&lt;&#x2F;script&gt;

但是对于显示富文本来说,显然不能通过上面的办法来转义所有字符,因为这样会把需要的格式也过滤掉。对于这种情况,通常采用白名单过滤的办法,当然也可以通过黑名单过滤,但是考虑到需要过滤的标签和标签属性实在太多,更加推荐使用白名单的方式。

const xss = require('xss')
let html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>')
// -> <h1>XSS Demo</h1>&lt;script&gt;alert("xss");&lt;/script&gt;
console.log(html)

2.CSP 内容安全策略 

CSP 本质上就是建立白名单,开发者明确告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则,如何拦截是由浏览器自己实现的。我们可以通过这种方式来尽量减少 XSS 攻击。

通常可以通过两种方式来开启 CSP:

  1. 设置 HTTP Header 中的 Content-Security-Policy

   为使CSP可用, 你需要配置你的网络服务器返回  Content-Security-Policy  HTTP头部 

  • 只允许加载本站资源                      Content-Security-Policy: default-src ‘self’
  • 只允许加载 HTTPS 协议图片       Content-Security-Policy: img-src https://*
  • 允许加载任何来源框架                 Content-Security-Policy: child-src 'none'

   2.设置 meta 标签的方式

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

对于csp来说,只要开发者配置了正确的规则,那么即使网站存在漏洞,攻击者也不能执行它的攻击代码,并且 CSP 的兼容性也不错。

CSRF  跨站点请求伪造

什么是 CSRF 攻击?  如何防范 CSRF 攻击?

CSRF 跨站点请求伪造  :通俗的讲 ,CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

情景假设:

假设我们有一个银行账户,其中有一个登录页面login.php和付款页面paybill.php,这些页面都属于我们信任的网站test.com(test.com网站是虚拟的)。

login.php中设置cookie进行登录:

<?php
    setcookie('uid', 1, time()+86400);
    echo "your uid is {$_COOKIE['uid']}"

paybill.php通过身份验证后进行扣款,但是必须输入收款人和扣款金额:

<?php
   //身份验证
    if (!isset($_COOKIE['uid']) || $_COOKIE['uid']< 0) {  
        die('login error!');
    }
   //金额获取   
    if (!isset($_GET['money'])) {
        die('no money');
    }   
   //收款人获取
    if (!isset($_GET['to_who'])) {
        die('nobody');
    }   
    $uid = $_COOKIE['uid']; 
    $money = $_GET['money'];
    $to_who = $_GET['to_who'];
   //此处应该还有相关DB操作,省去一万字
    echo "transfer {$money} yuan to {$to_who}!";

刷新一下login.php,进行登录(实际的用户登录更为复杂,这里进行简单模拟了)

在浏览器访问paybill.php页面,转钱1000元给妈妈

这时候黑客发现test.com网站没有做任何防御措施,他立刻在自己的网站B上伪造了一个页面,页面上有这么一个链接,他的收款人to_who变成了hacker。

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <a href="http://test.com/csrf/paybill.php?money=1000&to_who=hacker" taget="_blank">震惊!!史上尺度最大的照片!!<a/>
    </body> 
</html>

假设因为好奇心点击了该连接:

这样就转了1000元给了hacker

为什么会出现这种情况,我们在别的网站点击链接居然能扣自己账户的钱?点击链接前,我们已经登录了信任网站test.com,而这个test.com/csrf/paybill.php?money=1000&to_who=hacker这个连接是我们自己发送的,test.com会识别当前已经登录,然后转账,test.com网站无法判断到底是谁让我们点击的。

从上面这个实例可知,完成CSRF攻击流程:

1、用户登录了信任的网站A,并且保存登录状态

2、黑客找出网站A没有防御的链接,通过社会工程学伪装,诱导点击。

3、只要登录状态保持,用户主动访问目标链接,则攻击成功。

有人说那每次访问其他网站,把之前的网站都注销。是的,这个办法可以,但这么做这现实吗?我们需要注销许多常用的网站,下次登录又要输入用户名和密码,极其反人类。这肯定不是最佳办法,防御措施应该让程序员考虑,用户别乱点链接是最重要的。

CSRF的攻击渠道不一定来自其他网站,也可以是广告邮件、QQ空间、微信、facebook等社交媒体或软件。试想一下,如果你的女朋友知道这个链接,她在QQ上发给你

http://test.com/csrf/paybill.php?money=1000&to_who=girlfriend你点击后,那就转了1000元给女朋友,假设她将money改成10w,后果真的不敢想象,你居然存了这么私房钱,跪搓衣板吧,钱也都到了你女朋友账户上。

POST也能造成CSRF攻击

上面的CSRF可以说相当危险,更新资源的操作不应该使用GET方式,GET方式只应该用于读操作。更新操作一定要使用POST方式,特别涉及到钱的问题。

然而POST方式可以解决大部分的CSRF问题,还有剩下少部分的聪明的黑客,一样能够模拟POST请求,伪造身份进行攻击。

假设paybill.php 我们修改为POST取:

   

<?php
    if (!isset($_COOKIE['uid'])) {
        die('login error!');
    }   
    if (!isset($_POST['money'])) {
        die('no money');
    }   
    if (!isset($_POST['to_who'])) {
        die('nobody');
    }   
    $uid = $_COOKIE['uid']; 
    $money = $_POST['money'];
    $to_who = $_POST['to_who'];
    if ($uid > 0) {
        echo "transfer {$money} yuan to {$to_who}!";
    }

访问http://test.com/csrf/paybill.php?money=1000&to_who=girlfriend,提示没有money

但是道高一尺魔高一丈,聪明的黑客也改进了代码,使用POST提交,并且money改为2000:

抓包结果如下,可以看到Cookie: uid=1money=2000&to_who=girlfriend都已发送过去。

POST http://test.com/csrf/paybill.php HTTP/1.1
Host: test.com
Referer: http://hack.com/hack/welcome.php
Cookie: uid=1

money=2000&to_who=girlfriend

如此一来,不管哪种访问方式都可能受到攻击。所以,这并不是GET和POST谁更安全的问题,POST只是提高了攻击门槛和成本(其实也就多几行html和js)。

划重点,那么CSRF能够攻击的根本原因是:服务器无法识别你的来源是否可靠。

防御CSRF的思想

那么防御的方法有很多:

1、比如加上验证码。但这么做很繁琐,并且影响用户体验。

2、比如转账需要二次密码验证,现在很多银行就这么搞的。

3、确认来源是否可靠(推荐)

不管防御方法1还是2,都是让用户自身再次确认授权。

这种安全防范的事儿,更应该由程序验证。

根据验证是否可靠性思路,可以有以下几种方法:

验证HTTP Referer 字段

HTTP协议里面定义了一个访问来源的字段,这个字段叫Referer。黑客伪造的链接或表单是在其他网站上,所以我们可以判断Referer是否为自身网站,如果是,则允许访问,如果不是,则拒绝访问。

从我们的网站访问paybill.php,抓包发现Referer是不存在的

"HTTP_REFERER"=>""

从黑客的网站访问paybill.php,抓包发现Referer来自黑客网站

["HTTP_REFERER"]=> string(35) "http://hack.com/welcome.php"
然后代码里判断:

if  (HTTP_REFERER!="")  {
    die('允许访问');
} else {
   die('可能是CSRF攻击,拒绝访问');
}

所以我们只需要拦截Referer就可以判断是否为攻击。

但是这种方法是有缺陷的,上面实验尝试过,如果对方在QQ上发送给你一个链接呢?点击的时候属于主动点击,此时一样没有Referer。程序会把它归属为安全请求,那么就被绕过了。并且如果某些低版本的浏览器存在漏洞(比如IE6),Referer很有可能被篡改,所以这个方法并非十全十美。

服务端验证请求的token一致性

csrf攻击的核心原理就是利用用户验证信息储存cookie中,发送请求,使得服务器无法判断真伪,而token之所以能够拦截,就是因为它是csrf攻击过程中几乎不可能伪造的东西。

实现原理:在服务端生成一个随机的token,加入到HTTP请求参数中,服务器拦截请求,查看发送的token和服务端的是否一致,若一致,则允许请求;若不一致,则拒绝请求。

新增form.php表单页面,将token存入session(不要存在cookie中,你懂的):

<?php
session_start();
$csrf_token = md5(openssl_random_pseudo_bytes(32));//生成随机token
$_SESSION['token']= $csrf_token;
?>
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>csrf</title>
</head>
<body>
    <h1>转账</h1>
    <form action="paybill.php" method="POST">
        money:<input type="text" name="money">
        to_who:<input type="text" name="to_who">
        <input type="hidden" name="token" value="<?php echo $csrf_token ?>">
        <input type="submit" value="ok">
    </form>
</body>
</html>

paybill.php获取token,与session中存储的token判断是否一致:

<?php
    session_start();
    if (! isset($_COOKIE['uid'])) {
        die('login error!');
    }   
    if (! isset($_POST['money'])) {
        die('no money');
    }   
    if (! isset($_POST['to_who'])) {
        die('nobody');
    }   
    if (! isset($_POST['token']) || $_POST['token'] !=  $_SESSION['token']) {
        die('forbidden');
    }   
    $uid = $_COOKIE['uid']; 
    $money = $_POST['money'];
    $to_who = $_POST['to_who'];

    if ($uid > 0) {
        echo "transfer {$money} yuan to {$to_who}!";
    }

查看页面form.php

请求成功:

 每次访问表单页面,都应该生成一个token:

  

<input type="hidden" name="token" value="a88f67a7effa917450cff12e179df35d">

我们再尝试从黑客网站进行访问,显示”forbidden”,证明在token验证时被拦截:

这样子就已经有效防御csrf攻击。该方法可以用于a链接和表单等请求,属于同一个原理。

注意:网上很多文章并没有生成唯一的或者随机性较大的token,都是同一个token,这是有问题的,如果黑客看到该token,一样可以伪造请求,进行攻击。

Ajax防御CSRF

实际上A jax防御的思想也可以利用上面的token验证方式

把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。

CSRF防御原则:

  • GET方式不能用于更新资源的操作
  • POST方式请求加上随机token验证

点击劫持

什么是点击劫持?如何防范点击劫持?

点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击

攻击者实施攻击的一般步骤是:

  1. 黑客创建一个网页利用iframe包含目标网站;
    2)隐藏目标网站,使用户无法察觉到目标网站存在;
    3)构造网页,诱骗用户点击特定按钮 (图1中的PLAY!按钮);
  2. 用户在不知情的情况下点击按钮,触发执行恶意网页的命令。

比较重要的点击劫持漏洞利用技术包括目标网页隐藏、点击操作劫持、拖拽技术。

1、目标网页隐藏技术

目标网页隐藏技术原理是攻击者在恶意网站上通过 iframe 载入目标网页,然并隐藏目标网页,欺骗用户点击隐藏的恶意链接。目前主要的网页隐藏技术有两种:CSS隐藏技术和双iframe隐藏技术。
CSS 隐藏技术的原理是利用 CSS 技术控制网页内容显示的效果。其中opacity参数表示元素的透明度,取值范围为0~1,默认值为1表示不透明, 取值为0时元素在网页中完全透明显示。当设置目标 iframe 的opacity 属性小于或等于0.1,用户就无法看到含恶意代码的目标网页。
双iframe隐藏技术使用内联框架和外联框架。内联框架的主要功能是载入目标网页,并将目标网页定位到特定按钮或者链接。外联框架的主要功能是筛选,只显示内联框架中特定的按钮。

2、点击操作劫持

在成功隐藏目标网页后,攻击者下一个目标是欺骗用户点击特定的按钮,最简单实用的方法是使用社会工程学。例如,将攻击按钮外观设计成类似QQ消息的提示按钮,诱使用户点击从而触发攻击行为。另外一种思路是使用脚本代码以及其他技术增加用户点击特定按钮的概率。主要方法如JavaScript实现鼠标跟随技术、按键劫持 (Stroke jacking) 技术等。

3、拖拽(Drag and Drop)技术

主流的浏览器都有drag-and-drop API 接口,供网站开发人员创建交互式网页。但是,这些 API 接口在设计时没有考虑很多的安全性问题,导致通过拖拽就可以实现跨域操作。利用拖拽技术,攻击者可以突破很多已有的安全防御措施,
利用拖拽技术,攻击者可以轻易将文本注入到目标网页。在实际实施过程中,攻击者欺骗用户选择输入框的内容,完成拖拽操作。另外一种方式是,通过浏览器的 API 接口将 iframe 中的内容拖拽到目标网页的 text area 中,攻击者就可以获得用户网页中存在的敏感信息。

(二)、点击劫持实例

以下是一个简单的点击劫持攻击实例:
攻击者构造一个恶意链接诱使用户访问,若用户不慎打开了此链接,用户看到的是正常的页面。此例中制作了一个简单的“CLICK HERE !”,如图2所示:

实际上这个页面是由两个 iframe 嵌套而成,用户看到的是一个正常的页面。实则在其表面,覆盖了一个恶意链接的页面,click here按键的操作也相应的落在了这个恶意的页面上,对于整个过程,用户全然不知。
攻击者是怎样把这个恶意页面隐藏起来的呢?这主要是通过设置 CSS 中 div 元素的不透明度来实现的,在 CSS 中声明 opacity 用来设置一个元素的透明度。现在主流的浏览器都支持opacity属性,取值为由浮点数字和单位标识符组成,不可为负。Opacity 取值为1的元素是完全不透明的,反之取值为0时为全透明,即用户是看不见的。攻击者正是利用了opacity为0来隐藏了这个页面。
接下来,我们不妨把 opacity 值改为0.5,即设置表面的 iframe 为半透明,是不是就可以看见攻击者想要隐藏的那部分内容呢?结果如大家所料,我们就能够隐约看见被攻击者隐藏的iframe。如图3:

图3 攻击者构造的恶意网页(opacity值为0.5)
可以看出,在button上面其实覆盖了一个钓鱼网站,在不经意间,用户就已经落入攻击者制作的陷阱,被劫持到一个虚假的购物网站中。

对于这种攻击方式,推荐防御的方法有两种。

X-FRAME-OPTIONS

-FRAME-OPTIONS 是一个 HTTP 响应头,在现代浏览器有一个很好的支持。这个 HTTP 响应头 就是为了防御用 iframe 嵌套的点击劫持攻击。

该响应头有三个值可选,分别是

  • DENY,表示页面不允许通过 iframe 的方式展示
  • SAMEORIGIN,表示页面可以在相同域名下通过 iframe 的方式展示
  • ALLOW-FROM,表示页面可以在指定来源的 iframe 中展示

JS 防御

<head>
  <style id="click-jack">
    html {
      display: none !important;
    }
  </style>
</head>
<body>
  <script>
    if (self == top) {
      var style = document.getElementById('click-jack')
      document.body.removeChild(style)
    } else {
      top.location = self.location
    }
  </script>
</body>

以上代码的作用就是当通过 iframe 的方式加载页面时,攻击者的网页直接不显示所有内容了

中间人攻击

什么是中间人攻击?如何防范中间人攻击?

中间人攻击是攻击方同时与服务端和客户端建立起了连接,并让对方认为连接是安全的,但是实际上整个通信过程都被攻击者控制了。攻击者不仅能获得双方的通信信息,还能修改通信信息。

通常来说不建议使用公共的 Wi-Fi,因为很可能就会发生中间人攻击的情况。如果你在通信的过程中涉及到了某些敏感信息,就完全暴露给攻击方了

当然防御中间人攻击其实并不难,只需要增加一个安全通道来传输信息。HTTPS 就可以用来防御中间人攻击,但是并不是说使用了 HTTPS 就可以高枕无忧了,因为如果你没有完全关闭 HTTP 访问的话,攻击方可以通过某些方式将 HTTPS 降级为 HTTP 从而实现中间人攻击

参考链接:https://cloud.tencent.com/developer/article/1004943

https://www.jianshu.com/p/251704d8ff18

猜你喜欢

转载自blog.csdn.net/qq_37653449/article/details/86391058