16.4.2 转义输出

与过滤输入具有同样重要性的是输出转义。在系统保存用户输入值后,确保这些值不会破坏或者导致任何无意结果是非常重要的。通过几个关键函数,可以确保这些值不会被客户端Web浏览器错误的执行,而只是显示文本。

有些应用可能会需要接收用户指定的输入值并且在页面上显示这些输入值,例如,用户可以对一个发布的文章或消息公布板系统添加评论的页面就需要显示用户输入的信息。在这种情况下,需要非常注意用户输入的文本,确保在这些文本没有插入恶意的HTML标记。

最简单的方法是使用htmlspecialchars或者htmlentities函数。这些函数将检测到用户输入的特定字符并且将其转换成HTML实体。简单的说,HTML实体就是一个特殊字符序列,以“&”字符为开始,它可以用来表示那些不能在HTML代码表示的特殊字符。“&”字符后就是该实体名称,以“;”为结束。或者,一个实体可以是一个由“#”指定的ASCII键码以及一个数字,例如,/表示一个前导反斜线字符(“/”)。

例如,由于HTML的所有置标元素都是通过“<”和“>”来划分,在最后输出的内容中输入这些字符会比较困难(因为在默认情况下,浏览器认为这两个字符用来区分置标元素)。要解决这个问题,可以使用"<"和">"。同样的,如果希望在HTML中包含“&”字符,可以使用"&"。单引号和双引号分别用“'”和"""表示。HTML实体在客户端HTML解释器(Web浏览器)被转换并插入到输出,因此不会被认为是置标元素的一部分。

htmlspecialchars函数和htmlentities函数的区别在于:前一个函数在默认情况下只替换“&”、“<”和“>”,此外还有一个可选的开关设置用来确定是否替换单引号和双引号。相反,后者将替换所有由有名称实体所表示的字符串。有名称实体包括©版权符号,用"©"表示;而欧元符号用"€"表示。但是,后者不会将字符转换成数字实体。

这两个函数的第二个参数用来控制是否将单引号或双引号转换成HTML实体,而第三个参数用来指定输入字符串编码使用的字符集(编码字符集的指定是非常重要的,因为我们希望这些函数能够对UTF-8字符串是安全的)。第二个参数的可能值如下所示:

■ENT_COMPAT:双引号被转换为""",但单引号不会被转换。

■ENT_QUOTES:单引号和双引号都将被转换,被分别转换成“'”和"""

■ENT_NOQUOTES(默认值):不转换单引号和双引号。

分析如下所示的代码:


$input_str="<p align=\"center\">The user gave us\"15000?\".</p>

<script type=\"text/javascript\">

//malicious JavaScript code goes here.

</script>";


如果通过如下所示的PHP脚本来处理(将对以上文本执行nl2br函数,确保输出字符串在浏览器中具有良好的格式):


<?php

$str=htmlspecialchars($input_str,ENT_NOQUOTES,"UTF-8");

echo nl2br($str);

$str=htmlentities($input_str,ENT_QUOTES,"UTF-8");

echo nl2br($str);

?>


将得到如下所示的输出:


<p align="center">The user gave us"15000?".</p><br/>

<br/>

<script type="text/javascript"><br/>

//malicious JavaScript code goes here.<br/>

</script><p align="center">The user gave us

"15000€".</p><br/>

<br/>

<script type="text/javascript"><br/>

//malicious JavaScript code goes here.<br/>

</script>


在浏览器中,以上代码就将变成:


<p align="center">The user gave us"15000?".</p>

<script type="text/javascript">

//malicious JavaScript code goes here.

</script><p align="center">The user gave us"15000?".</p>

<script type="text/javascript">

//malicious JavaScript code goes here.

</script>


请注意,htmlentities函数将欧元符号转换成一个实体("€"),而htmlspecial-chars函数则不会对其进行处理。

对于允许用户输入HTML的情况,例如,在一个消息公布板系统中,用户会希望使用某些字符控制字体、颜色以及样式(粗体或斜体),就必须定义某些字符串并且不要对其进行转义。