2007年5月24日星期四

[Tips]Do not Echo into Javascript

by jno[jno_0x71@yahoo.com.cn]
date: 2007-05-25
http://www.ph4nt0m.org

<?php
function htmlencode($str)
{
    
$str = htmlspecialchars($str);
    
$str = preg_replace("/\'/i", "'", $str);
    
return $str;
}

$str = htmlencode($_GET['str']);
?>
<div style="width: 200px; height: 200px; background-color: #ccc;" 
onmouseover
="javascript: alert('<?php echo $str;?>')" />

如果你习惯用上面的方式写代码,把用户输入输出到客户端作为javascript的一部分,那么就有问题了,即使你有编码用户输入的习惯也无济于事,因为用户输入可以使用引号来闭合javascript的字符串变量,然后加入他自己的恶意代码:

http://localhost/vul_code.php?str=aaa');alert('bbb

你这时可能还有点疑问,我htmlencode了,单双引号也被转义成&quot;和&#39;了,怎么还可能突破呢?事实上htmlencode了的内容作为html元素的属性里的脚本时,是被按照它的原意来执行的,你可以试试这个例子就明白了:

<input type="button" value="双引号" 
onclick
="javascript: alert(&quot;双引号& quot;);" />
<input type="button" value="单引号" 
onclick
="javascript: alert('单引号');" />

那么,怎么样解决这样的问题,一个办法就是再对字符串进行javascript转义,把单双引号分别转义成\"和\',避免利用这两个符号突破,但是要转义全了还是需要做点文章,因为单双引号的变形形式还是很多样的,比如:

双引号:&quot; &#34; &#x22; &#0034; &#000034; &#00000034; ...
单引号:&#39; &#x27; &#0039; &#000039; ...

难免会有疏漏,所以这样做不太好,如果有疏漏过滤就会被突破。另外有一种思路来解决这个问题就是避免这样的写法,为什么非要从服务器端输出到javascript里呢,我们很容易可以用其他办法实现相同的效果,又更加安全:

<?php
function htmlencode($str)
{
    
$str = htmlspecialchars($str);
    
$str = preg_replace("/\'/i", "'", $str);
    
return $str;
}

$str = htmlencode($_GET['str']);
?>
<div style="width: 200px; height: 200px; background-color: #ccc;" 
onmouseover
="javascript: alert(this.getAttribute('server_data'))" 
server_data
="<?php echo $str;?>"/>

把数据输出到一个属性里,这里的server_data就是我们开辟的一个服务器数据岛,因为是输出到普通的属性里面,所以htmlencode就可以保证它没法突破这个双引号,那么这个数据岛就是安全的,脚本需要数据的话,就通过dom的方式从安全的服务器数据岛取得数据。

所以,说了这么多,就是表达一个意思,避免输出到javascript里面,当然其他script也不行。

没有评论: