PHP удаляет все атрибуты события HTML в теге HTML
Я хотел бы удалить все атрибуты событий (например, из всех событий на основе списка ссылок на события).
Есть ли в классе PHP DOMDocument функция, которая распознает атрибуты событий?
Я пытался использовать RegEx, но он усложнился с одинарными и двойными кавычками:
preg_replace('/on*[a-z]+=".*?"/i', '', $html); // Doesn't match onclick="alert(\"hello\");"
Я попробовал внешнюю библиотеку под названием HTMLPurifier, но у нее нет возможности удалить все атрибуты события.
Любая идея, какие направления или простое решение?
4 ответа
Если вам нужен действительно безопасный код, подход с использованием белого списка ("разрешать только эти вещи: ..."), как правило, более надежный, чем подход с использованием черного списка ("не разрешать эти вещи: ...").
Вы упомянули HTML Purifier и что "у него нет возможности удалить все атрибуты события".
Это... технически правильно, в том смысле, что вы не можете сказать ему удалить атрибуты события. Причина в том, что смысл продажи: он делает это автоматически. Опция, которая "отсутствует" - это возможность настроить HTML Purifier для разрешения атрибутов событий. Это намеренно не так. HTML Purifier (как следует из названия) имеет сильный фокус безопасности.
Есть некоторые аспекты "небезопасного HTML", которые вы можете разрешить с помощью конфигурации очистителя HTML (конфигурация по умолчанию является преднамеренно разборчивой), но атрибуты события не входят в их число. (Ну, вы можете научить HTML Purifier принимать их, если прыгаете через обручи, но это потребует много усилий.)
Я бы рекомендовал попробовать еще раз, если вы хотите принять пользовательский HTML. Это довольно устоявшийся инструмент, который был протестирован многими людьми.
Есть несколько очень хитрых способов взломать HTML и внедрить JavaScript. Например, знаете ли вы, что вы можете ввести JavaScript, используя src
или же href
атрибут? Знаете ли вы, что в некоторых браузерах можно вводить JavaScript с помощью style
тег? Взгляните на эту XSS шпаргалку. Это может дать вам приблизительное представление о том, с чем вы столкнулись, и почему белый список обычно считается более эффективным.
В любом случае, удачи!
function filterText($value)
{
if(!$value) return $value;
return escapeJsEvent(removeScriptTag($value));
}
function escapeJsEvent($value){
return preg_replace('/(<.+?)(?<=\s)on[a-z]+\s*=\s*(?:([\'"])(?!\2).+?\2|(?:\S+?\(.*?\)(?=[\s>])))(.*?>)/i', "$1 $3", $value);
}
function removeScriptTag($text)
{
$search = array("'<script[^>]*?>.*?</script>'si",
"'<iframe[^>]*?>.*?</iframe>'si");
$replace = array('','');
$text = preg_replace($search, $replace, $text);
return preg_replace_callback("'&#(\d+);'", function ($m) {
return chr($m[1]);
}, $text);
}
echo filterText('<img src=1 href=1 onerror="javascript:alert(1)"></img>');
Загрузите HTML-документ, выполните итерации по всем элементам, а затем по всем их атрибутам (вложенным), удалите атрибуты, если они начинаются с on
:
$doc = new DOMDocument();
$doc->loadHTML($html);
foreach ($doc->getElementsByTagname('*') as $element)
{
foreach (iterator_to_array($element->attributes) as $name => $attribute)
{
if (substr_compare($name, 'on', 0, 2, TRUE) === 0)
{
$element->removeAttribute($name);
}
}
}
Вы также можете захотеть очистить список известных имен атрибутов и выдать предупреждение, если найден неизвестный (или у вас есть белый список атрибутов, которые вы разрешаете). Надеюсь, это поможет, код быстро набирается, и в нем могут быть небольшие ошибки.
Способ сделать это с DOM.
Следующий код ищет и удаляет атрибуты, имя которых начинается с "on" во всех HTML-тегах.
( $html
расшифровывается как HTML-код)
$doc = new DOMDocument();
@$doc->loadHTML($html);
$xpath = new DOMXPath($doc);
$onAttributes = $xpath->query("//*/@*[starts-with(name(), 'on')]");
foreach ($onAttributes as $onAttribute) {
$onAttribute->ownerElement->removeAttributeNode($onAttribute);
}
$body = $xpath->query('body')->item(0);
$result = substr($doc->saveHTML($body),6,-7);