Безопасные дефолты: по умолчанию закрыто
Большинство систем работают на настройках по умолчанию — значит, дефолт обязан быть безопасным.
Secure defaults — принцип, по которому из коробки система максимально закрыта, а ослабления требуют явного осознанного действия.
Почему дефолты решают всё
Люди редко меняют настройки по умолчанию: некогда, не знают, забыли. Если «удобный» дефолт небезопасен, большинство установок останутся дырявыми. Поэтому ответственный инженер делает безопасный путь — путём наименьшего сопротивления: чтобы сделать правильно было проще, чем неправильно.
За этим стоит трезвый взгляд на человеческую психологию. Бесполезно рассчитывать, что каждый, кто использует ваш компонент, прочитает документацию до конца, поймёт раздел про безопасность и аккуратно выставит все нужные флаги. В реальности человек берёт библиотеку, копирует пример из первого ответа в поиске и идёт дальше. Если в этом «пути по умолчанию» защита уже включена, безопасными окажутся тысячи установок людей, которые о ней даже не задумывались. Если защиту нужно включать вручную — большинство этого не сделает. Безопасные дефолты — это способ защитить пользователей вашего кода от их же спешки и невнимательности, и в этом их огромная сила.
Обратная сторона того же принципа: ослабление защиты должно быть заметным и осознанным. Если выключить важный контроль так же легко, как поставить галочку, кто-нибудь сделает это «чтобы быстрее заработало» и забудет вернуть. Хороший дизайн делает небезопасный выбор не невозможным (иногда он реально нужен), а явным — с говорящим именем, требующим отдельного решения, заметным на ревью. Тогда каждое отступление от безопасного состояния оставляет след, который можно увидеть и обсудить.
Deny by default
Доступ, фичи, сетевые соединения — всё начинается с «запрещено», и разрешается точечно. Это прямое следствие least privilege и fail securely.
Разница между «запретить лишнее» и «разрешить нужное» кажется чисто стилистической, но на деле она определяет, что произойдёт при ошибке. Если вы начинаете с «разрешено всё» и затем перечисляете запреты, то любой пункт, который вы забыли внести в чёрный список, остаётся открытым — а забыть всегда есть что, потому что список опасного бесконечен и постоянно пополняется. Если же вы начинаете с «запрещено всё» и перечисляете разрешения, то забытый пункт остаётся закрытым: в худшем случае что-то легитимное не заработает, вы это быстро заметите и добавите. Ошибка в белом списке проявляется как сломанная функция, ошибка в чёрном — как тихо открытая дыра. Поэтому белый список почти всегда предпочтительнее.
// Уязвимо: открыли всё, потом «забыли» закрыть лишнее
firewall.allowAll();
firewall.deny(port=22); // надеемся, что перечислили всё опасное
// Безопасно: закрыто по умолчанию, открываем только нужное
firewall.denyAll();
firewall.allow(port=443); // явный белый список
Безопасные дефолты в коде и конфиге
Это касается не только фаервола. Куки — HttpOnly и Secure по умолчанию. Новый пользователь — без админских прав. Загрузка файлов — запрещённые расширения по умолчанию, разрешён узкий список. Шифрование канала — включено, обычный HTTP редиректит на HTTPS.
Особенно коварны дефолты, заданные на этапе создания сущностей. Если новая учётная запись по умолчанию получает повышенные права, а уже потом их «при необходимости урезают», то любой забытый аккаунт остаётся опасным. Правильный порядок обратный: минимум прав на старте, расширение — отдельным осознанным шагом. То же касается доступа к данным: только что созданный документ или запись по умолчанию видны лишь автору, а не «всем в организации». Делая ослабление прав явным действием, вы гарантируете, что широкий доступ появляется там, где его кто-то сознательно выдал, а не там, где про него просто забыли.
# Безопасные дефолты сессионной куки
session:
cookie:
httpOnly: true # недоступна из JavaScript -> защита от кражи через XSS
secure: true # только по HTTPS
sameSite: "Lax" # базовая защита от CSRF
maxAge: 3600 # короткое время жизни
Как работает под капотом: secure-by-default фреймворки
Хорошие фреймворки берут безопасные дефолты на себя. Шаблонизаторы автоматически экранируют вывод (защита от XSS), пока вы явно не попросите «сырой» вывод. ORM по умолчанию параметризует запросы. Заголовки безопасности (CSP, HSTS) включаются конфигом, а не вручную в каждом ответе.
Опасность — в «escape hatch»: конструкциях вроде dangerouslySetInnerHTML, raw, safe, которые отключают защиту. Они нужны редко; каждое их использование — повод для ревью.
Стоит отдать должное авторам таких фреймворков: обратите внимание, что отключатели защиты обычно носят пугающие или подчёркнуто «сырые» имена. Это не случайность, а сознательный приём — сделать опасный выбор визуально заметным. Имя dangerouslySetInnerHTML прямо кричит о риске; тройные скобки в шаблонизаторе выделяются на фоне обычных. Когда вы встречаете такую конструкцию в коде — в своём или на ревью, — это сигнал остановиться и спросить: данные, которые сюда попадают, точно безопасны и точно под нашим контролем? В подавляющем большинстве случаев честный ответ — «защиту выключать не нужно», и строку можно переписать на обычный экранируемый вывод.
Полезно помнить и обратную ловушку: secure-by-default снимает с вас рутину, но не отменяет понимания. Разработчик, привыкший, что «фреймворк всё экранирует сам», однажды соберёт строку HTML вручную в обход шаблонизатора — и защита, на которую он рассчитывал, просто не сработает, потому что данные не прошли через неё. Дефолты защищают только тот путь, который через них проходит. Поэтому важно знать, где именно срабатывает встроенная защита, чтобы случайно не вынести данные за её пределы.
// По умолчанию фреймворк экранирует -> безопасно
<p>{{ userName }}</p>
// Явный «сырой» вывод отключает защиту -> опасно, нужен особый повод
<p>{{{ rawHtml }}}</p> // тройные скобки = без экранирования
Частые ошибки
- Дефолтные пароли и ключи.
admin/admin, демонстрационные секреты в проде — классика взломов. - Отладочный режим в проде.
DEBUG=Trueраскрывает стектрейсы и внутренности. - «Разрешим всё, потом закроем». «Потом» обычно не наступает; начинайте с запрета.
- Бездумное снятие защиты. Отключили экранирование «чтобы заработало» — открыли XSS.
Итоги
- Большинство систем живут на дефолтах — значит, дефолт должен быть самым безопасным состоянием.
- Deny by default: всё закрыто, открываем точечно и осознанно.
- Опирайтесь на secure-by-default фреймворки; «escape hatch» — повод для ревью.
- Никаких дефолтных паролей и DEBUG в проде.