Безопасное экранирование вывода для полей html и input - PullRequest
5 голосов
/ 30 июня 2010

В моем веб-приложении пользователи могут вводить текстовые данные.Эти данные могут быть показаны другим пользователям, а первоначальный автор также может вернуться и отредактировать свои данные.Я ищу правильный способ безопасного экранирования этих данных.

Я только санирую 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 в обоих местах (мне пришлось сделать обратную галочкуэто или вы бы увидели «déjà vu»!) Проблема в том, что вывод в <p> правильный, но ввод просто показывает экранированный текст.Если пользователь повторно отправляет свою форму, он дважды убегает и разрушает свой ввод.

Я знаю, что мне все еще нужно очистить текст, который входит в поле, в противном случае вы можете закончить кавычку и делать плохие вещи.Единственное решение, которое я нашел, это.Опять же, я использую jQuery.

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

Это работает, так как заставляет div читать экранированный текст как закодированные символы, а затем jquery копирует эти закодированные символы во входной тег, должным образом сохраненный.

Итак, мой вопрос: это все еще безопасно или где-то есть дыра в безопасности?И что более важно, это единственный / правильный способ сделать это?Я что-то упускаю из-за того, как работает html и кодировка символов, что делает эту тривиальную проблему решаемой?

EDIT

Это на самом деле неправильно, я упростил мой пример до такой степени, что он не работает.Проблема на самом деле в том, что я использую valQ (jQuery) для вставки текста в поле.

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

Причина этого в том, что форма динамическая - пользователь может добавлять или удалять поля по своему усмотрениюи поэтому они генерируются после загрузки страницы.

Таким образом, кажется, что jQuery сбрасывает данные для ввода во ввод, но это не совсем хорошо - если я сам ничего не делаю, пользователь все еще можетвставьте тег </script>, убив мой код и вставив вредоносный код.Но здесь есть еще один аргумент.Так как в любом случае только оригинальный автор может видеть текст в поле ввода, стоит ли мне беспокоиться?По сути, единственные люди, на которых они могут выполнить атаку XSS, - это они сами.

Ответы [ 3 ]

5 голосов
/ 30 июня 2010

Извините, но я не могу воспроизвести поведение, которое вы описываете.Я всегда использовал 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.

Ближайшая известная мне PHP-функция для # 1 - 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 голос
/ 30 июня 2010

Если вам просто нужно изменить кодировку, то вы можете использовать html_entity_decode - http://www.php.net/manual/en/function.html-entity-decode.php.

Другая возможность - запускать htmlentities только тогда, когда контент будет отображаться как часть веб-страницы.В противном случае сохраните незашифрованный текст, отправленный или загруженный из хранилища данных.

0 голосов
/ 30 июня 2010

Я считаю, что это проблема с тем, как вы применяете значение к входу.Он отображается как закодированный, что имеет смысл, потому что это Javascript, а не HTML.Итак, я бы предложил написать закодированный текст как часть разметки, чтобы он анализировался естественным образом (в отличие от внедрения в клиентский скрипт).Поскольку ваши текстовые поля недоступны, когда сервер отвечает, вы можете использовать временное скрытое поле ...

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

Тогда оно будет проанализировано как старый добрый HTML, и при попытке доступа к значениюс помощью Javascript он должен быть декодирован ...

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