Безопасность — это тема, которая возникает время от времени. Это не проблема с самого начала, но когда случается что-то плохое, его обычно считают виновным. Программное обеспечение является сложным, человек, программирующий машину, далек от совершенства, и пользователь также может не следовать рекомендациям. Итак, как мы можем создать безопасную систему?
Интернет является одним из самых небезопасных мест. Компьютеры с потенциальными рисками безопасности связаны друг с другом. Серверы, которые могут получать произвольные данные. Клиенты, которые выполняют код из неизвестных источников. Хотя мы не можем контролировать безопасность серверов, мы должны что-то сделать для защиты клиентов. Хотя JavaScript можно считать безопасным языком сценариев, код для любых плагинов ActiveX, Flash или Silverlight определенно не является. Более того, даже если сам JavaScript находится в «песочнице», он может использоваться таким образом, что пользователь будет запускать небезопасные действия.
В этом уроке мы увидим модель веб-безопасности в действии. Мы рассмотрим лучшие практики и общие рекомендации по созданию безопасных веб-приложений. Мы узнаем, какова политика совместного использования ресурсов между источниками и как мы можем ее контролировать. Наконец, мы также поговорим о песочнице (внешнем) контенте.
Правила безопасности
Одно из самых важных указаний не имеет прямого отношения к HTML напрямую: используйте HTTPS! Конечно, отношение к HTML заключается в распространении наших гипертекстовых документов. Тем не менее, мы должны понимать, что использование HTTPS для транспортировки нашего документа и использование HTTPS для наших ресурсов — это две разные вещи. Определенно необходимо проверить, действительно ли все содержащиеся ресурсы используют схему https://
.
Другое важное руководство связано с пользовательским контентом. Как только мы позволим пользователям вводить данные в форму, мы должны быть осторожны. Нам нужно не только убедиться, что веб-сервер защищен от распространенных атак, таких как SQL-инъекция, мы также должны убедиться, что хранимые данные не используются в исполняемом коде без предупреждения. Например, мы должны экранировать строки, чтобы они не содержали HTML. HTML сам по себе не является вредоносным, но он может инициировать выполнение скрипта или выборку ресурсов. Один из способов разрешить пользователям писать HTML, который будет размещен на выходной странице без изменений, состоит в внесении в белый список определенных тегов и атрибутов. Другие элементы будут экранированы.
Наши JavaScripts также должны минимизировать воздействие и доверие к сторонним библиотекам. Конечно, мы используем выражение немедленного вызова функции (IIFE), чтобы предотвратить загрязнение глобального контекста; однако другая причина заключается в том, чтобы не пропускать (возможно) важные внутренние состояния, которые затем могут быть изменены добровольно или случайно другими сценариями.
1
2
3
|
(function () {
// Standard code here.
})();
|
Для нас, безусловно, полезно полагаться на 'use strict';
и переданные выгоды. Тем не менее, ограничение работающего скрипта не мешает нам использовать API с потенциально поврежденными данными. Файлы cookie и содержимое в localStorage
могут быть изменены или просмотрены пользователем или другими программами в зависимости от условий, на которые мы не можем повлиять. Поэтому мы всегда должны осуществлять некоторые проверки работоспособности, которые дают нам подсказку как можно скорее выявить недостатки целостности.
Наконец, мы должны убедиться, что используем только доверенные сторонние ресурсы. Использование скриптов с других серверов на нашем сайте позволяет изменять страницы или нарушать конфиденциальность наших пользователей.
Обмен ресурсами между источниками
Концепция совместного использования ресурсов между источниками (CORS) проста. Браузер не позволяет встраивать специальные ресурсы из разных источников, если только источники явно не разрешают это. Специальными ресурсами могут быть, например, веб-шрифты или что-либо, запрошенное через XMLHttpRequest
. Запросы AJAX между источниками по умолчанию запрещены из-за их способности выполнять расширенные запросы, которые создают много проблем безопасности сценариев.
Источник в основном определяется с помощью используемого протокола, комбинации хоста и порта. Поэтому http://a.com отличается от https://a.com , который отличается от https://a.com:8080 . Все они отличаются от http://b.com .
Клиентам может быть разрешено использовать ресурсы, включив в ответ определенный заголовок. Затем браузер определяет, разрешено ли текущему веб-сайту использовать ресурс или нет. Происхождение обычно определяется через домен текущего сайта.
Давайте посмотрим на иллюстративный пример. Далее мы предполагаем, что наша страница находится на foo.com
. Мы запрашиваем данные JSON со страницы, размещенной на bar.com
. Для запроса JSON мы используем XMLHttpRequest
как показано ниже.
1
2
3
4
5
6
7
8
9
|
var xhr = new XMLHttpRequest();
xhr.open(‘GET’, ‘https://bar.com/users’);
xhr.addEventListener(‘load’, function (ev) {
if (xhr.status === 200) {
var result = JSON.parse(xhr.responseText);
// …
}
}, false);
xhr.send();
|
Браузер уже ожидает возможность ответа с поддержкой CORS, добавив в запрос заголовок Origin
:
1
|
Origin: http://foo.com
|
Теперь сервер должен доставить правильный ответ. Мы не только хотим, чтобы транспортировался правильный JSON, но, что еще важнее, нам требуются специальные заголовки CORS. Можно использовать подстановочные знаки. Например, следующий пример предоставит право использовать запрошенный ресурс для любого запроса.
1
|
Access-Control-Allow-Origin: *
|
CORS также можно использовать в качестве альтернативы обходному пути JSONP. JSONP использует сценарии для создания перекрестных AJAX-запросов, что приводит к ответам JSON. До CORS междоменные вызовы были вообще запрещены, но включение сценариев из разных доменов всегда было приемлемым. В большинстве API ответ JSONP инициировался путем предоставления специального параметра запроса с именем функции обратного вызова.
Предположим, что вызов http://bar.com/api
приводит к следующему ответу JSON:
1
|
{ «name»: «example», «factor»: 5, «active»: true }
|
Вызов JSONP, например, http://bar.com/api?jsonp=setResult
даст нам:
1
|
setResult({ «name»: «example», «factor»: 5, «active»: true });
|
Поскольку результат JSONP виден только элементом <script>
, подразумевается метод запроса GET. Нет возможности использовать что-то другое. CORS дает нам гораздо больше свободы в этой области, так что мы также можем определять другие параметры. Это все включено, позволяя нам свободно использовать стандартизированный объект XMLHttpRequest
.
Идеальным решением было бы использовать только JSONP для устаревших браузеров и использовать CORS для более новых. Это предотвратит многие проблемы межсайтового скриптинга (XSS), возникающие на скомпрометированных внешних сайтах. Встраивание скриптов с внешних страниц всегда было рискованным делом. Еще лучшим вариантом было бы перенаправить запрос JSON с нашего сервера на целевой компьютер. Таким образом, мы общаемся с нашим (доверенным) сервером, который получает ответ от цели, оценивает ее работоспособность и возвращает (действительный) результат.
Флаги для песочницы
Каждый document
имеет собственное window
. Доступ к этому window
в основном проксируется к window
текущего контекста просмотра, который управляет вкладкой, которую мы видим. Контекст просмотра создается с несколькими параметрами, такими как родительский контекст, создатель и начальная страница. Наряду с этими параметрами устанавливаются флаги безопасности. Флаги устанавливают возможности и ограничения контекста. Фактически можно предотвратить определенное поведение, такое как запуск сценариев или открытие новых вкладок.
Как мы можем установить флаги безопасности нового контекста? Мы устанавливаем контекст с помощью атрибутов, которые присваиваются элементам, которые должны создать новый контекст. В настоящее время такой атрибут есть только у элемента iframe
, хотя стандартные кадры также подходят под предыдущее описание. Однако стандартные кадры считаются устаревшими и поэтому не очень хорошо поддерживаются. Несмотря на то, что стандарт HTML5 упоминает их, их больше не следует использовать.
Есть куча доступных флагов. Самые важные из них:
-
allow-top-navigation
(позволяет изменить верхний контекст) -
allow-plugins
(включаетembed
,object
,…) -
allow-same-origin
(доступ к контенту из одного источника) -
allow-forms
(формы могут быть представлены) -
allow-popups
(всплывающие окна / новые контексты не будут заблокированы) -
allow-pointer-lock
(включает API блокировки указателя) -
allow-scripts
(разрешает выполнение скрипта)
<iframe>
использует атрибут sandbox
для входа в режим песочницы. Если этот атрибут не указан, все разрешено, как мы его знаем. С этим атрибутом все запрещено. Поэтому ранее заявленные флаги включают определенные функции.
Давайте подробнее рассмотрим флаг allow-same-origin
. По умолчанию политика для iframe
действительно смягчена. Если мы не указываем атрибут « sandbox
то разрешается читать только встроенные страницы из одного домена, например куки или локальное хранилище браузера для данного домена. Конечно, существуют и другие риски — поэтому мы обычно хотим предоставить атрибут sandbox
.
На следующем рисунке показано поведение по умолчанию на простой диаграмме. Хотя второй iframe
разрешен доступ к ранее указанному контенту, первый — нет. Причина в другом домене, выделенном желтым.
Так как же меняется эта картина с атрибутом sandbox
? Контент из другого домена по-прежнему запрещен. Поэтому мы смотрим только на дополнительные возможности для содержимого из того же происхождения. По умолчанию даже контент из одного источника обрабатывается так, как если бы он был из другого источника.
Есть больше возможностей, которые можно контролировать или устанавливать для контекстов, но, к сожалению, они передаются через свои собственные атрибуты. allowfullscreen
примером является атрибут allowfullscreen
. Опять же, это доступно только на iframe
. В принципе это позволяет приложениям переходить в полноэкранный режим.
Кроме того, мы должны обратить внимание на атрибут seamless
для элемента <iframe>
. Этот атрибут включает стилизацию родительского документа в содержащемся документе. Он особенно эффективен в сочетании с атрибутом srcdoc
, который позволяет srcdoc
источник iframe
, не требуя HTTP-запросов или использования URI данных. Таким образом, мы можем очень легко разрешить пользовательский контент, не беспокоясь о выполнении скрипта.
Пример отображения пользовательского контента в изолированной среде показан ниже.
1
2
3
4
5
6
|
<iframe sandbox seamless srcdoc=»
<div class=comment>
<h3>Example User</h3>
<span class=comment-content>Custom, but <strong>safe</strong>!
</div>
«></iframe>
|
Песочницы iframes могут быть использованы для многих задач. Например, если мы хотим безопасно оценить скрипты, мы могли бы передать контент для оценки специальному обработчику, находящемуся внутри iframe
. Обработчик вызовет функцию eval
. Встроенный фрейм помещается в «песочницу», чтобы разрешить только сценарии, ничего больше Никакие всплывающие окна не могут быть открыты, навигация не может быть использована, и весь наш DOM отделен.
Точная реализация также требует обмена сообщениями HTML5 между документами. Мы отправляем запрос на оценку методом postMessage
и ожидаем ответа через событие message
.
Мы также можем помещать в песочницу части текущей страницы, используя политику безопасности контента (CSP). Это свойство обычно передается через HTTP-заголовки страницы, но HTML дает нам возможность установить его в <meta>
.
Пример выглядит следующим образом. Нам нужно использовать правильное значение http-equiv
. Контент представляет собой серию определений, характерных для данной страницы.
1
2
3
|
<meta
http-equiv=»Content-Security-Policy»
content=»default-src https://bar.com; child-src ‘none’; object-src ‘none'»>
|
Различные определения (называемые политиками) также допускают использование подстановочных знаков. Точная цель и внешний вид сильно зависят от используемой политики.
Вывод
Веб-безопасность возможна, хотя ее трудно достичь, и она зависит от множества (часто невозможно контролировать) внешних параметров. Если мы можем минимизировать эти внешние параметры, такие как вставляемый контент или сценарии, то мы обычно в хорошей форме. Большинство векторов атак можно использовать только из сценариев.
Мы видели, что (встроенные) фреймы и гиперссылки можно настраивать с помощью флагов песочницы. Наши собственные ресурсы должны быть развернуты только с учетом CORS.