为html和输入字段安全地转义输出

时间:2010-06-30 11:35:36

标签: php jquery html xss escaping

在我的网络应用中,用户可以输入文字数据。此数据可以显示给其他用户,原始作者也可以返回并编辑他们的数据。我正在寻找安全地逃避这些数据的正确方法。

我只是在进行sql清理,因此所有内容都会在读取时存储。假设我在数据库中有“似曾相识”。或者,更为极端的是<script>标签。这可能是有效的,甚至不是恶意的输入。

我在出路时使用htmlentities()来确保所有内容都已转义。问题是html和输入字段对待事物的方式不同。我想确保它在HTML中是安全的,但是作者在编辑文本时会看到他们在输入字段中输入的内容。我也在使用jQuery动态填充表单字段和数据。

如果我这样做:

 <p><?=htmlentities("déjà vu");?></p>
 <input type=text value="<?=htmlentities("déjà vu");?>">

页面源将d&eacute;j&agrave; vu放在两个地方(我不得不反击或者你会看到“似曾相识”!)问题是<p>中的输出是正确的,但是输入只显示转义的文字。如果用户重新提交表单,他们会双重逃脱并破坏他们的输入。

我知道我仍然需要清理进入该领域的文本,否则您可以结束价值报价并做坏事。我找到的唯一解决方案就是这个。我再次使用jQuery。

var temp = $("<div></div>").html("<?=htmlentities("déjà vu");?>");
$("input").val(temp.html());

这有效,因为它会导致div将转义的文本作为编码字符读取,然后jquery将这些编码的字符复制到输入标记中,并妥善保存。

所以我的问题是:这仍然是安全的,还是某处有安全漏洞?更重要的是,这是唯一/正确的方法吗?我是否遗漏了有关html和字符编码如何工作的问题,这使得这个问题难以解决?

修改

这实际上是错误的,我过度简化了我的例子,以至于它不起作用。问题实际上是因为我使用jQuery的val()将文本插入到字段中。

<input>
<script>$("input").val("<?=htmlentities("déjà vu");?>");</script>

原因是表单是动态的 - 用户可以随意添加或删除字段,因此它们是在页面加载后生成的。

所以似乎jQuery正在逃避数据进入输入,但它还不够好 - 如果我自己不做任何事情,用户仍然可以放入</script>标签,杀了我的代码和插入恶意代码。但是这里有另一个论点要做。由于只有原作者才能在输入框中看到文字,我是否应该打扰?基本上他们可以执行XSS攻击的唯一人就是他们自己。

3 个答案:

答案 0 :(得分:5)

对不起,我无法重现您描述的行为。我总是使用htmlspecialchars()(它与htmlentities()基本上完成相同的任务)并且它永远不会导致任何形式的双重编码。页面源在两个地方显示d&eacute;j&agrave; vu(当然!这就是重点!)但是呈现的页面显示了适当的值,这是发送回服务器的内容。

您是否可以发布展示此类行为的完整自包含代码段?

更新:一些测试代码:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>

<?php

$default_value = 'déjà vu <script> ¿foo?';

if( !isset($_GET['foo']) ){
    $_GET['foo'] = $default_value;
}

?>

<form action="" method="get">
    <p><?php echo htmlentities($_GET['foo']); ?></p>
    <input type="text" name="foo" value="<?php echo htmlentities($_GET['foo']); ?>">
    <input type="submit" value="Submit">
</form>

</body>
</html>

回答更新的问题

htmlentities()函数,顾名思义,在生成HTML输出时使用。这就是为什么它在你的第二个例子中没什么用处:JavaScript 不是 HTML。它是一种自己的语言,有自己的语法。

现在,您要解决的问题是如何生成遵循以下两个规则的输出:

  1. 这是JavaScript中的有效字符串。
  2. 可以安全地嵌入HTML文档中。
  3. 我知道的#1最接近的PHP函数是json_encode()。由于JSON语法是JavaScript的一个子集,如果您使用PHP字符串提供它,它将输出一个JavaScript字符串。

    关于#2,一旦浏览器进入JavaScript块,它就会期望</script>标签离开它。 json_encode()函数负责处理并正确转义它(<\/script>)。

    我修改过的测试代码:

    <?php
    
    $default_value = 'déjà vu </script> ¿foo?';
    
    if( !isset($_GET['foo']) ){
        $_GET['foo'] = $default_value;
    }
    
    ?>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head><title></title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript"><!--
    $(function(){
        $("input[type=text]").val(<?php echo json_encode(utf8_encode($_GET['foo'])); ?>);
    });
    //--></script>
    </head>
    <body>
    
    
    <form action="" method="get">
        <p><?php echo htmlentities($_GET['foo']); ?></p>
        <input type="text" name="foo" value="(to be replaced)">
        <input type="submit" value="Submit">
    </form>
    
    </body>
    </html>
    

    注意:utf8_encode()从ISO-8859-1转换为UTF-8,如果您的数据已经是UTF-8(推荐),则不需要它。

答案 1 :(得分:1)

如果您只需要反转编码,则可以使用html_entity_decode - http://www.php.net/manual/en/function.html-entity-decode.php

另一种可能性是在内容将作为网页的一部分显示时运行htmlentities。否则,请保留从数据存储区提交或加载的未编码文本。

答案 2 :(得分:0)

我认为将值应用于输入的方式存在问题。它被显示为编码,这是有道理的,因为它是Javascript,而不是HTML。所以,我建议将编码文本作为标记的一部分编写,以便自然地进行解析(而不是注入客户端脚本)。由于在服务器响应时您的文本框不可用,您可以使用临时隐藏字段...

<input type="hidden" id="hidEncoded" value="<?=htmlentities("déjà vu");?>" />

然后它将被解析为旧的HTML,当您尝试使用Javascript访问该值时,它应该被解码...

// Give your textbox an ID!
$("#txtInput").val($("#hidEncoded").val());