XSS 防护 99%的人知道转义过滤,50%的人知道 httponly,但是只有1%的人知道它

周梦康
防止xss 99%的都知道要做标签过滤,和标签属性过滤,50%知道非标签内容转义,40%知道httponly,10%的人知道 waf,只有1%的人知道它。

周末尝试用跑路技巧来强行引出 CSP ,故事有点牵强,本想大家都一起留言说如何解决这种 self-xss 的问题,但是效果不是很好,这里我自己回应下吧~~

对于这种”顽固型“存储内容,存在更高权限的用户编辑其他人低权限用户的内容的情况。下面这个方案应该能在一定程度上解决问题。

image

当然 csp 和 httponly 的指定也都是服务端输出的。waf 是一层可以帮助我们拦截掉一些攻击代码的网络防火墙,属于云产品范畴,大家可以自行搜索相关云产品。

今天主要和大家分享学习下 CSP (Content Security Policy)的知识,CSP 里定义的内容有很多,不仅可以防止被 XSS 还有被 iframe 或者 iframe 引用等

假如我们提供的是一个存储型的服务,比如博客。
案例 http://mengkang.net/demo/csp/...

<html>
<header>
    <title></title>
</header>
<body>
<div id="blog">
    <script>
        alert(document.cookie);
    </script>
</div>
</body>
</html>

也就是说"#blog"里面的内容是不可信的,一方面要做好展示时的过滤,假如我们没做好过滤,上面的 js 就会执行;另一方面,我们也不能随意修改用户的内容,所以即使展示时过滤了,而原始内容不能修改,当编辑用户的内容时,依然会触发xss,也就是self-xss,这主要攻击的就是有编辑其他人内容的人。

CSP 主要是为了解决跨站脚本攻击和数据注入攻击,它的核心原理是在服务端渲染页面的时候 http header 头里带上 CSP 协议,协议里面有一个nonce(服务端随机生成的码,不需要存储),然后页面需要执行的 js 必须也必须带上该nonce

拦截攻击

案例 http://mengkang.net/demo/csp/...

<?php
$nonce  = md5(uniqid());
$policy = "script-src 'nonce-" . $nonce . "';";

header("content-security-policy:". $policy);
?>
<html>
<header>
    <title></title>
</header>
<body>
<div id="blog">
    <script>
        alert(document.cookie);
    </script>
</div>
</body>
</html>

这样,用户写的博客里面的 js 就不会被执行了。并且控制台上会有报错记录

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'nonce-be6737d35f2c558943dae9ad44c369ee'". Either the 'unsafe-inline' keyword, a hash ('sha256-0FWdMKk2PWuytGHwpB/r+HwqVTWZoSWX/M+OKSueWHI='), or a nonce ('nonce-...') is required to enable inline execution.

但是如果页面有一些我们自己开发的 js 要执行怎么办?

内部引入 js 的放行

案例 http://mengkang.net/demo/csp/...

<?php
$nonce  = md5(uniqid());
$policy = "script-src 'nonce-" . $nonce . "';";

header("content-security-policy:". $policy);
?>
<html>
<header>
    <title></title>
</header>
<body>
<div id="blog">
    <script>
        alert(document.cookie);
    </script>
</div>
<div id="notice">
    <script nonce="<?php echo $nonce?>">
        alert("我是自己人,别拦截");
    </script>
</div>
</body>
</html>
原理就是我们自己的 js script 标签上加上了服务端渲染的nonce值,所以能正常执行;而攻击者写的博客里面的js是写死的,即使他写了一个 nonce,但是他没法控制“被攻击者”打开页面时的nonce值正好等于他写死的那个值。

外部引入 js 的放行

案例 http://mengkang.net/demo/csp/...

<?php
$nonce  = md5(uniqid());
$policy = "script-src 'nonce-" . $nonce . "';";

header("content-security-policy:". $policy);
?>
<html>
<header>
    <title></title>
</header>
<body>
<div id="blog">
    <script>
        alert(document.cookie);
    </script>
</div>
<div id="notice">
    <script nonce="<?php echo $nonce?>">
        alert("我是自己人,别拦截");
    </script>
</div>
<script src="test.js"></script>
</body>
</html>

test.js是不能被引入的,需要改造成

<script src="test.js" nonce="<?php echo $nonce?>"></script>

如果外部引入 js 有动态插入的情况,一定要把nonce传递给外部 js。比如test.js背后是一段服务端代码,里面会执行document.write,解决方案是

<script src="test.js?nonce=<?php echo $nonce?>" nonce="<?php echo $nonce?>"></script>

test.js在收到参数nonce 之后,对后面动态插入的js做类似上面的操作两类操作添加nonce

行内 js 改造

如果页面中有大量的行内 js,需要改造成内部引入 js 的方式

<span onclick="alert(1);"></span>

这样的js 是不能被执行的,需要改造成

<span id="myButton"></span>

<script nonce="<?php echo $nonce?>">
    document.getElementById("myButton").addEventListener("click", myFunction);
    function myFunction(){
        alert(1);
    }
</script>

最终版

加上各种浏览器的兼容性,还有CSP1和CSP2的兼容性之后得到以下经过实战项目的规则

content-security-policy: base-uri 'self';script-src 'self' 'unsafe-inline' 'unsafe-eval' 'report-sample' https: http: 'strict-dynamic' 'nonce-543c368ef6f944e1a93fa60d39687a02';frame-src 'self' a.baidu.com b.taobao.com ;worker-src blob: 'self' data:;object-src 'self' a.baidu.com;frame-ancestors *.aliyun.com;report-uri /csp/report;

可以参考这个去改下

  • frame-src控制我们可以引入的页面域名
  • frame-ancestors控制谁可以引用我们
  • report-uri是 csp 拦截日志上报地址

原创码字不易,期待你的关注,我的公众号
image

阅读 2.6k

周梦康
金三银四 还不上车补课?学了就是赚了 [链接]

退隐江湖

8.9k 声望
6.7k 粉丝
0 条评论

退隐江湖

8.9k 声望
6.7k 粉丝
宣传栏