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

Tes*_*rex 5 html php xss jquery escaping

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

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

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

如果我这样做:

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

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

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

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

这有效,因为它导致div将转义的文本作为编码字符读取,然后jquery将这些编码的字符复制到输入标记,并进行适当保留.

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

编辑

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

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

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

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

Álv*_*lez 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>
Run Code Online (Sandbox Code Playgroud)

回答更新的问题

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

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

  1. 它是JavaScript中的有效字符串.
  2. 它可以安全地嵌入HTML文档中.

我知道的#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>
Run Code Online (Sandbox Code Playgroud)

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