更新于 

Web应用安全/核心防御机制

问题

问题

旨在阻止跨站点脚本攻击的输入确认机制按以下顺序处理一个输入:
(1)删除任何出现的<script>表达式;
(2)将输入截短为50个字符;
(3)删除输入中的引号;
(4)对输入进行URL解码;
(5)如果任何输入项被删除,返回步骤(1)。
是否能够避开上述确认机制,让以下数据通过确认?

1
“><script>alert(“foo”)</script>
官方答案

是。
如果没有第4步,此机制将是可靠的,能够过滤其旨在阻止的特定项目。
但是,由于输入在执行过滤步骤后被解码,攻击者只需要对有效载荷中的选定字符进行URL编码,就可以避开这种过滤:
“>
如果首先执行第4步,或根本不执行该步骤,攻击者将不可能避开上述过滤。

代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function inputFilter(src){
let hasInvalid = false
// 1. 删除任何出现的<script>表达式
const reg = /<(\/)?script>/g
if(reg.test(src)){
src = src.replaceAll(reg, '')
hasInvalid = true
}

// 2. 将输入截断为50个字符
if(src.length>50){
src = src.slice(0,50)
hasInvalid = true
}

// 3. 删除2输入中的引号
let signReg = /['"]/g
if(signReg.test(src)){
src = src.replaceAll(signReg, '')
hasInvalid = true
}

// 4. 对输入进行URL解码
src = urlencode.decode(src)

if(hasInvalid){
return inputFilter(urlencode.encode(src))
}
return src
}

这时如果输入的数据经过攻击者编码:

1
2
// %22=" %27=' %3C=< %3E=>
const src = '%22>%3Cscript>alert(%22foo%22)%3C/script>'

输出结果显示字符串被解析为跨站脚本攻击字符串:

1
2
3
4
5
--------------------------------------
目标输出:><script>alert("foo")</script>
输入:%22>%3Cscript>alert(%22foo%22)%3C/script>
输出:"><script>alert("foo")</script>
--------------------------------------