@CSS魔法
2015. 12. 30.
(45~60 分钟)
三个要点:
危害程度取决于:
千万不要看我用 alert 来演示,
就以为 XSS 只能 alert……
从 HTML 说起:
<div class="topic">
<h1>防范 XSS 的注意事项与最佳实践</h1>
<p class="description">
XSS 的含义是 “跨站脚本攻击”。简单来
说,就是我们的网页……
</p>
</div>
原则:
让内容
尤其是用户生成的内容
保持绿色属性
但是,HTML 本身是不带颜色的……
<div class="topic">
<h1>如何使用 <p> 标签</h1>
<p class="description">
……
……
</p>
</div>
如果标题叫作《如何使用 <p> 标签》:
<div class="topic">
<h1>本期分享的标题</h1>
<p class="description">
本期分享的讲师、背景、内容介绍等
信息…… <scrip>/*...*/</script>
</p>
</div>
如果内容来自用户:
如何解决?
原始内容
HTML 代码
显示结果
<
<
<
<
<
<
网页作者
浏览器
<
因此,特殊字符都需要转义:
>
'
"
&
...
再来看 HTML 中的 JS:
<script>
var postTitle = '二手 iPhone 6 九成新'
</script>
再来看 HTML 中的 JS:
<script>
var postTitle = '<?= $data->postTitle ?>'
</script>
用户输入的帖子标题是:
'; /* ... */; '
“SQL 注入” 的即视感?
因此:
要点:
写数据时统一转义?
内容的存储策略:
?
“写库前转义” 的问题:
还有一个问题……
还记得这个例子吧:
<script>
var postTitle = '<?= $data->postTitle ?>'
</script>
但是……
<script>
var keyword = '<?= $_GET["query"] ?>'
</script>
你还是要操心转义的问题!
数据并不一定总是来自数据库:
或者,来自第三方……
甚至还要操心哪些该转,哪些不该转!
所以:
什么叫 “最佳实践”:
在面对需求时
条件反射般地采用 “成熟的对策”
即可确保不出问题
No Zuo, No Die!
需求一
(把后端变量输出到向 HTML 中)
对策:
模板引擎
div(class=$data->var1)
= 我的名字叫 #{$data->var2} ……
PHP-Jade
div @class=var1
"我的名字叫 {var2} ……"
Jedi
需求二
(后端通过模板向前端脚本传递数据)
需求 2.1
传递单个变量
<script>
var postTitle = '<?= $data->postTitle ?>'
</script>
div.post
h2.post-title #{$data->postTitle}
p ...
对策 2.1
PHP-Jade
var postTitle = $('h3.post-title').text()
JS
script = [postTitle]
!
window.__postTitle = data[0]
对策 2.1
Jedi
伪需求
传递 URL 参数
<script>
var keyword = '<?= $_GET["query"] ?>'
</script>
对策
var keyword = _.url.getParam('query')
JS
JS 本身是有解析 URL 参数的能力的!
需求 2.2
传递复杂数据
<script>
var list = '<?= json_encode($data->postList) ?>'
</script>
script#json-list(type='text/json').
!{$data->jsonList}
对策 2.2
PHP-Jade
var list = JSON.parse($('#json-list').html())
JS
PHP (Controller)
$postList = [/* ... */];
$templateData->jsonList = json_stringify($postList);
json_stringify()
是我们对 json_encode()
的包装,已针对 XSS 做作优化,默认处理所有必要的特殊字符。
script = [postList]
!
window.__list = data[0]
对策 2.2
Jedi
PHP (Controller)
$templateData->postList = [/* ... */];
需求三
(前端脚本拿到数据后更新页面)
需求 3.1
填入单个变量
$('h3.post-title').html(postTitle)
JS
对策 3.1
$('h3.post-title').text(postTitle)
JS
需求 3.2
把复杂数据渲染为 HTML
var html = ''
_.each(list, function (item) {
html += '<li>' + item.title + '</li>'
})
$('ul.post-list').html(html)
JS
对策 3.2
前端模板引擎
// 定义模板
_.template.add('list-items', [
'<% _.each(data, function (item) { %>',
'<li><%= item %></li>', // 这里会自动转义
'<% }) %>',
].join('\n'))
JS
// 定义模板
_.template.add('list-items', [
'<% _.each(data, function (item) { %>',
'<li><%= item %></li>', // 这里会自动转义
'<% }) %>',
].join('\n'))
// 用模板渲染数据,得到 HTML 片断
var html = _.template.render('list-items', list)
JS
// 定义模板
_.template.add('list-items', [
'<% _.each(data, function (item) { %>',
'<li><%= item %></li>', // 这里会自动转义
'<% }) %>',
].join('\n'))
// 用模板渲染数据,得到 HTML 片断
var html = _.template.render('list-items', list)
// 更新页面
$('ul.post-list').html(html)
JS
番外篇
死法一
div
= $data->var
PHP-Jade
PHP-Jade
div
- echo $data->var;
死法二
extends layout
block append scripts
script.
function handleData() {/* 一些业务逻辑 */}
var data = {
user: "<?= $data->user->username ?>", // 妥妥地 XSS
desc: "<?= $data->user->descr ?>" // 妥妥地 XSS
};
handleData(data);
PHP-Jade
Thank You!