PHP - Достаточно ли htmlentities() для создания безопасных для XML значений?
Я строю XML-файл с нуля, и мне нужно знать, преобразует ли htmlentities() каждый символ, который потенциально может сломать XML-файл (и, возможно, данные UTF-8)? Значения будут взяты из ленты Twitter/ Flickr, поэтому я должен быть уверен!
5 ответов
htmlentities()
не является гарантированным способом создания легального XML.
использование htmlspecialchars()
вместо htmlentities()
если это все, что вас беспокоит. Если у вас есть несоответствие кодировки между представлением ваших данных и кодировкой вашего XML-документа, htmlentities()
может обойти / прикрыть их (это увеличит размер вашего XML). Я считаю, что лучше, чтобы ваши кодировки были последовательными и просто использовались htmlspecialchars()
,
Кроме того, имейте в виду, что если вы качаете возвращаемое значение htmlspecialchars()
внутри атрибутов XML, разделенных одинарными кавычками, вам нужно будет передать ENT_QUOTES
помечайте также, чтобы любые одинарные кавычки в исходной строке также были правильно закодированы. Я предлагаю сделать это в любом случае, так как это делает ваш код невосприимчивым к ошибкам, вызванным тем, что кто-то в будущем будет использовать одинарные кавычки для атрибутов XML.
Изменить: чтобы уточнить:
htmlentities()
преобразует количество не-ANSI символов (я полагаю, это то, что вы подразумеваете под данными UTF-8) в сущности (которые представлены только символами ANSI). Однако он не может сделать это для любых символов, которые не имеют соответствующей сущности, и поэтому не может гарантировать, что его возвращаемое значение состоит только из символов ANSI. Вот почему я предлагаю не использовать его.
Если кодирование является возможной проблемой, обработайте ее явно (например, с помощью iconv()
).
Изменить 2: Улучшенный ответ с учетом комментария Джоша Дэвиса ниже.
Dom::createTextNode()
автоматически покинет ваш контент.
Пример:
$dom = new DOMDocument;
$element = $dom->createElement('Element');
$element->appendChild(
$dom->createTextNode('I am text with Ünicödé & HTML €ntities ©'));
$dom->appendChild($element);
echo $dom->saveXml();
Выход:
<?xml version="1.0"?>
<Element>I am text with Ünicödé & HTML €ntities ©</Element>
Когда вы устанавливаете внутреннюю кодировку в utf-8, например
$dom->encoding = 'utf-8';
вы все равно получите
<?xml version="1.0" encoding="utf-8"?>
<Element>I am text with Ünicödé & HTML €ntities ©</Element>
Обратите внимание, что приведенное выше не совпадает с установкой второго аргумента. $value
в Dom::createElement()
, Метод будет только проверять правильность имен ваших элементов. Смотрите примечания на странице руководства, например,
$dom = new DOMDocument;
$element = $dom->createElement('Element', 'I am text with Ünicödé & HTML €ntities ©');
$dom->appendChild($element);
$dom->encoding = 'utf-8';
echo $dom->saveXml();
приведет к предупреждению
Warning: DOMDocument::createElement(): unterminated entity reference HTML €ntities ©
и следующий вывод:
<?xml version="1.0" encoding="utf-8"?>
<Element>I am text with Ünicödé </Element>
Ответ Гордона хорош и объясняет проблемы кодирования XML, но не показывает простой функции (или того, что делает черный ящик). Ответ Джона хорошо начинается с рекомендации функции "htmlspecialchars", но он и другие делают некоторую ошибку, тогда я буду решительным.
Хороший программист ДОЛЖЕН иметь контроль над использованием или отсутствием UTF-8 в ваших строках и данных XML: UTF-8 (или другое кодирование не ASCII) БЕЗОПАСНО в согласованном алгоритме.
БЕЗОПАСНЫЙ UTF-8 XML НЕ НУЖДАЕТСЯ В ПОЛНОЦЕННОМ ЭНКОДЕ. Кодирование без разбора создает "второй класс, не читаемый человеком, кодирование / декодирование по требованию, XML". И безопасный ASCII XML, также не нужно кодировать сущность, когда весь ваш контент является ASCII.
В строке XML-содержимого необходимо экранировать только 3 или 4 символа: >
, <
, &
и необязательно "
, Пожалуйста, прочитайте http://www.w3.org/TR/REC-xml/ "2.4 Символьные данные и разметка" и "4.6 Предопределенные объекты". ТОГДА ВЫ МОЖЕТЕ использовать " htmlentities"
Для иллюстрации, следующая функция PHP сделает XML полностью безопасным:
// it is a didactic illustration, USE htmlentities($S,flag)
function xmlsafe($s,$intoQuotes=0) {
if ($intoQuotes)
return str_replace(array('&','>','<','"'), array('&','>','<','"'), $s);
// SAME AS htmlspecialchars($s)
else
return str_replace(array('&','>','<'), array('&','>','<'), $s);
// SAME AS htmlspecialchars($s,ENT_NOQUOTES)
}
// example of SAFE XML CONSTRUCTION
function xmlTag( $element, $attribs, $contents = NULL) {
$out = '<' . $element;
foreach( $attribs as $name => $val )
$out .= ' '.$name.'="'. xmlsafe( $val,1 ) .'"';
if ( $contents==='' || is_null($contents) )
$out .= '/>';
else
$out .= '>'.xmlsafe( $contents )."</$element>";
return $out;
}
В блоке CDATA вам не нужно использовать эту функцию... Но, пожалуйста, избегайте неизбирательного использования CDATA.
Таким образом, ваш вопрос "гарантированно ли результат htmlentities() будет XML-совместимым и UTF-8-совместимым?" Ответ - нет, это не так.
htmlspecialchars () должно быть достаточно для экранирования специальных символов XML, но вам придется дезинфицировать строки UTF-8 в любом случае. Даже если вы создадите свой XML, скажем, с помощью SimpleXML, вам придется санировать строки. Я не знаю о других библиотеках, таких как XMLWriter или DOM, я думаю, что это то же самое.
Думаю, я бы добавил это для тех, кому нужно продезинфицировать, а не потерять атрибуты XML.
// Returns SimpleXML Safe XML keeping the elements attributes as well
function sanitizeXML($xml_content, $xml_followdepth=true){
if (preg_match_all('%<((\w+)\s?.*?)>(.+?)</\2>%si', $xml_content, $xmlElements, PREG_SET_ORDER)) {
$xmlSafeContent = '';
foreach($xmlElements as $xmlElem){
$xmlSafeContent .= '<'.$xmlElem['1'].'>';
if (preg_match('%<((\w+)\s?.*?)>(.+?)</\2>%si', $xmlElem['3'])) {
$xmlSafeContent .= sanitizeXML($xmlElem['3'], false);
}else{
$xmlSafeContent .= htmlspecialchars($xmlElem['3'],ENT_NOQUOTES);
}
$xmlSafeContent .= '</'.$xmlElem['2'].'>';
}
if(!$xml_followdepth)
return $xmlSafeContent;
else
return "<?xml version='1.0' encoding='UTF-8'?>".$xmlSafeContent;
} else {
return htmlspecialchars($xml_content,ENT_NOQUOTES);
}
}
Использование:
$body = <<<EG
<?xml version='1.0' encoding='UTF-8'?>
<searchResult count="1">
<item>
<title>2016 & Au Rendez-Vous Des Enfoir&</title>
</item>
</searchResult>
EG;
$newXml = sanitizeXML($body);
var_dump($newXml);
Возвращает:
<?xml version='1.0' encoding='UTF-8'?>
<searchResult count="1">
<item>
<title>2016 & Au Rendez-Vous Des Enfoir&</title>
</item>
</searchResult>