在我的网络应用中,用户可以输入文字数据。此数据可以显示给其他用户,原始作者也可以返回并编辑他们的数据。我正在寻找安全地逃避这些数据的正确方法。
我只是在进行sql清理,因此所有内容都会在读取时存储。假设我在数据库中有“似曾相识”。或者,更为极端的是<script>
标签。这可能是有效的,甚至不是恶意的输入。
我在出路时使用htmlentities()
来确保所有内容都已转义。问题是html和输入字段对待事物的方式不同。我想确保它在HTML中是安全的,但是作者在编辑文本时会看到他们在输入字段中输入的内容。我也在使用jQuery动态填充表单字段和数据。
如果我这样做:
<p><?=htmlentities("déjà vu");?></p>
<input type=text value="<?=htmlentities("déjà vu");?>">
页面源将déjà 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攻击的唯一人就是他们自己。
答案 0 :(得分:5)
对不起,我无法重现您描述的行为。我总是使用htmlspecialchars()
(它与htmlentities()
基本上完成相同的任务)并且它永远不会导致任何形式的双重编码。页面源在两个地方显示déjà 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最接近的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());