Портрет 4X_Pro
Был в Сети 8 сент. 2025 г., 04:42
Мультиблог
4X_Pro
Кратко о себе: Web-разработчик. Пишу на PHP, Python, JavaScript. Знаю Ruby и Go, со студенческих времён более-менее помню C и asm. Специализируюсь на ускорении загрузки сайтов и разработке ботов для Telegram. Linuxоид (использую Debian+LXDE). Сторонник IndieWeb, slow lifer.

Социальные сети


Новости сайта в Telegram

t.me/4x_pro

Компьютерное

Выборочная очистка HTML

4X_Pro

Как известно, любой текст, который вводится пользователем и затем отображается на сайте, нужно обезопасить от 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 — для типичных блочных тегов. При необходимости их можно объединять через операцию +.

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

4X_Pro

Недавно решил написать клиент для протокола IndieAuth. Для этого потребовалось сделать возможность находить на произвольной Web-странице все ссылки с атрибутом rel="me", причём ссылки могут быть как обычные (тег a), так и скрытые (тег link).

Первое, что приходит в голову, — регулярные выражения. Но для разбора произвольного HTML, который может быть свёрстан не по стандартам и иметь произвольную кодировку, это плохое и ненадёжное решение.

Пришлось вспомнить про такое расширение как PHP DOM. Оно позволяет работать с XML и HTML с помощью API, аналогичной той, которая есть для работы с DOM в броузерном JavaScript, а также делать запросы с помощью XPath. Вот им-то я и решил воспользоваться.
Итак, для начала создаём объект DOMDocument и загружаем в него HTML. Вместо обычного load, предназначенного для работы с правильно отформатированным XML, используем loadHTMLFile:
"Смотреть пример кода"

Как отцентрировать текст по вертикали

4X_Pro

У начинающих верстальщиков часто возникает вопрос, как отцентрировать текст в блоке по горизонтали и вертикали. С горизонталью всё достаточно просто: используем свойство 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

4X_Pro

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

Читать далее…

Checkbox большого размера

4X_Pro

Недавно потребовалось сверстать страницу, на которой у элемента 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 }

Задать вопрос

Здесь можно задать мне вопрос или спросить совета по любой теме, затронутой в блогах или на форуме. После того, как я отвечу, вопрос и ответ появятся в соответствующем разделе. Но не забываем, что я — сторонник slow life, поэтому каких-либо сроков ответов не обещаю. Самые интересные вопросы станут основой для новых тем на форуме или записей в блоге.
Сразу предупреждаю: глупости, провокации, троллинг и тому подобное летит прямо в /dev/null.