использование скриптов в html
Фундаментальная уязвимость HTML при встраивании скриптов
Чтобы описать суть проблемы, мне нужно рассказать, как вообще устроен HTML. Вы наверняка в общих чертах представляли себе, но я все равно коротко пробегусь по основным моментам, которые понадобятся для понимания. Если кому-то не терпится, сразу переходите к сути.
HTML — это язык гипертекстовой разметки. Чтобы говорить на этом языке, нужно соблюдать его формат, иначе тот, кто читает написанное, не сможет вас понять. Например, в HTML у тегов есть атрибуты:
Если это не то, чего вы ожидали, вам нужно как-то изменить значение атрибута, чтобы в нем не встречалась кавычка. Самое простое, что можно придумать — просто вырезать кавычки.
Чтобы вы могли указать в качестве значения любую строку, формат языка HTML предлагает возможность экранировать значения атрибутов. Вместо кавычки в строке значения вы можете записать последовательность символов [«] и парсер поймет, что в этом месте в исходной строке, которую вы хотите использовать в качестве значения атрибута, была кавычка. Такие последовательности называются HTML entities.
Получается, что преобразование из исходной строки в ту, которую мы запишем между двумя символами кавычек, является однозначным и обратимым. Благодаря этим преобразованиям можно записать и прочитать любую строку в качестве атрибута HTML-тега, не вдаваясь в суть её содержимого. Вы просто соблюдаете формат, и все работает.
Собственно, так работает большинство форматов, с которыми мы сталкиваемся: есть синтаксис, есть способ экранирования контента от этого синтаксиса и способ экранирования символов экранирования, если вдруг такая последовательность встречается в исходной строке. Большинство, но не…
Что тут должно происходить: переменной s должна присваиваться безобидная строка.
Получается парадоксальная ситуация: после встраивания валидного Javascript в валидный документ HTML абсолютно валидными средствами мы можем получить невалидный результат.
На мой взгляд это и является уязвимостью разметки HTML, приводящей к уязвимостям в реальных приложениях.
Как эксплуатируется уязвимость
Конечно, когда вы просто пишете какой-то код, трудно представить, что вы напишете в строке и не заметите проблем. Как минимум, подсветка синтаксиса даст вам знать, что тег закрылся раньше времени, как максимум, написанный вами код не запустится и вы будете долго искать, что произошло. Но это не является основной проблемой с этой уязвимостью. Проблема возникает там, где вы вставляете какой-то контент в Javascript, когда генерируете HTML. Вот частый кусок кода приложений на реакте с серверным рендерингом:
В initialState может появиться в любом месте, где данные поступают от пользователя или из других систем. JSON.stringify не будет менять такие строки при сериализации, потому что они полностью соответствуют формату JSON и Javascript, поэтому они просто попадут на страницу и позволят злоумышленнику выполнить произвольный Javascript в браузере пользователя.
Но на закрывающем теге приколы не заканчиваются. Опасность представляет и открывающий тег
Что видит здоровый человек и большинство подсветок синтаксиса в этом коде? Два тега ниже, хе-хе). Если вы до этого не сталкивались с подобным, то можете подумать, что я сейчас шучу. К сожалению, нет. Вот скриншот DOM-дерева примера выше:
А вы точно спецификация?
Спецификация HTML, помимо того, что запрещает использование легальных последовательностей символов внутри тега «‘; console.log(script.outerHTML); >>> «
Как видите, строка с сериализованным элементом не будет распаршена в элемент, аналогичный исходному. Преобразование DOM-дерево → HTML-текст в общем случает не является однозначным и обратимым. Некоторые DOM-деревья просто нельзя представить в виде исходного HTML-текста.
Как избежать проблем?
Как вы уже поняли, способа безопасно вставить Javascript в HTML нет. Но есть способы сделать Javascript безопасным для вставки в HTML (почувствуйте разницу). Правда для этого нужно быть предельно внимательным всё время, пока вы пишете что-то внутри тега
Точно так же можно экранировать и отдельные строки.
Другой совет — не встраивайте в тег «>
Но, по-хорошему, конечно, если вы хотите нормально разрабатывать приложения, а не аккуратно ходить по минному полю, нужен надежный способ встраивания скриптов в HTML. Поэтому правильным решением считаю вообще отказаться от тега
Код внутри выглядит ужасно и непривычно. Но это код, который попадет в сам HTML. В шаблонизаторе, который вы используете, можно сделать простой фильтр, который будет вставлять тег и экранировать все его содержимое. Вот так может выглядеть код в шаблонизаторе Django:
HTML JavaScript
В современном мире верстальщику, который не знает хотя бы основ JavaScript, будет очень сложно. Совершенно не обязательно быть гуру JavaScript, но основами данного языка владеть необходимо.
JavaScript — клиентский язык программирования (выполняется не на сервере, а непосредственно в браузере пользователя на его локальном компьютере).
JavaScript используется для создания веб-приложений и динамических сайтов способных взаимодействовать с пользователем. В этом уроке вы получите лишь общее представление об этом мощном инструменте веб-дизайна, т.к. изучение JavaScript не входит в начальный курс обучения HTML.
Прежде всего нужно запомнить, что JavaScript является регистрозависимым языком. По этой причине следует запоминать не только названия свойств, методов или ключевых слов JavaScript, но и заглавными или строчными буквами они пишутся.
Код JavaScript может быть написан либо непосредственно в самом HTML-документе, либо в отдельном файле.
Для записи JavaScript-кода в HTML-документе используется тег
В следующем примере код JavaScript написан непосредственно в самом HTML-документе. При загрузке страницы данный код сработает и вызовет всплывающее сообщение:
Запуск скрипта
В предыдущем примере скрипт запустился во время загрузки HTML-документа. Но что делать если вы не хотите, чтобы скрипт запускался автоматически? Вы легко можете сделать так, чтобы запуск скрипта осуществлялся только в том случае, если пользователь делает что-то на странице (например, перемещает курсор мыши или кликае ссылку).
Эти действия называются внутренними событиями (события, для краткости). Есть множество предопределенных внутренних событий, которые осуществляют запуск скрипта. Вы можете использовать обработчики событий, чтобы сообщить браузеру, какое событие должно вызвать тот или иной сценарий. События определяются как атрибуты внутри HTML-тега.
Допустим, вы хотите, чтобы появлялось сообщение после того, когда пользователь нажимает кнопку. Вы можете использовать обработчик событий onclick() для выполнения действий. В следующем примере будет отображено окно предупреждения JavaScript, содержащее сообщение:
Подключение внешнего скрипта
Для подключения JS-файлов также используется тег
Руководство по HTML/CSS/JavaScript
Frontend Guidelines
Правила и руководства оформления, форматирования HTML, СSS и JavaScript кода. Его цель — повысить качество кода и облегчить совместную работу и поддержку инфраструктуры.
От переводчика
Приветствую всех снова, меня зовут Максим Иванов, и сегодня я подготовил перевод, который, возможно, окажется для вас полезным. Бенджамин Де Кук (Benjamin De Cock), разработчик из Бельгии, собрал некоторые указания по оформлению кода, которые позиционируют себя как лучшие практики по написанию HTML, CSS, JS. Конечно, существует множество рекомендаций, например, есть хороший гайдлайн от Google, наверное, есть еще что-то, однако, если следовать хотя бы некоторым из них, то можно надеяться, что ваш код станет лучше. В отдельных случаях следование этим гайдлайнам не полезно, а совсем наоборот. В общем и целом, все зависит от вашего опыта и виденья дела, если вы новичок, то скорее вам будет полезно оценить то, что пишут другие и в обществе считается верным, если вы гуру, то наверное вам и не нужны гайдлайны, которые написаны непонятно кем на ваш взгляд. Итак, приступим.
JavaScript
Семантика
HTML5 предоставляет нам достаточное количество элементов, направленных на точное семантическое описание документа или содержимого. Поэтому, убедитесь в том, что у вас все хорошо с лексикой.
Убедитесь, что вы поняли семантику элементов, которые вы используете в своем коде. Очень плохо использовать семантические элементы не по назначению или не в том месте, если бы вы просто оставались нейтральными к этим этим семантическим элементам.
Лаконичность
Старайтесь кратко писать код. Забудьте о своих старых привычках, перетекающих из XHTML.
Читабельность
Читабельность вашего кода не должна иметь второстепенный характер. Конечно, вы не обязаны быть экспертом по оптимизации (WCAG) для того, чтобы наверняка улучшить свой сайт, однако, уже прямо сейчас вы можете начать немедленно исправлять мелочи, которые в итоге помогут сделать ваш код лучше, например:
— использовать атрибут alt правильно
— связывайте имена (links) и элементы (buttons) и называйте их по смыслу (
— это зверство)
— не полагайтесь на название цветов, в качестве информации
— явно связывайте определенной меткой и элемент формы
Язык документа
Определении языка и кодировки символов не является обязательным, однако рекомендуется всегда объявлять их на уровне документа, даже если они указаны в заголовках HTTP. Приоритетной кодировкой символов является UTF-8.
Эффективность
Если нет уважительных причин добавлять загрузку скриптов до контента, старайтесь прописывать скрипты в конце документа, тем самым ваши скрипты не будут блокировать рендеринг страницы. Если у вас очень тяжелые таблицы стилей, старайтесь разбить их на несколько и загружать по отдельности, так чтобы первичный контент был сразу отрисован. Очевидно, чем больше файлов, тем больше HTTP запросов, два запроса, конечно, медленнее, чем один, но в нашем случае, важным фактором будет являться скорость отображения нужного контента.
Точка с запятой
В то время как точка с запятой является чисто технически разделителем правил, CSS всегда относится к точке запятой, как к оператору завершения.
Box model
Блочная модель, в идеале, должно быть одинаковой для всего документа. Глобальное правило * это нормально, однако нужно помнить, что вы не можете изменить блочную модель у специфичных элементов.
Потоки
Не меняйте стандартное поведение элемента, если вы можете обойтись без этого. Сохраняйте элементы в их классическом отображении в потоке документа. Например, удаление пробелов под изображением, не должно влиять на его отображение.
Аналогично, не вытаскивайте из основного потока элементы, если вы можете обойтись без этого.
Позиционирование
Существует множество способов позиционирования элементов в CSS, однако старайтесь ограничивать себя свойствами представленными ниже. В порядке предпочтения:
Селекторы
Минимизируйте сочетания селекторов связанных с DOM. Иногда, лучше добавить лишний класс элементу, который вы хотите найти, чем искать его через три структурных селектора: псевдо-класс, потомок, комбинатор.
Избегайте перегрузку селекторов.
Специфичность
Не используйте значения и селекторы, которые нельзя переопределить. Минимизируйте использование id (идентификаторов) и избегайте правило !important
Переопределения
Переопределение стилей селекторов затрудняет отладку. Избегайте этого, если можете.
Наследование
Не дублируйте определения селекторов, стили которых и так могут передаваться по наследству.
Лаконичность
Пусть ваш код будет немногословным. Старайтесь избегать использование несколько свойств в том месте, где это вовсе не нужно.
Англоязычные обозначения
Предпочтительно использовать английскую математику.
Вендорные префиксы
Не используйте устаревшие префиксы. Если вам действительно необходимо использовать их, в этом случае, добавляйте их перед стандартными.
Анимация
Иногда предпочтительнее использовать переходы (transitions), нежели анимацию (animations). Избегайте анимирование свойств в пользу opacity и transform.
Единицы измерения
Используйте безразмерные значения. Старайтесь использовать rem, если вы рассчитываете все в относительных единицах. Предпочтительно использовать секунды, а не миллисекунды.
Цветовая модель
Если нужна прозрачность, использовать rgba. В противном случае, всегда используйте шестнадцатеричный формат.
Отрисовка
Избегайте http-запросов, если ресурс легко воспроизводится с помощью CSS.
JavaScript
Производительность
Легкочитаемый, выразительный и правильный код важнее производительности. На самом деле, JavaScript никогда не будет вашим узким местом в расчете на производительность. Лучше уделять внимание таким вещам, как сжатие изображений, оптимизация http-запросов и доступа к сети, перерасчет DOM-дерева. Если вы запомните хотя бы одну из рекомендаций в данной статье, опирайтесь на нее.
Независимость от внешнего состояния
Старайтесь создавать свободные функции. Все функции и методы в идеале не должны нести никакого побочного характера, зависеть от внешних данных, и в итоге, они должны возвращать новые объекты, а не вносить изменения в существующие.
Нативность
Полагайтесь на стандартные методы, как можно больше.
Принудительное сравнение
Лишние сравнения не имеют смысл, избегайте их.
Циклы
Не используйте циклы, так как они заставляют вас использовать изменяемые объекты. Полагайтесь на методы Array.prototype.
Если вы не можете использовать методы Array.prototype, используйте рекурсию.
Вот общая функция Loop, делающая рекурсию проще в использовании.
Аргументы
Забудьте об arguments как об объекте. Остаточные параметры это всегда лучший вариант, потому что:
1. Передаваемое имя дает лучшее представление о том, что ожидает функция на входе
2. Остаточный параметр — это массив и он легок в использовании
Использование метода apply
Забудьте метода apply(). Вместо этого используйте оператор распространения (вызова функции).
Использование метода bind
Не используйте bind(), когда есть более идиоматические подходы.
К прочтению:
1. Babeljs (пример в ES5): плохо vs хорошо
2. bind(), а также итераторы в JavaScript
Методы высшего порядка
Избегайте вложенных функций там где это не нужно.
Композиции
Избегайте несколько вложенных вызовов функций. Используйте вместо них композицию.
К прочтению:
1. Babeljs (пример в ES5): плохо vs хорошо
Кеширование
Дорогостоящие операции с большими структурами данных непростительны, если вы не кешируете их.
Переменные
Используйте const вместо let и let вместо var.
Условия
Будьте привержены концепции IIFE’s (немедленно выполняемый функтор), используйте возвращаемые значения в условиях (if, else if, else и switch).
Итераторы
Избегайте цикл перебора for. in
Разница между циклом и методом в том, что цикл перечисляет свойства и из цепочки прототипов.
Объект как ассоциативный массив
В то время как объекты можно законно использовать, как правило, ассоциативный массив (Map) лучше и мощнее. Если сомневаетесь, тогда точно используйте Map.
Каррирование
Каррирование является мощной, но в свою очередь чужеродной парадигмой для многих разработчиков. Не злоупотребляйте.
Читабельность
Не скрывайте намерения вашего кода, используя умные трюки.
Повторное использование кода
Не бойтесь создавать много мелких и многоразовых функций, они очень полезны.
К прочтению:
1. Babeljs (пример в ES5): плохо vs хорошо
Зависимости
Минимизируйте зависимости стороннего кода, если вы не в курсе как он работает. Не нужно загружать всю библиотеку ради нескольких методов, которые легко воспроизводимы:
К прочтению:
1. Babeljs (пример в ES5): плохо vs хорошо
2. Путь JavaScript модуля и документация по библиотеке Underscore
Руководство по JavaScript, часть 1: первая программа, особенности языка, стандарты
Недавно мы провели опрос, посвящённый целесообразности перевода этого руководства по JavaScript. Как оказалось, около 90% проголосовавших отнеслись к данной идее положительно. Поэтому сегодня публикуем первую часть перевода.
Это руководство, по замыслу автора, рассчитано на тех, кто уже немного знаком JavaScript и хочет привести свои знания в порядок а также узнать о языке что-то новое. Мы решили немного расширить аудиторию этого материала, включить в неё тех, кто совершенно ничего не знает о JS, и начать его с написания нескольких вариантов «Hello, world!».
Hello, world!
Программа, которую по традиции называют «Hello, world!», очень проста. Она выводит куда-либо фразу «Hello, world!», или другую подобную, средствами некоего языка.
JavaScript — это язык, программы на котором можно выполнять в разных средах. В нашем случае речь идёт о браузерах и о серверной платформе Node.js. Если до сих пор вы не написали ни строчки кода на JS и читаете этот текст в браузере, на настольном компьютере, это значит, что вы буквально в считанных секундах от своей первой JavaScript-программы.
Этот текст можно ввести с клавиатуры, можно скопировать и вставить его в консоль. Результат будет одним и тем же, но, если вы учитесь программировать, рекомендуется вводить тексты программ самостоятельно, а не копировать их.
Первая программа в консоли браузера — вывод сообщения в консоль
Ещё один вариант браузерного «Hello, world!» заключается в выводе окна с сообщением. Делается это так:
Вот результат выполнения этой программы.
Вывод сообщения в окне
Обратите внимание на то, что панель инструментов разработчика расположена теперь в нижней части экрана. Менять её расположение можно, воспользовавшись меню с тремя точками в её заголовке и выбирая соответствующую пиктограмму. Там же можно найти и кнопку для закрытия этой панели.
Инструменты разработчика, и, в том числе, консоль, имеются и в других браузерах. Консоль хороша тем, что она, когда вы пользуетесь браузером, всегда под рукой.
Использование JavaScript на веб-страницах
Клиентский JavaScript-код может встраиваться в HTML-документы четырьмя способами:
встроенные сценарии между парой тегов ;
из внешнего файла, заданного атрибутом src тега :
В языке разметки XHTML содержимое тега
Сценарии во внешних файлах
Использование тега с атрибутом src дает ряд преимуществ:
HTML-файлы становятся проще, т.к. из них можно убрать большие блоки JavaScript-кода, что помогает отделить содержимое от поведения.
JavaScript-функцию или другой JavaScript-код, используемый несколькими HTML-файлами, можно держать в одном файле и считывать при необходимости. Это уменьшает объем занимаемой дисковой памяти и намного облегчает поддержку программного кода, т.к. отпадает необходимость править каждый HTML-файл при изменении кода.
Атрибут src принимает в качестве значения произвольный URL-адрес, поэтому JavaScript-программа или веб-страница с одного веб-сервера может воспользоваться кодом (например, из библиотеки подпрограмм), предоставляемым другими веб-серверами. Многие рекламодатели в Интернете используют этот факт.
Возможность загружать сценарии с других сайтов еще больше увеличивает выгоды, получаемые от кэширования: компания Google продвигает использование стандартных, хорошо известных URL-адресов для часто используемых клиентских библиотек, что позволяет браузерам хранить в кэше единственную копию, совместно используемую многими сайтами в Веб. Привязка сценариев JavaScript к серверам компании Google может существенно уменьшить время запуска веб-страниц, поскольку библиотека наверняка уже будет храниться в кэше браузера пользователя, но при этом вы должны доверять стороннему программному коду, который может оказаться критически важным для вашего сайта. За дополнительной информацией обращайтесь по адресу: code.google.com/apis/ajaxlibs/.
Возможность загрузки сценариев со сторонних серверов, отличных от тех, где находятся документы, использующие эти сценарии, влечет за собой важное следствие, имеющее отношение к обеспечению безопасности. Политика общего происхождения предотвращает возможность взаимодействия сценария на JavaScript в документе из одного домена с содержимым из другого домена. Однако следует отметить, что источник получения самого сценария не имеет значения, значение имеет источник получения документа, в который встраивается сценарий.
Таким образом, политика общего происхождения в данном случае неприменима: JavaScript-код может взаимодействовать с документами, в которые он встраивается, даже если этот код получен из другого источника, нежели сам документ. Включая сценарий в свою веб-страницу с помощью атрибута src, вы предоставляете автору сценария (или веб-мастеру домена, откуда загружается сценарий) полный контроль над своей веб-страницей.
Обработчики событий в HTML
JavaScript-код, расположенный в теге
JavaScript-код может регистрировать обработчики событий, присваивая функции свойствам объектов Element (таким как onclick или onmouseover), представляющих HTML-элементы в документе.
Свойства обработчиков событий, такие как onclick, отражают HTML-атрибуты с теми же именами, что позволяет определять обработчики событий, помещая JavaScript-код в HTML-атрибуты. Например:
Обратите внимание на атрибут onClick. JavaScript-код, являющийся значением этого атрибута, будет выполняться всякий раз, когда пользователь будет щелкать на кнопке.
Атрибуты обработчиков событий, включенных в разметку HTML, могут содержать одну или несколько JavaScript-инструкций, отделяемых друг от друга точками с запятой. Эти инструкции будут преобразованы интерпретатором в тело функции, которая в свою очередь станет значением соответствующего свойства обработчика события.
Однако обычно в HTML-атрибуты обработчиков событий включаются простые инструкции присваивания или простые вызовы функций, объявленных где-то в другом месте. Это позволяет держать большую часть JavaScript-кода внутри сценариев и ограничивает степень взаимопроникновения JavaScript и HTML-кода. На практике многие веб-разработчики считают плохим стилем использование HTML-атрибутов обработчиков событий и предпочитают отделять содержимое от поведения.
JavaScript в URL
«Ресурсом», который определяется URL-адресом javascript:, является значение, возвращаемое этим программным кодом, преобразованное в строку. Если программный код возвращает значение undefined, считается, что ресурс не имеет содержимого.
Некоторые браузеры (такие как Firefox) выполняют программный код в URL и используют возвращаемое значение в качестве содержимого нового отображаемого документа. Точно так же, как при переходе по ссылке http:, браузер стирает текущий документ и отображает новое содержимое. Значение, возвращаемое примером выше, не содержит HTML-теги, но если бы они имелись, браузер мог бы отобразить их точно так же, как любой другой HTML-документ, загруженный в браузер.
Если необходимо гарантировать, что URL-адрес javascript: не затрет документ, можно с помощью оператора void обеспечить принудительный возврат значения undefined:
Без оператора void в этом URL-адресе значение, возвращаемое методом Window.open(), было бы преобразовано в строку и (в некоторых браузерах) текущий документ был бы затерт новым документом.