Простой способ направить исследователей в правильную сторону.
Идея похожа на robots.txt
, только для безопасности. Смысл не в «бумажке ради бумаги», а в снижении трения между исследователем и компанией. Вы даёте понятную точку входа, а взамен получаете сообщения по делу, меньше хаоса и шанс исправить проблему до того, как о ней узнают посторонние.
security.txt
— обычный текстовый файл в кодировке UTF-8. Его основное место — https://ваш-домен/.well-known/security.txt
. Для совместимости допускается копия в корне сайта по адресу /security.txt
, но приоритет у варианта в каталоге /.well-known/
. Файл должен отдаваться по HTTPS с типом содержимого text/plain; charset=utf-8
. Это важно для автоматических сканеров и просто хорошего тона.
Файл относится именно к тому домену, с которого его скачали. Если у вас есть несколько поддоменов или витрин, для каждого лучше публиковать свой экземпляр. Так вы избежите двусмысленности: кто отвечает за конкретную площадку и куда писать.
Во-первых, он экономит время. Исследователь не ищет случайные адреса в подвалах страниц и не спорит с техподдержкой, а сразу попадает на правильный канал. Во-вторых, вы задаёте рамки взаимодействия: что можно тестировать, как оформлять отчёт, как быстро ждать ответа. В-третьих, это укрепляет репутацию — видно, что компания относится к сообщениям об уязвимостях серьёзно и не игнорирует обратную связь.
Отдельный плюс для команд безопасности — дисциплина внутри. Как только появляется понятный вход, появляется и понятная очередь: письма автоматически попадают в трекер, статус двигается, сроки контролируются. Никакой магии, просто порядок.
Файл состоит из строк вида «поле: значение». Комментарии начинаются с символа #
и игнорируются. Некоторые поля можно повторять несколько раз, сохраняя порядок предпочтения. Пустые строки допустимы для читаемости. Не используйте HTML или JSON — это именно простой текст.
Ниже перечислены поля, которые встречаются чаще всего. Два из них обязательны: Contact
и Expires
. Остальные добавляют удобства и повышают доверие, но формально не требуются.
Contact — канал связи для отчётов. Можно указать несколько значений: mailto:
, страницу с формой обратной связи, сервисный портал, иногда номер телефона для экстренных случаев. Размещайте в порядке предпочтения. Если используете электронную почту, лучше сразу предложить шифрование (см. поле Encryption
).
Expires — срок актуальности файла. После указанной даты информацию нельзя считать свежей. Формат — дата и время по международному стандарту (например, 2026-03-31T12:00:00Z
). Рекомендуемый горизонт — не больше года. Продлевать можно заранее, это вполне нормальная практика.
Policy — ссылка на политику ответственного раскрытия уязвимостей. Здесь уместно описать границы тестирования, пример отчёта, сроки обратной связи, как вы поступаете с демонстрационными эксплойтами и что делать в случае утечки данных.
Encryption — ссылка на открытый ключ для шифрования переписки. Обычно размещают OpenPGP-ключ, опубликованный на вашей странице или в репозитории. Сам ключ в поле не вставляют — только ссылку или отпечаток.
Acknowledgments — страница благодарностей исследователям. Это мотивирует сообщать ответственно и помогает выстроить доверие. По желанию можно публиковать «зал славы» с именами, датами и краткими описаниями найденных проблем.
Preferred-Languages — список языков, на которых вам удобнее получать сообщения. Пример: ru, en
. Если поле не указано, по умолчанию обычно предполагают английский.
Hiring — ссылка на вакансии в области безопасности. Поле не влияет на обработку отчётов, но помогает тем, кто хочет к вам присоединиться.
Canonical — канонический URI самого файла. Полезно, если размещаете одинаковые копии на нескольких хостах и хотите зафиксировать «главный» адрес.
# Контакт службы безопасности
Contact: mailto:security@example.com
# Срок действия информации
Expires: 2025-12-31T23:59:59Z
# Канонический адрес файла
Canonical: https://example.com/.well-known/security.txt
# Каналы связи по убыванию предпочтения
Contact: https://example.com/security
Contact: mailto:security@example.com
# Ссылка на открытый ключ для шифрования
Encryption: https://example.com/keys/security-pgp.txt
# Политика ответственного раскрытия
Policy: https://example.com/vulnerability-disclosure
# Благодарности исследователям
Acknowledgments: https://example.com/hall-of-fame
# Предпочтительные языки общения
Preferred-Languages: ru, en
# Срок актуальности файла
Expires: 2026-03-31T12:00:00Z
Можно публиковать файл в «прозрачно подписанном» виде. Внутри виден текст и криптографическая подпись. Это повышает доверие и защищает от скрытой подмены, если кто-то попытается отдать ложный файл через прокси.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Contact: mailto:security@example.com
Encryption: https://example.com/keys/security-pgp.txt
Policy: https://example.com/vulnerability-disclosure
Acknowledgments: https://example.com/hall-of-fame
Preferred-Languages: ru, en
Expires: 2026-03-31T12:00:00Z
-----BEGIN PGP SIGNATURE-----
...подпись...
-----END PGP SIGNATURE-----
Главные требования — доступность по /.well-known/security.txt
, HTTPS и корректный тип содержимого. Ниже — короткие примеры для популярных веб-серверов. Настройки нужно адаптировать под вашу инфраструктуру.
server {
listen 443 ssl;
server_name example.com;
# ...ваши настройки TLS и корня сайта...
location = /.well-known/security.txt {
default_type text/plain; charset=utf-8;
add_header X-Content-Type-Options nosniff;
try_files $uri =404;
}
}
# Включите mod_alias (обычно включен)
Alias "/.well-known/security.txt" "/var/www/security/security.txt"
<Files "/var/www/security/security.txt">
ForceType text/plain; charset=utf-8
Header set X-Content-Type-Options "nosniff"
</Files>
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".txt" mimeType="text/plain; charset=utf-8" />
</staticContent>
<rewrite>
<rules>
<rule name="SecurityTxt">
<match url="^\.well-known/security\.txt$" />
<action type="Rewrite" url="D:\site\security\security.txt" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Проверка нужна по двум причинам. Сначала убедиться, что файл вообще доступен и отдаётся корректно. Затем — что в нём нет опечаток, просроченной даты и нерабочих ссылок. Начните с простого запроса командой curl
и посмотрите заголовки.
$ curl -i https://example.com/.well-known/security.txt
HTTP/2 200
content-type: text/plain; charset=utf-8
content-length: 342
x-content-type-options: nosniff
Contact: mailto:security@example.com
Policy: https://example.com/vulnerability-disclosure
Preferred-Languages: ru, en
Expires: 2026-03-31T12:00:00Z
Дальше проверьте, что дата в поле Expires
ещё не истекла. Перейдите по ссылке из Policy
и убедитесь, что страница доступна. Если публикуете ключ шифрования, скачайте его и проверьте отпечаток. В качестве дополнительных тестов пригодятся онлайн-проверки:
Файл сам по себе проблему не решает. Важно, что будет происходить дальше. Организуйте базовый поток обработки: письма с адреса из Contact
автоматически попадают в систему отслеживания задач, назначаются ответственным, по ним выставляются сроки. Если у вас нет отдельной команды, назначьте дежурных из разработки и эксплуатации, чтобы никто не перекладывал сообщение «на потом».
В политике раскройте ожидаемый формат отчёта. Например: краткое описание, влияние, шаги воспроизведения, артефакты (скриншоты, proof of concept), возможные обходные варианты. Это уменьшит переписку и ускорит разбор. Сроки ответа тоже обозначьте честно — лучше пообещать немного и выполнить, чем наоборот.
Если команде не хватает базовой практики по этичному тестированию и корректной коммуникации с исследователями, закройте этот пробел обучением. В качестве стартовой ступени подойдёт практический курс «Белый хакер» — он помогает выстроить аккуратный подход к поиску уязвимостей и ответственному раскрытию.
Чаще всего забывают продлить срок в Expires
. Это легко автоматизировать. Простейший путь — хранить файл в репозитории, а при каждом релизе шаблон обновлять скриптом. Ниже — наглядная заготовка на оболочке.
#!/usr/bin/env bash
# Генерация security.txt с продлением на 270 дней и подписью OpenPGP
DOMAIN="example.com"
OUT="/var/www/security/security.txt"
KEY_URL="https://$DOMAIN/keys/security-pgp.txt"
POLICY_URL="https://$DOMAIN/vulnerability-disclosure"
HOF_URL="https://$DOMAIN/hall-of-fame"
LANGS="ru, en"
EXPIRES="$(date -u -d '+270 days' +'%Y-%m-%dT%H:%M:%SZ')"
cat > "$OUT" <<TXT
Canonical: https://$DOMAIN/.well-known/security.txt
Contact: https://$DOMAIN/security
Contact: mailto:security@$DOMAIN
Encryption: $KEY_URL
Policy: $POLICY_URL
Acknowledgments: $HOF_URL
Preferred-Languages: $LANGS
Expires: $EXPIRES
TXT
# Необязательно: прозрачная подпись, если настроен gpg
# gpg --clearsign --local-user security@$DOMAIN --output "$OUT" "$OUT"
Добавьте проверку в пайплайн: после деплоя скрипт делает curl
и проверяет код ответа, тип содержимого и свежесть даты. Если что-то не так, сборка падает и вы видите проблему до релиза.
Нет поля Expires. Без него файл считается неактуальным. Даже если всё остальное заполнено правильно, автоматическая проверка может проигнорировать такой файл.
Просроченная дата. Классика. Помогает автоматическое продление и напоминание в календаре команды. Не ставьте горизонт больше года — лучше обновлять чуть чаще.
Ссылки без HTTPS. Поля со ссылками должны указывать на защищённые адреса. Любое исключение подрывает доверие к документу.
Вставили открытый ключ прямо в поле Encryption. В поле должна быть ссылка, а сам ключ — по этой ссылке. Так проще менять ключ и проверять его отпечаток.
Отдаёте HTML вместо текста. Проследите за типом содержимого. Должно быть text/plain; charset=utf-8
. Некоторые серверы по умолчанию пытаются «угадать» тип и подменяют его.
Недоступный контакт. Адрес вида noreply@
или форма без уведомлений — путь в никуда. Письма должны попадать в рабочую очередь, а не в почтовый мешок.
Одна копия на все субдомены. Лучше публиковать отдельные файлы. Так вы явно показываете, кто отвечает за конкретный сервис, и избегаете коллизий.
Небольшой проект. Достаточно одного адреса mailto:
, аккуратной политики на одной странице и свежего Expires
. Не усложняйте раньше времени. Главное — чтобы письма быстро доходили до тех, кто может исправить проблему.
Средняя компания. Добавьте форму на сайте, настройте интеграцию с системой задач и укажите предпочитаемые языки. Публикуйте ключ шифрования и продумайте шаблон ответа: «получили, разбираем, ориентировочный срок — такой-то». Страницу благодарностей можно сделать простым списком.
Группа компаний и холдинги. Чаще всего удобна отдельная зона вида security.company.tld
с единой политикой и контактами CSIRT. На каждую публичную витрину — свой security.txt
со ссылкой на общую политику и локальные нюансы. Для каналов связи используйте очередь с приоритизацией и отчётами.
<h1>Ответственное раскрытие уязвимостей</h1>
<p>Мы благодарны исследователям, помогающим делать наши сервисы безопаснее. Ниже — как прислать отчёт и что ожидать в ответ.</p>
<h2>Что мы принимаем</h2>
<ul>
<li>Уязвимости, влияющие на конфиденциальность, целостность или доступность данных.</li>
<li>Ошибки аутентификации и авторизации, инъекции, XSS, RCE и другие существенные проблемы.</li>
</ul>
<h2>Границы тестирования</h2>
<p>Запрещены атаки, приводящие к отказу в обслуживании, массовой рассылке, социальной инженерии сотрудников и пользователeй, а также доступ к данным третьих лиц.</p>
<h2>Как оформить отчёт</h2>
<ol>
<li>Краткое описание и влияние.</li>
<li>Шаги воспроизведения.</li>
<li>Ожидаемое и фактическое поведение.</li>
<li>Артефакты: скриншоты, логи, PoC.</li>
</ol>
<h2>Наши сроки</h2>
<p>Подтверждаем получение отчёта в течение 3 рабочих дней, делимся предварительной оценкой в течение 10 рабочих дней. По согласованию координируем раскрытие информации.</p>
<h2>Шифрование</h2>
<p>Если отчёт содержит чувствительные данные, используйте наш открытый ключ по ссылке в файле security.txt.</p>
Мобильные приложения. Для магазинов приложений удобнее дать ссылку на страницу политики на вашем сайте, а в самом приложении — раздел «Поддержка и безопасность». Если есть домен, публикуйте security.txt
именно там.
CDN и прокси. Убедитесь, что кэш не мешает обновлению файла, особенно при продлении Expires
. Для /.well-known/
обычно хватает умеренного кэша или полного запрета кэширования.
Сервисы без сайта. Если у вас есть API на отдельном домене, размещайте файл именно там. Для чистого IP-адреса без домена файл не пригодится — лучше выделить доменное имя.
/.well-known/security.txt
и отдаётся как text/plain; charset=utf-8
.Contact
и Expires
, дата не просрочена.https://
и ведут на живые страницы.Encryption
, ключ открывается и проверяется.
security.txt
— простой шаг, который даёт много пользы. Вы объявляете понятные правила, снижения трения хватает уже в первый же день, а исследователи получают ясный канал связи. Создайте файл, положите его в /.well-known/
, свяжите с политикой, добавьте ключ шифрования и автоматизируйте продление Expires
. Это займёт совсем немного времени, зато сэкономит его в будущем и поможет исправлять уязвимости раньше, чем они станут новостями.
Первое — находим постоянно, второе — ждем вас