Как известно, любой текст, который вводится пользователем и затем отображается на сайте, нужно обезопасить от XSS-атак — вставок JavaScript, которые могут украсть идентификатор сессии или совершить какие-либо нежелательные действия от имени пользователя. Если текст не предполагает сложного форматирования, то сделать это достаточно легко: пропустить его через функцию htmlspecialchars, которая экранирует все небезопасные символы и превратит HTML в обычный текст. Но как быть, если пользователю нужно разрешить использовать форматирование текста, например, вставку картинок, ссылок, текста с курсивом и жирным начертанием, видео?
Первое, что приходит в голову — это воспользоваться функцией strip_tags со списком разрешённых тегов. Увы, эта функция имеет существенный недостаток: если тег разрешён, то она позволяет использовать в нём любые атрибуты, в том числе и атрибуты обаботчиков событий (onclick, onmouseover и так далее), на которые легко можно повесить вредоносный код.
Другой вариант — это использование специальных языков разметки, например, BBCode или Markdown, которые затем преобразовывать в HTML. Главный недостаток такого подхода — в том, что это существенно сужает выбор WYSIWYG-редакторов, так как далеко не в каждом из них есть поддержка этих языков.
Поэтому приходится прибегать к другому решению — использованию расширения DOM и удалению все тех тегов и атрибутов, которых нет в белом списке. Для начала решим, как будем задавать этот белый список. На мой взгляд, самый эффективный вариант — это хеш-массив, где ключи — это теги, а значения — массивы разрешённых для тега атрибутов (в примерах кода дальше будем считать, что он лежит в $tags, а HTML-код для очистки — в HTML)
Для начала просто удалим все те теги, которых нет в списке разрешённых, с помощью функции strip_tags:
$html = strip_tags($html,'<'.join('><',array_keys($tags)).'>');
Теперь загрузим HTML-код в объект DOMDocument и создадим объект XPath для поиска атрибутов тегов и выполним этот поиск:
$html = mb_encode_numericentity($html, [0x80, 0x10FFFF, 0, ~0], 'UTF-8'); // без этого не будет корректно работать с UTF-8
if (!class_exists('DOMDocument')) throw new Exception('DOM extension not loaded!');
$dom = new DOMDocument();
$dom->formatOutput = false;
$dom->loadHTML($html,LIBXML_NONET|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query('//@*');
В переменной $nodes лежит список таких узлов-атрибутов. Пройдёмся по ним и проверим, есть ли тег (на него указывает $node->parentNode->nodeName) в списке разрешённых тегов и есть ли сам атрибут ( $node->nodeName) в списке разрешённых атрибутов для этго тега. Если его там не будет, обратимся к родительскому элементу через $node->parentNode и вызовем метод removeAttribute для его удаления.
foreach ($nodes as $node) {
$tagname = $node->parentNode->nodeName;
if (!empty($tags[$tagname])) {
$attrs = $tags[$tagname];
$attrname = $node->nodeName;
if (!in_array($attr_name,$attrs)) $node->parentNode->removeAttribute($attrname);
}
}
Теперь осталась ещё одна задача: проверить атрибуты href и src на наличие адресов вида javascript:alert('Небезопасно'). Для этого найдём с помощью XPath все атрибуты href и src, и проверим, какой протокол для ссылок используется. Если там есть слово script (так как кроме javascript, можно использовать ещё и vbscript), будем считать такую ссылку небезопасной и заменим её на безопасное значение "#":
$links = $xpath->query('//@href|//@src');
foreach ($links as $link) {
$scheme = parse_url($link->textContent,PHP_URL_SCHEME);
if (strpos(strtolower($scheme),'script')!==false) {
$link->nodeValue='#'; // removing dangerous link address
}
}
Теперь осталось только сохранить обработанный HTML-код из DOM-дерева обратно в строку:
$html = $dom->saveHTML();
Посмотреть полный код, оформленный в виде класса HTMLCleaner со статическим методом clean, можно в приложенном файле. В этом же классе определён набор констант с наиболее часто требующимися тегами и их атрибутами: TAGS_MINIMUM — только a и img, TAGS_MEDIA — для мультимедиа-тегов audio и video, TAGS_INLINE — для самых частых строчных тегов оформления, TAGS_FORMAT — для типичных блочных тегов. При необходимости их можно объединять через операцию +.

- Перово, Москва, Россия
- me@4xpro.ru
- XXXXPro
Новости сайта в Telegram
t.me/4x_proКомпьютерное
Выборочная очистка HTML

Как получить из HTML-документа все ссылки с определённым атрибутом на PHP

Недавно решил написать клиент для протокола IndieAuth. Для этого потребовалось сделать возможность находить на произвольной Web-странице все ссылки с атрибутом rel="me", причём ссылки могут быть как обычные (тег a), так и скрытые (тег link).
Первое, что приходит в голову, — регулярные выражения. Но для разбора произвольного HTML, который может быть свёрстан не по стандартам и иметь произвольную кодировку, это плохое и ненадёжное решение.
Пришлось вспомнить про такое расширение как PHP DOM. Оно позволяет работать с XML и HTML с помощью API, аналогичной той, которая есть для работы с DOM в броузерном JavaScript, а также делать запросы с помощью XPath. Вот им-то я и решил воспользоваться.
Итак, для начала создаём объект DOMDocument и загружаем в него HTML. Вместо обычного load, предназначенного для работы с правильно отформатированным XML, используем loadHTMLFile:
"Смотреть пример кода"
Как отцентрировать текст по вертикали

У начинающих верстальщиков часто возникает вопрос, как отцентрировать текст в блоке по горизонтали и вертикали. С горизонталью всё достаточно просто: используем свойство text-align. А вот для вертикали раньше приходилось прибегать ко всяческим ухищрениям. Но с появлением flex-верстки всё стало намного проще. Зададим в CSS класс, для которого пропишем выравнивание по центру по главной и вспомогательной оси:
.centered { display: flex; justify-content: center; align-items: center; text-align: center }
И просто применим его к нужному блоку:
<div class="centered">Немного текста <br /> и дополнительного <br />содержимого</div>
Посмотреть пример
Yaml Form Generator -- простое и быстро создание форм для PHP

В прошлом году, работая над одним проектом, задумался, сколько же времени уходит на то, чтобы сделать качественные формы: нужно сверстать форму на HTML, сделать валидацию формы на стороне клиента, а потом и на стороне сервера, а для некоторых типов данных выполнить еще ряд действий: преобразовать дату в Unix Timestamp и обратно, обработать ситуацию "снятый checkbox", проверить, что злоумышленник не добавил каких-то своих полей. Поскольку все эти действия весьма рутинны, и я задумался над тем, можно ли их как-то автоматизировать, причем так, чтобы форма целиком и полностью описывалась в одном месте.
Читать далее…Checkbox большого размера

Недавно потребовалось сверстать страницу, на которой у элемента checkbox был бы увеличен размер. Варианты с рисованным checkbox, который переключался бы с помощью JavaScript для меня изначально были неприемлемы Сначала пытался воздействовать на него с помощью font-size, line-height, padding и border, но ничего из перечисленного не помогло. Потом обнаружил, что свойства height и width, выставленные одновременно, позволяют добиться желаемого. Но увы, оказалось, что только в Google Chrome. В результате пришлось воспользоваться воспользоваться свойством transform из CSS 3: <input type="checkbox" style="transform: scale(2)" />, но при этом сам checkbox становится размытым.
Поэтому тем, кому валидность CSS не критична, посоветовал бы делать несколько иначе:
.bigbox { -ms-transform: scale(2); -moz-transform: scale(2); -o-transform: scale(2); height: 28px; width: 28px }
Такой подход позволит отрисовывать checkbox в Google Chrome нужного размера, а в остальных броузерах — масштабировать с размытием.
Обновлено в мае 2018: теперь свойства height и width работают во всех броузерах, включая Firefox. Можно ограничиться ими:
.bigbox { height: 28px; width: 28px }