По своему виду метка ничем не отличается от обычного текста, но благодаря ей пользователь может выбрать элемент формы кликом по тексту, расположенному внутри элемента , а не по самому элементу .
Поддержка браузерамиДа | Да | Да | Да | Да | Да |
for | element_id | Определяеть к какому элементу формы относится текущая метка. |
form | form_id | Определяет форму/формы с которой будет связана метка. В качестве значения атрибута выступает идентификатор элемента ( ). Этот атрибут позволяет размещать метки в произвольном месте конкретного документа, а не только внутри тега . Этот атрибут был удален из спецификации HTML 28 апреля 2016 года , но скрипты имеют доступ только для чтения HTMLLabelElement.form (возвращает форму с которой связана метка, либо "0", если метка не связана с формой). |
Чтобы определить к какому элементу формы относится текущая метка, необходимо использовать атрибут for тега . Значение атрибута for должно соответствовать значению глобального атрибута того элемента формы, к которому будет относится метка. Атрибут for можно не использовать, если элемент будет находиться внутри элемента .
Рассмотрим пример использования:
Пример использования тега Да НетДа Нет
В этом примере мы:
- Внутри первой
формы:
- Разместили две радиокнопки ( ) для выбора одного из ограниченного числа вариантов. Еще раз обратите внимание, что для радиокнопок внутри одной формы необходимо указывать одинаковое имя , значения мы указали разные. Для первой checked , который указывает, что элемент должен быть предварительно выбран при загрузке страницы (в данном случае радиокнопка со значением yes ). Кроме того, мы указали для радиокнопок глобальные атрибуты , которые определяют уникальный идентификатор для элемента.
- Разместили два элемента , которые определяют текстовые метки для наших текстовых полей. Обратите внимание, что мы использовали атрибут for , чтобы определить к какому элементу формы относится текущая метка. Значение атрибута for соответствует значению глобального атрибута необходимой нам радиокнопки.
- Внутри второй
формы:
- Разместили две радиокнопки ( ) для выбора одного из ограниченного числа вариантов. Для второй радиокнопки мы указали атрибут
Чтобы описать суть проблемы, мне нужно рассказать, как вообще устроен HTML. Вы наверняка в общих чертах представляли себе, но я все равно коротко пробегусь по основным моментам, которые понадобятся для понимания. Если кому-то не терпится, сразу переходите к сути .
HTML - это язык гипертекстовой разметки. Чтобы говорить на этом языке, нужно соблюдать его формат, иначе тот, кто читает написанное, не сможет вас понять. Например, в HTML у тегов есть атрибуты:
Тут - это имя атрибута, а - это его значение. В статье я буду использовать квадратные скобки вокруг кода, чтобы было понятно, где он начинается и заканчивается. После имени стои́т знак равенства, а после него - значение, заключенное в кавычки. Значение атрибута начинается сразу после первого символа кавычки и заканчивается сразу перед следующим символом кавычки, где бы он не находился. Это значит, что если вместо вы запишете , то значение атрибута name будет , а еще у вашего элемента будет три других атрибута с именами: [рога] , [и] и [копыта"."] , но без значений.
Если это не то, чего вы ожидали, вам нужно как-то изменить значение атрибута, чтобы в нем не встречалась кавычка. Самое простое, что можно придумать - просто вырезать кавычки.
Тогда парсер HTML верно прочтет значение, но беда в том, что это будет другое значение. Вы хотели , а получили . В каких-то случаях такое различие может быть критичным.
Чтобы вы могли указать в качестве значения любую строку, формат языка HTML предлагает возможность экранировать значения атрибутов. Вместо кавычки в строке значения вы можете записать последовательность символов ["] и парсер поймет, что в этом месте в исходной строке, которую вы хотите использовать в качестве значения атрибута, была кавычка. Такие последовательности называются HTML entities.
При этом, если в вашей исходной строке действительно была последовательность символов ["] , у вас все еще есть возможность записать её так, чтобы парсер не превратил её в кавычку - для этого надо заменить знак [&] на последовательность символов [&] , то есть вместо ["] вам нужно будет записать в сыром тексте ["] .
Получается, что преобразование из исходной строки в ту, которую мы запишем между двумя символами кавычек, является однозначным и обратимым . Благодаря этим преобразованиям можно записать и прочитать любую строку в качестве атрибута HTML-тега, не вдаваясь в суть её содержимого. Вы просто соблюдаете формат, и все работает.
Собственно, так работает большинство форматов, с которыми мы сталкиваемся: есть синтаксис, есть способ экранирования контента от этого синтаксиса и способ экранирования символов экранирования, если вдруг такая последовательность встречается в исходной строке. Большинство, но не…
ТегТег служит для встраивания в HTML фрагментов, написанных на других языках. На сегодняшний день в 99% случаев это Javascript. Скрипт начинается сразу после открывающего тега и заканчивается сразу перед закрывающим тегом . Парсер HTML внутрь тега не заглядывает, для него это просто какой-то текст, который он потом отдает в парсер Javascript.
В свою очередь, Javascript - это самостоятельный язык с собственным синтаксисом, он, вообще говоря, никаким специальным образом не рассчитан на то, что будет встроен в HTML. В нем, как в любом другом языке, есть строковые литералы, в которых может быть что угодно. И, как вы уже должны были догадаться, может встретиться последовательность символов, означающая закрывающий тег .
var s = "surprise!alert("whoops!")";
Что тут должно происходить: переменной s должна присваиваться безобидная строка.
Что тут происходит на самом деле: Скрипт, в котором объявляется переменная s на самом деле заканчивается так: , что приводит к ошибке синтаксиса. Весь текст после него интерпретируется как чистый HTML и в него может быть внедрена любая разметка. В данном случае открывается новый тег и выполняется зловредный код.
Мы получили тот же эффект, как когда в значении атрибута присутствует кавычка. Но в отличие от значений атрибута, для тега нет никакого способа экранировать исходный контент. HTML entities внутри тега не работают, они будут переданы в парсер Javascript без изменений, то есть либо приведут к ошибке, либо изменят его смысл. Стандарт HTML прямо говорит, что в содержимом тега не может быть последовательности символов ни в каком виде. А стандарт Javascript не запрещает такой последовательности быть где угодно в строковых литералах.
Получается парадоксальная ситуация: после встраивания валидного Javascript в валидный документ HTML абсолютно валидными средствами мы можем получить невалидный результат .
На мой взгляд это и является уязвимостью разметки HTML, приводящей к уязвимостям в реальных приложениях.
Как эксплуатируется уязвимостьКонечно, когда вы просто пишете какой-то код, трудно представить, что вы напишете в строке и не заметите проблем. Как минимум, подсветка синтаксиса даст вам знать, что тег закрылся раньше времени, как максимум, написанный вами код не запустится и вы будете долго искать, что произошло. Но это не является основной проблемой с этой уязвимостью. Проблема возникает там, где вы вставляете какой-то контент в Javascript, когда генерируете HTML. Вот частый кусок кода приложений на реакте с серверным рендерингом:
window.__INITIAL_STATE__ = ;
В initialState может появиться в любом месте, где данные поступают от пользователя или из других систем. JSON.stringify не будет менять такие строки при сериализации, потому что они полностью соответствуют формату JSON и Javascript, поэтому они просто попадут на страницу и позволят злоумышленнику выполнить произвольный Javascript в браузере пользователя.
Другой пример:
analytics.identify("", ...);
Тут в строки с соответствующим экранированием записываются id пользователя и referer , который пришел на сервер. И, если в user.id вряд ли будет что-то кроме цифр, то в referer злоумышленник может запихнуть что угодно.
Но на закрывающем теге приколы не заканчиваются. Опасность представляет и открывающий тег , если перед ним в любом месте есть символы [
console.log("а вот и скрипт");
Атрибуты async и defer - способы выполнения скрипта
С помощью атрибутов async и defer можно задать способ выполнения скрипта.
Возможны 3 варианта использования async и defer атрибутов тега :
- Не используется ни async ни defer. HTML документ загружается, дойдя до скрипта извлекается и выполняется сценарий. После этого загрузка продолжается.
- Используется атрибут async. По возможности, скрипт выполняется асинхронно - при этом продолжается загрузка страницы.
- Используется атрибут defer. Скрипт выполняется после полной загрузки страницы.
Тег | |||||
Да | Да | Да | Да | Да |
async | async пусто |
Указывает, что скрипт должен выполняться асинхронно. Логический атрибут. |
charset | charset |
Указывает кодировку внешнего файла скрипта. |
defer | defer пусто |
Указывает, что скрипт должен быть выполнен только после полной загрузки страницы. Логический атрибут. Атрибут применим только для внешних скриптов. |
src | URL |
Содержит адрес файла внешнего скрипта. |
type | media_type |
Указывает тип содержимого скрипта. Атрибут обязателен при использовании в HTML 4.01 и не обязателен в HTML5. |