После перезаписи веб-сайта FormBlob с Хьюго я хотел убедиться, что веб-сайт соответствует передовым методам безопасности и не подвержен каким-либо известным проблемам. Я начал искать меры безопасности веб-сайтов и нашел Mozilla Observatory. На самом первом сканировании я получил тройку. Хотя это и не так уж плохо, я хотел исправить все обнаруженные недостатки, чтобы получить максимально возможный результат. Это также была возможность узнать немного больше о безопасности веб-сайтов.

Так что же такое обсерватория Mozilla?

Следующая цитата взята непосредственно из FAQ на https://observatory.mozilla.org/faq.

Обсерватория тестирует превентивные меры против атак межсайтового скриптинга, атак «человек посередине», междоменной утечки информации, компрометации файлов cookie, компрометации сети доставки контента и неправильно выпущенных сертификатов.

Однако он не проверяет устаревшие версии программного обеспечения, уязвимости SQL-инъекций, уязвимые плагины системы управления контентом, неправильные политики создания паролей или процедуры хранения и многое другое. Они так же важны, как и то, что проверяет обсерватория, и операторы площадок не должны пренебрегать ими просто потому, что они хорошо оцениваются обсерваторией.

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

Настройка FormBlob

Я разделю обсуждение в этой статье на две части — одну для https://formblob.com и другую для https://build.formblob.com. Это связано с тем, что основной сайт — это статический сайт, созданный с помощью Hugo и развернутый на Netlify, а сайт построителя форм — это приложение React, развернутое на AWS ECS за эластичным балансировщиком нагрузки (ELB).

Это вторая часть статьи, в которой обсуждается, как настроить Nginx, чтобы получить оценку A+. Для первой части обсуждения того, как настроить Netlify, нажмите здесь.

Начиная

Чтобы повысить свою оценку за сканирование Mozilla Observatory, вам необходимо добавить заголовки ответов HTTP. Чтобы добавить заголовки ответа HTTP в Nginx, вам нужно будет отредактировать контекст сервера в файле nginx.conf. Здесь я предполагаю, что вы достаточно знакомы с Nginx, чтобы настроить файл конфигурации. Ниже я привожу полный список заголовков и конфигураций, добавленных для получения оценки A+.

server {
  listen 80;
  ...
  return 301 https://$host$request_uri;
}

server {
  listen 443:
  ...
  # Only connect to this site and subdomains via HTTPS for the next two years
  add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
  # Only allow my site to frame itself
  add_header X-Frame-Options "SAMEORIGIN";
  # Prevent browsers from incorrectly detecting non-scripts as scripts
  add_header X-Content-Type-Options "nosniff";
  # Block pages from loading when they detect reflected XSS attacks
  add_header X-Xss-Protection "1; mode=block";
  # Only send the shortened referrer to a foreign origin, full referrer to a local host
  add_header Referrer-Policy "strict-origin-when-cross-origin";
  # Configue CSP
  add_header Content-Security-Policy "default-src 'self' formblob.com *.formblob.com fblob.me *.fblob.me; connect-src 'self' formblob.com *.formblob.com wss://*.formblob.com fblob.me *.fblob.me https://sockjs-mt1.pusher.com firebase.googleapis.com firebaseinstallations.googleapis.com https://www.googleapis.com/webfonts/ www.google-analytics.com; font-src 'self' https://fonts.gstatic.com; img-src https: data: blob: www.googletagmanager.com https://ssl.gstatic.com https://www.gstatic.com; media-src https: data: blob:; script-src 'self' 'unsafe-eval' js.stripe.com https://www.googletagmanager.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'sha256-TQ+edvHvrQ4+h8G+tZQdFsQSrUAeSfwE/D8KjAzDnT0='; style-src 'self' 'unsafe-inline' fonts.googleapis.com; frame-src 'self' formblob.com *.formblob.com fblob.me *.fblob.me js.stripe.com https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/; frame-ancestors 'self' formblob.com *.formblob.com fblob.me *.fblob.me;";
...
}

Результаты теста

Mozilla Observatory оценивает ваш сайт по предварительно заданному набору тестов. Пройдемся по каждому тесту по отдельности.

Политика безопасности контента

Политика безопасности контента (CSP) дает вам детальный контроль над тем, какие ресурсы могут быть загружены на ваш веб-сайт и откуда такие ресурсы разрешены. Он направлен на защиту вашего веб-сайта от уязвимостей межсайтового скриптинга (XSS). Уязвимости XSS связаны с небезопасным встроенным Javascript, и его отключение эффективно устраняет большинство XSS-атак. Однако отключение небезопасного встроенного кода также означает, что весь Javascript должен загружаться из <script src="">tags. Javascript внутри тегов <script>, но не загруженный через src, не будет выполнен.

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

# Configure CSP
add_header Content-Security-Policy "
default-src 'self' formblob.com *.formblob.com fblob.me *.fblob.me;
connect-src 'self' formblob.com *.formblob.com wss://*.formblob.com fblob.me *.fblob.me https://sockjs-mt1.pusher.com firebase.googleapis.com firebaseinstallations.googleapis.com https://www.googleapis.com/webfonts/ www.google-analytics.com;
font-src 'self' https://fonts.gstatic.com;
img-src https: data: blob: www.googletagmanager.com https://ssl.gstatic.com https://www.gstatic.com;
media-src https: data: blob:;
script-src 'self' 'unsafe-eval' js.stripe.com https://www.googletagmanager.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'sha256-TQ+edvHvrQ4+h8G+tZQdFsQSrUAeSfwE/D8KjAzDnT0=';
style-src 'self' 'unsafe-inline' fonts.googleapis.com;
frame-src 'self' formblob.com *.formblob.com fblob.me *.fblob.me js.stripe.com https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/;
frame-ancestors 'self' formblob.com *.formblob.com fblob.me *.fblob.me;";

Я использовал подмножество полного списка директив, относящихся к FormBlob. Я ограничу свое обсуждение здесь только этими директивами и некоторыми другими, которые являются общими. Подробнее о полном списке директив можно прочитать здесь. Также для начала рекомендую использовать шапку Content-Security-Policy-Report-Only вместо Content-Security-Policy, чтобы получить отчет обо всех нарушениях, не ломая при этом свой сайт.

- default-src является резервной директивой для других директив выборки. Указанные директивы не имеют наследования, а неуказанные директивы возвращаются к значению default-src. Здесь вы хотите включить 'self’ (исходный сайт с той же схемой и портом) и другие доверенные домены. Я включаю cloudfront.net, так как это CDN, из которого Netlify обслуживает ресурсы. Рекомендуемая настройка для этого — none, что потребует от вас установки почти всех остальных директив.

- connect-src обеспечивает контроль над запросами на выборку, XHR, источниками событий, маяками и подключениями к веб-сокетам. Это определяет любые ресурсы, к которым вам нужно подключиться. Для соединений через веб-сокет вам нужно будет установить соответствующую схему. Например, wss://\*.formblob.com.

- font-src указывает, с каких URL загружать шрифты. Если вы используете Google Fonts, эта директива должна включать данные `https://fonts.gstatic.com:`.

- img-src указывает URL-адреса, с которых можно загружать изображения. Если вы используете Диспетчер тегов Google, эта директива должна включать www.googletagmanager.com, https://ssl.gstatic.com, https://www.gstatic.com.

- media-src указывает URL-адреса, с которых могут быть загружены ресурсы видео, аудио и текстовой дорожки.

- manifest-src указывает URL-адреса, с которых могут быть загружены манифесты приложений.

- script-src указывает места, из которых может выполняться скрипт. Если вы используете Диспетчер тегов Google, рекомендуется использовать метод nonce для скриптов GTM. Я включаю сюда ключевое слово 'unsafe-eval', потому что FormBlob использует тщательно очищенный метод Function() для оценки ранее установленных полей, чтобы персонализировать работу пользователей, заполняющих формы. Это типичная функция для всех конструкторов форм.

- В эту директиву я также включаю хэш-ключевое слово 'sha256-TQ+edvHvrQ4+h8G+tZQdFsQSrUAeSfwE/D8KjAzDnT0=’. Вы можете использовать хешированный скрипт в качестве ключевого слова, чтобы разрешить загрузку любого скрипта. Этот хэш автоматически генерируется в отчете в консоли для любого скрипта, нарушающего директиву. Вы можете скопировать хэш вместе с одинарными кавычками прямо в директиву, чтобы разрешить скрипт.

- style-src контролирует, откуда стили применяются к документу. Сюда входят <link> элементы, @import правила и запросы, исходящие из Link поля заголовка HTTP-ответа. Вы могли заметить, что я включаю ключевое слово 'unsafe-inline'. Это связано с тем, что я напрямую изменяю встроенные стили с помощью Material UI. Опять же, это потенциальная уязвимость, которую вам следует избегать, если можете. Если вы используете Google Fonts и/или Google Tag Manager, вы должны указать fonts.googleapis.com https://tagmanager.google.com.

- frame-src ограничивает URL-адреса, которые можно встроить на сайт.

- form-action ограничивает URL-адреса, на которые могут отправлять формы.

- frame-ancestors ограничивает URL-адреса, которые могут встраивать запрошенный ресурс в элементы <frame>, <iframe>, <object>, <embed> или <applet>. Эта директива не является откатом к директиве default-src. Если это установлено, X-Frame-Options игнорируются пользовательскими агентами.

- upgrade-insecure-requests указывает пользовательским агентам переписать схемы URL, заменив HTTP на HTTPS. Эта директива предназначена для веб-сайтов с большим количеством старых URL-адресов, которые необходимо переписать.

Печенье

Для FormBlob все используемые файлы cookie создаются на сервере API с целью отслеживания сеансов, и я не устанавливаю файлы cookie на внешнем сервере, на котором размещен веб-сайт. Однако, если вам нужно установить файлы cookie с помощью Nginx, убедитесь, что ваши файлы cookie используют флаг Secure и установите соответствующий атрибут SameSite. Файлы cookie сеанса также должны иметь флаг HttpOnly. Файлы cookie также должны иметь срок действия, как только это необходимо.

Для атрибута SameSite это прямая цитата из https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie.

Строгий означает, что браузер отправляет файл cookie только для запросов того же сайта, то есть запросов, исходящих с того же сайта, который установил файл cookie. Если запрос исходит с URL-адреса, отличного от текущего, файлы cookie с атрибутом SameSite=Strict не отправляются.

Нестрогий означает, что файл cookie не отправляется при межсайтовых запросах, таких как запросы на загрузку изображений или кадров, а отправляется, когда пользователь переходит на исходный сайт с внешнего сайта (например, при переходе по ссылке). Это поведение по умолчанию, если атрибут SameSite не указан.

# Add a cookie with [name] and [value] that expires in 1 day (86400 seconds)
# and restricted to formblob.com and its subdomains
add_header Set-Cookie "[name]=[value]; Max-Age=86400; Path=/; Domain=formblob.com HttpOnly; Secure";

Совместное использование ресурсов между источниками

Совместное использование ресурсов между источниками (CORS) не требуется для большинства веб-сайтов, и FormBlob не является исключением. Это не должно быть установлено, если это не требуется специально.

Закрепление открытого ключа HTTP

Закрепление открытого ключа HTTP требуется только для сайтов с максимальным риском. Это не рекомендуется для большинства сайтов, и вам, вероятно, это не понадобится.

Строгая транспортная безопасность HTTP

HTTP Strict Transport Security (HSTS) уведомляет пользовательские агенты подключаться к данному сайту только через HTTPS, даже если выбрана схема HTTP. Он работает в тандеме с перенаправлениями HTTP на HTTPS и должен быть установлен в заголовках ответа из запроса HTTPS. Рекомендуемая настройка: `Strict-Transport-Security: max-age=63072000; включить поддомены`. Если присутствует флаг `includeSubdomains`, все запросы к поддоменам также будут обновлены до HTTPS. Перед включением этого флага убедитесь, что все поддомены могут обрабатывать HTTPS-трафик.

# Only connect to this site and subdomains via HTTPS for the next two years
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";

HTTPS-перенаправление

Сайты, прослушивающие порт 80, должны перенаправляться на тот же ресурс по HTTPS. После перенаправления HSTS гарантирует, что все будущие HTTP-запросы вместо этого отправляются непосредственно на безопасный сайт. Приведенный ниже параметр перенаправляет весь HTTP-трафик на тот же самый ресурс на HTTPS.

server {
  listen 80;
  ...
  return 301 https://$host$request_uri;
}

Реферальная политика

Для этого есть четыре варианта:

- no-referrer: никогда не отправлять заголовок Referer
- same-origin: отправлять Referrer, но только по запросам к одному и тому же источнику
- strict-origin: отправлять Referrer всем источникам, но только URL без пути (например, https: //example.com/)
- strict-origin-when-cross-origin: отправить полный реферер в том же источнике, URL-адрес без пути в иностранном источнике

По умолчанию вы хотели бы использовать `strict-origin-when-cross-origin`. Это защищает конфиденциальность пользователей при запросах из разных источников, но позволяет отслеживать пользователей с помощью аналитики на вашем собственном сайте.

# Only send the shortened referrer to a foreign origin, full referrer to a local host
add_header Referrer-Policy "strict-origin-when-cross-origin";

Целостность субресурсов

Целостность подресурсов (SRI) защищает от злоумышленников, изменяющих содержимое библиотек Javascript, размещенных в сетях доставки контента (CDN), с целью создания уязвимостей на веб-сайтах, использующих эту размещенную библиотеку. SRI использует хэш содержимого библиотеки, чтобы убедиться, что библиотека не была изменена. Если это так, веб-сайт откажется загружать библиотеку. [Вот хороший ресурс для генерации хэшей SRI](https://www.srihash.org/). Ниже приведен пример встроенной формы из FormBlob с использованием SRI.

<script
 src=”https://unpkg.com/@jeremyling/[email protected]/dist/web.min.js"
 integrity=”sha512–27spyugyD2KOU0tPev6hnJ2bCeKPh5WpMzEWna4uXXCSlSQcFRDxAKZDfBhJ21lF0hyBbTD1KoOXmXJwKU5NHQ==”
 crossorigin=”anonymous”
></script>

X-Content-Type-Options

X-Content-Type-Options — это заголовок, поддерживаемый Internet Explorer, Chrome и Firefox 50+, который указывает пользовательским агентам не загружать скрипты и таблицы стилей, если сервер не укажет правильный тип MIME. Без этого заголовка эти браузеры могут неправильно определять файлы как скрипты и таблицы стилей, что приводит к XSS-атакам.

# Prevent browsers from incorrectly detecting non-scripts as scripts
add_header X-Content-Type-Options "nosniff";

X-Frame-Параметры

X-Frame-Options контролирует, где ваш сайт может быть обрамлен внутри iframe. Это помогает предотвратить кликджекинг, при котором злоумышленник размещает ваш сайт на вредоносной платформе, которая обманом заставляет пользователей переходить по ссылкам, которые злоумышленник контролирует.

# Only allow my site to frame itself
add_header X-Frame-Options "SAMEORIGIN";

X-XSS-защита

X-XSS-Protection — это функция Internet Explorer и Chrome, которая останавливает загрузку страниц при обнаружении отраженных XSS-атак. Хотя сильный CSP может сделать этот заголовок избыточным, он защищает пользователей старых браузеров, не поддерживающих CSP.

# Block pages from loading when they detect reflected XSS attacks
add_header X-Xss-Protection "1; mode=block";

Полная настройка nginx.conf

Мы успешно прошли все тесты и настроили конфигурации, которые помогут нам их пройти. Если вы следовали этому примеру, вы должны получить по крайней мере оценку A с этой настройкой. Вот краткий обзор полной конфигурации.

server {
  listen 80;
...
return 301 https://$host$request_uri;
}
server {
  listen 443:
...
# Only connect to this site and subdomains via HTTPS for the next two years
  add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
# Only allow my site to frame itself
  add_header X-Frame-Options "SAMEORIGIN";
# Prevent browsers from incorrectly detecting non-scripts as scripts
  add_header X-Content-Type-Options "nosniff";
# Block pages from loading when they detect reflected XSS attacks
  add_header X-Xss-Protection "1; mode=block";
# Only send the shortened referrer to a foreign origin, full referrer to a local host
  add_header Referrer-Policy "strict-origin-when-cross-origin";
# Configue CSP
  add_header Content-Security-Policy "default-src 'self' formblob.com *.formblob.com fblob.me *.fblob.me; connect-src 'self' formblob.com *.formblob.com wss://*.formblob.com fblob.me *.fblob.me https://sockjs-mt1.pusher.com firebase.googleapis.com firebaseinstallations.googleapis.com https://www.googleapis.com/webfonts/ www.google-analytics.com; font-src 'self' https://fonts.gstatic.com; img-src https: data: blob: www.googletagmanager.com https://ssl.gstatic.com https://www.gstatic.com; media-src https: data: blob:; script-src 'self' 'unsafe-eval' js.stripe.com https://www.googletagmanager.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'sha256-TQ+edvHvrQ4+h8G+tZQdFsQSrUAeSfwE/D8KjAzDnT0='; style-src 'self' 'unsafe-inline' fonts.googleapis.com; frame-src 'self' formblob.com *.formblob.com fblob.me *.fblob.me js.stripe.com https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/; frame-ancestors 'self' formblob.com *.formblob.com fblob.me *.fblob.me;";
...
}

Заключительные замечания

Имея класс безопасности A+, FormBlob считается самой безопасной платформой для создания форм на рынке. FormBlob уважает конфиденциальность данных и стремится обеспечить защиту всех данных от вредоносных атак. Мы прилагаем усилия, чтобы соответствовать и превосходить все установленные требования безопасности. Несмотря на то, что мы официально не сертифицированы HIPAA или NIST из-за того, насколько мы новы в настоящее время, наша инфраструктура и настройки безопасности соответствуют этим требованиям, и мы уверены, что получим сертификацию в долгосрочной перспективе. Вот результаты Mozilla Observatory других популярных конструкторов форм, с которыми мы сравнивались.