По своему виду метка ничем не отличается от обычного текста, но благодаря ей пользователь может выбрать элемент формы кликом по тексту, расположенному внутри элемента , а не по самому элементу .

Поддержка браузерами Тег
Opera
IExplorer
Edge
Да Да Да Да Да Да
Атрибуты Атрибут Значение Описание
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.