Slide 1

Slide 1 text

WEB 前端安全 ——从 MVVM 框架说起

Slide 2

Slide 2 text

WEB 安全的最大敌人 XSS(Cross-site Scripting) 2 1.

Slide 3

Slide 3 text

“ 3 31.80% 22.30% 7.80% 4.60% 4.50% 4.20% 3.80% 3.50% 3.00% 2.90% 11.80% 2016 年 TOP10 漏洞比例分布 XSS 信息泄露类 权限控制 后台弱密码 SSRF 逻辑设计缺陷 安全性误配置 可执行漏洞 暴力破解

Slide 4

Slide 4 text

4 怎么对付 XSS 保护 Cookie • HttpOnly • Secure 设置请求头 • CSP • X-XSS-Protection 过滤 • < > • script onerror 转义 • escape() • HTMLEncode() 十八般武艺齐上阵

Slide 5

Slide 5 text

5 事情好像没有那么简单 编码 • URL encode • Unicode 其他 • window.name • ES6 feature (alert`1`) • iframe 字符集 • ISO-2022-JP • UTF-7 关键字 • scrscriptipt • String.fromCharcode() • location.hash 各种花式绕过技巧

Slide 6

Slide 6 text

MVVM 自动编码输出 天生预防 XSS 和 CSRF 6 Angular、React、Vue …

Slide 7

Slide 7 text

正确使用 MVVM 框架 模板注入(Client-Side Template Injection) 7 2.

Slide 8

Slide 8 text

Flask 用的 Jinja2 模板引擎 什么是模板注入 8 @app.errorhandler(404) def page_not_found(e): template = '''{%% extends "layout.html" %%} {%% block body %%}

Oops! That page doesn't exist.

%s

{%% endblock %%} ''' % (request.url) return render_template_string(template), 404

Slide 9

Slide 9 text

9 发生了什么? 模板引擎计算了数学表达式 7 + 7 = 14 然后将结果输出到了页面上 如果换成 {{ config.items() }} 呢? 127.0.0.1:5000/invalid{{ 7+7 }}

Slide 10

Slide 10 text

“ 10 不能将用户输入直接作为模版内容的一部分

Slide 11

Slide 11 text

用 AngularJS 举个栗子 11
{{ 7+7 }}
» 直接生成前端模板 » {{ 7 + 7 }} » AngularJS 解析和执行 » 输出 14 » 那如果换成 {{ alert(1) }} 会咋 样?

Slide 12

Slide 12 text

AngularJS 里的沙箱 什么都没发生, 源码里连 alert 都不见了 ! 12

Slide 13

Slide 13 text

AngularJS 里的沙箱 Sandbox 对模板和表达式进行检查、过滤、解析、重写 » ensureSafeMemberName » ensureSafeObject » ensureSafeFunction 沙箱这么厉害,那还担心什么呢? 13

Slide 14

Slide 14 text

AngularJS 里的沙箱 绕过 从 1.0.1 版开始 再次绕过 1.6 以下的每个版本 废除沙箱 1.6 版本开始 14

Slide 15

Slide 15 text

框架的安全机制不是万能的 严格遵守开发文档,提防用户的输入 15

Slide 16

Slide 16 text

危险的模板和表达式 16 有多种方法可以控制模板和表达式 表达式在解析时包含用户提供的内容: » $compile(userContent) » $parse(userContent) » $interpolate(userContent) 表达式中使用管道时条件包含用户提供的内容: » {{ value | orderBy : userContent }} 在生成 AngularJS 模板时包含用户提供的内容。 表达式时在调用下面的方法时包含用户提供的内容: » $watch(userContent, ...) » $watchGroup(userContent, ...) » $watchCollection(userContent, ...) » $eval(userContent) » $evalAsync(userContent) » $apply(userContent) » $applyAsync(userContent)

Slide 17

Slide 17 text

危险的模板和表达式 17 » 不要混合客户端和服务器模板。 » 不要使用用户输入动态生成模板。 » 不要使用之前列出的表达式函数运行用户输入 如果必须在 AngularJS 模板中使用用户提供的内容, 需要确保它在通过 ngNonBindable 指令明确指定了不编译的模板部分中。

Slide 18

Slide 18 text

正确使用 MVVM 框架 在 DOM 中插入 HTML 及使用第三方库 18 3.

Slide 19

Slide 19 text

向 DOM 中插入 HTML 19 错误做法(一) 关闭自动转义 // Within the controller // Disables strict auto escaping $sce.enabled(false); $scope.html = "Hello " + userInput + "!"; // Within the view
{{html}}

Slide 20

Slide 20 text

向 DOM 中插入 HTML 20 错误做法(二) 使用 element.html() 插入 HTML // Within the controller angular.element(someElement) .html("Hello " + userInput + "!")

Slide 21

Slide 21 text

向 DOM 中插入 HTML 21 错误做法(三) 使用 ngBindHtml + trustAsHtml // Within the controller $scope.html = $sce.trustAsHtml("Hello " + userInput + "!"); // Within the view
{{html}}

Slide 22

Slide 22 text

向 DOM 中插入 HTML 22 错误做法(四) 使用 ngBindHtml + trustAsHtml 然后自己写 XSS 过滤函数 // Within the controller var escapedUserInput = escapeForHtml(userinput); $scope.html = $sce.trustAsHtml("Hello " + escapedUserInput + "!"); // Within the view
{{html}}

Slide 23

Slide 23 text

向 DOM 中插入 HTML 23 正确做法 使用 ngBindHtml + sanitizer

var userInput = 'test<svg/onload=alert(1)>'; var myApp = angular.module('myApp', ["ngSanitize"]); var controller = myApp.controller('myCtrl', function($scope) { $scope.html = "Hello <b>" + userInput + "</b>!" });

Slide 24

Slide 24 text

混用第三方库时的风险 24 开发时往往会用到很多第三方库 单独使用时可能没有漏洞, 但与 AngularJS 混合使用时就出现 问题了。 // A non angular-related library. // Secure without Angular. Insecure with Angular. document.write(escapeForHTML(userInput));

Slide 25

Slide 25 text

混用第三方库时的风险 25 » EscapeForHTML 常见转义函数 » userInput 用户输入 » 没有引入 Angular 时一切正常 » 引入 Angular 后 » 输入沙箱绕过的 Payload » BOOM!
function escapeForHTML(unsafe) { return unsafe .replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/"/g, "&quot;") .replace(/'/g, "&#039;"); } var userInput = '{{x = {"y":"".constructor.prototype}; x["y"].charAt=[].join;$eval("x=alert(1)");}}'; document.write(escapeForHTML(userInput));

Slide 26

Slide 26 text

“ 26 复制粘贴网上的代码时要留心

Slide 27

Slide 27 text

正确使用 MVVM 框架 资源 URL 黑白名单 27 4.

Slide 28

Slide 28 text

AngularJS 校验 URL 的方式 28 » $sceDelegateProvider.resourceUrl(White|Black)list([list]) » 默认只能访问同一域名下的资源 » 有三种校验方式:’self’, String 和 Regxes(正则) » 其中 String 支持两种通配符: * :匹配除 : / . ? & 和 ; 之外的任意数量字符 **:匹配所有的字符

Slide 29

Slide 29 text

AngularJS 校验 URL 的方式 29 错误做法(一) 在协议处使用通配符 // Whitelist all possible schemes "**://example.org/*" ● Exploit 1: http://evil.com/?ignore=://example.org/a ● Exploit 2: javascript:alert(1);//example.org/a // Less permissive, but still bad "*://example.org/*" ● Exploit 1: javascript://example.org/a%0A%0Dalert(1) 注释 换行

Slide 30

Slide 30 text

AngularJS 校验 URL 的方式 30 错误做法(二) 在域名里使用 ** 通配符 // Whitelist all possible subdomains "https://**.example.org/*" ● Exploit 1: https://evil.com/?ignore=://example.org/a // Whitelist all possible top level domains "https://example.**" ● Exploit 1: https://example.evil.com ● Exploit 2: https://example.:[email protected]

Slide 31

Slide 31 text

AngularJS 校验 URL 的方式 31 错误做法(三) 使用正则表达式 // Use a RegEx to whitelist a domain /http:\/\/www.example.org/g ● Exploit 1: // (dots are not escaped) http://wwwaexample.org ● Exploit X: All the wildcard-based exploits can be applied as well

Slide 32

Slide 32 text

AngularJS 校验 URL 的方式 32 结论 » 尽量不要使用正则表达式 » 不要在协议中使用通配符 » 不要在域名中使用 ** » 只在子域名和网站路径中使用 * » 最好的方式是仅列出特定的 URL 作为白名单

Slide 33

Slide 33 text

正确使用 MVVM 框架 前端工程的新问题 33 5.

Slide 34

Slide 34 text

Webpack 打包 34

Slide 35

Slide 35 text

SourceMap 的隐患 35

Slide 36

Slide 36 text

SourceMap 的隐患 36

Slide 37

Slide 37 text

37 解决办法 » 线上环境删除 SourceMap » 前端工程打包时配置按需加载 » 对于既有用户界面又有后台管理界面的应用,不要做成一个 SPA » RESTful 接口权限验证,使用 JWT

Slide 38

Slide 38 text

38

Slide 39

Slide 39 text

如何写出安全的应用 遵循规范 熟读文档 从容细心 39

Slide 40

Slide 40 text

Any questions? 联系方式: » YjByZ0BxcS5jb20= » https://0x0d.im 40 THANKS!