Встраивание безопасности в программное обеспечение с самого начала является наиболее эффективным подходом к кибербезопасности. В отличие от физических систем, поведение которых изучается эмпирически, программные системы полностью описываются исходным кодом, отражающим намерения программиста с использованием синтаксических и семантических правил языка программирования. Поскольку программное обеспечение работает на основе чётко определённых инструкций, мы теоретически можем с высокой точностью рассуждать о его поведении, контролировать его и отслеживать. Разрабатывая всё более совершенные инструменты и процессы безопасности, в пределе мы должны быть способны предотвращать успешные атаки злоумышленников. Так можно ли раз и навсегда решить проблему кибербезопасности?
Как решить проблему кибербезопасности раз и навсегда
Институт Макса Планка по безопасности и конфиденциальности
Аннотация: На соревновании Pwn2Own один участник успешно взломал все основные браузеры — Chrome, Firefox, Safari и Edge, используемые миллиардами людей. Несмотря на десятилетия исследований в области безопасности, обнаружение новых уязвимостей в критически важных программных системах продолжается. В статье обсуждаются причины устойчивости этих проблем и возможные пути их окончательного решения.
Ключевые слова (IEEE): конфиденциальность, программные системы, компьютерная безопасность.
Дополнительные термины: исходный код, системное ПО, инструменты безопасности, лучшие практики, статический анализ, научный прогресс, типы атак, успешные атаки, модель угроз, будущие атаки, типы уязвимостей, универсальные утверждения, недостатки безопасности, секретные значения.
Как окончательно решить проблему кибербезопасности
Представим, что мы использовали все доступные инструменты и процессы для проектирования, разработки и поддержки нашей программной системы, где безопасность является первоочередным приоритетом [1]. Мы применили наступательные и оборонительные стратегии для поиска и устранения уязвимостей, создали модели угроз и внедрили лучшие практики, такие как использование языков программирования с защитой памяти и строгие принципы безопасной разработки. Мы также выполняем непрерывное тестирование, включая фаззинг и инструменты безопасности (статический/динамический анализ приложений, SAST/DAST), и даже формально проверяем критические компоненты. Но достаточно ли этого? Действительно ли мы в безопасности?
Возможно ли, чтобы программная система была полностью свободна от уязвимостей? Если нет, то зачем вообще стараться?
Теперь представьте, что вы — производитель широко используемого мобильного телефона. Несмотря на все усилия по защите безопасности и конфиденциальности, первый джейлбрейк появляется уже через две недели. После выпуска исправления новый джейлбрейк возникает всего через несколько месяцев. Даже после масштабной работы по обеспечению защиты новые взломы продолжают появляться. В течение следующих двух десятилетий вы создаёте критически важные механизмы защиты, многие из которых становятся отраслевым стандартом, но очередной джейлбрейк всё равно вынуждает выпускать обновление безопасности. Означает ли это, что ваши меры защиты неэффективны? Определённо нет.
Нет универсальных утверждений о безопасности
Существует как минимум две причины, по которым мы не можем гарантировать, что любая программная система будет полностью свободна от уязвимостей. Первая — это неизвестные неизвестности: мы не знаем того, чего мы не знаем. Чтобы система могла противостоять атакам, необходимо понимать, какие свойства должны выполняться. Во многих случаях мы осознаём, что то или иное поведение программы является уязвимостью лишь постфактум. Например, спекулятивное выполнение — техника оптимизации производительности, при которой процессоры предсказывают и выполняют инструкции до того, как становится ясно, нужны ли они на самом деле, — было создано для повышения производительности и действительно работает почти всегда. Однако специалист по безопасности с достаточной долей любопытства обнаружил, что все операции, зависящие от секретных данных (например, в криптографическом протоколе), должны выполняться за одинаковое время, независимо от обрабатываемых значений. Это свойство постоянного времени нарушается при спекулятивном выполнении. Злоумышленник может измерять тонкие различия во времени и восстанавливать секретные значения, фактически разрушая криптографическую защиту. Но как проверить или обеспечить это высокоуровневое свойство на практике? Отключить спекулятивное выполнение (Spectre)? Считать быстрые попадания в кеш и медленные промахи (Meltdown)? Запретить все зависящие от данных оптимизации в компиляторе и процессоре? Какое бы решение мы ни выбрали, проверка или обеспечение свойства всегда будет специфичной. Таким образом, защита (например, от атак на постоянное время) никогда не может быть универсальной. Я утверждаю, что атакующий всегда может использовать: 1) отсутствие свойств, о необходимости которых мы даже не подозреваем, и 2) специфику методов, которыми мы пытаемся обеспечить известные нам свойства.
Вторая причина — это разрыв моделирования. Ради эффективности мы обычно рассуждаем о поведении системы в рамках некоторой модели, и на основе свойств этой модели делаем выводы о реальной системе, работающей в production-среде. Например, доказуемая безопасность — криптография, проверка моделей, анализ протоколов, построение систем с встроенной безопасностью, доказательный код и верификация ПО — моделирует полное поведение системы с помощью формальных методов и позволяет делать универсальные утверждения о её свойствах. Техники SAST — такие как символьное выполнение, абстрактная интерпретация и логический статический анализ (включая Infer, CodeQL, SonarQube и FindBugs) — моделируют все релевантные исполнения программы после анализа её исходного или бинарного кода, автоматически проверяя утверждения о поведении системы. Техники DAST — такие как санитизация, изоляция, песочницы и доверенные среды выполнения — моделируют текущее выполнение программы на фиксированном уровне абстракции. Подходы secure-by-design — такие как лучшие практики, моделирование угроз и языковая безопасность — моделируют будущие программные системы ещё до их создания, чтобы избежать внедрения уязвимостей на этапе проектирования.
Атакующий всегда может использовать неверные предположения или более высокий уровень абстракции реальной системы. Например, представьте запуск формально проверенной реализации криптографического протокола на Rust на процессорах с ошибкой в микрокоде. В 2023 году была обнаружена уязвимость (CVE-2023–20593 [2]) в процессорах AMD Zen 2, которая позволяла утечку параметров критически важных операций, таких как memcpy или strcmp, в открытом виде, пересекающем границы виртуальных машин, песочниц, контейнеров и процессов. Для эксплуатации достаточно было вызвать оптимизацию объединения регистров XMM, затем переименование регистра и неверно предсказанный vzeroupper в определённом временном окне. Гарантии, установленные на уровне протокола, исходного кода или даже исполняемого файла, перестают действовать, если процессор работает не так, как мы предполагаем. В зазоре между исходным кодом и исполняемым файлом мы сталкиваемся с неопределённым поведением, которое вызывает 72% [3] эксплойтов «в дикой природе», 86% [4] критических уязвимостей в Android и 70% [5] в Chrome. В зазоре между исполняемым файлом и процессом, работающим на машине, мы находим аппаратные уязвимости: ошибки микрокода, побочные каналы и RowHammer (аппаратная уязвимость, при которой многократный доступ к определённым строкам памяти в DRAM вызывает переключение битов в соседних строках, позволяя атакующему изменять память, к которой он не должен иметь доступ, и потенциально получать несанкционированные привилегии). Имея только программу, без предположений о компиляторе или машине, трудно делать надёжные утверждения о свойствах работающего процесса.
Безопасна или уязвима. В этом не вопрос
Вернёмся к нашему предыдущему вопросу: если нет гарантий, зачем вообще стараться? Дело в том, что безопасность программной системы — это не бинарное свойство, которое можно гарантировать. На практике безопасность носит фундаментально эмпирический характер и скорее является числовой характеристикой, которую необходимо усиливать. Настоящая цель инструментов безопасности (включая те, что предназначены для формальных гарантий в частных случаях) заключается в повышении общей защищённости системы. Другими словами, мы стремимся снизить вероятность того, что злоумышленник сможет нарушить конфиденциальность, целостность или доступность системы. Если вам кажется, что ваша корпоративная система «гарантированно защищена», это лишь означает, что вы пока не столкнулись с контрпримером. Мы можем лишь асимптотически приближаться к абсолютной безопасности.
Нам следует сосредоточиться на степени защищённости наших систем. Во‑первых, рассматривать безопасность как стоимость атаки для злоумышленника. В нашем примере мы говорили о производителе смартфонов, который реагировал на джейлбрейки, разрабатывая новые механизмы защиты. Одни меры были долгосрочными, прорывными и общими, другие — лишь краткосрочными заплатками, но вместе они сделали стоимость следующего джейлбрейка практически неприемлемой. Если несколько лет назад достаточно было получить доступ на чтение/запись в ядре, то сегодня это лишь отправная точка для многомесячной работы. Во‑вторых, рассматривать безопасность в экономических терминах как функцию стимула. Как в случае с производителем телефонов, существует огромный спрос на джейлбрейки и, соответственно, на преодоление защитных мер. С одной стороны — предложение защитных мер от вендора, с другой — спрос на их обход. Если спроса нет, система может казаться защищённой независимо от силы обороны. Если же спрос велик, система может восприниматься извне как уязвимая, хотя внутренне мы прилагаем огромные усилия. Я утверждаю, что отчёты об успешных атаках — будь то через программы bug bounty, упражнения Red Team или иные этичные методы — являются единственным надёжным сигналом текущей силы наших защит. Интересно, что система с большим количеством публично известных уязвимостей (CVE) может технически быть гораздо более защищённой, чем система, у которой CVE нет вовсе.
Следовательно, если наши инструменты и процессы не смогли обнаружить или предотвратить конкретную уязвимость, даже если читатель работает в области верификации ПО — не стоит беспокоиться! Наши инструменты и процессы не неэффективны, их просто нужно закалить. Мы должны выявить конкретную причину, по которой они дали сбой в данном случае, и устранить её. Мы укрепляем нашу защиту шаг за шагом, инкрементально и под руководством контрпримеров. Поскольку безопасность является эмпирическим свойством, подход к ней также должен быть эмпирическим. Вместо заявлений об эффективности наших защит нам следует сосредоточиться на утверждениях о нашей неспособности найти контрпримеры.
Инженерия безопасности
Настоящая сила инструментов безопасности измеряется тем, что они не позволяют успешно атаковать систему даже тем, кто имеет достаточные стимулы для поиска уязвимостей. Тем не менее, мы часто оцениваем научный прогресс, подтверждая лишь неуспешность известных атак. Вместо этого следует сосредоточиться на выявлении тех уязвимостей, которые наши инструменты не способны обнаружить или предотвратить. Например, разработчики (или пользователи) инструмента статического анализа должны оценивать его эффективность, уделяя внимание именно тем уязвимостям, которые находятся в зоне охвата, но остаются невыявленными, и рассматривать эти ограничения как направления для будущих исследований. Простое подтверждение того, что методика сработала для большего числа уязвимостей в конкретном тесте, не помогает оценить научный прогресс. Фокус смещается от универсальных утверждений о безопасности программной системы к их опровержению. Переход от 99% к 100% на каком‑то бенчмарке ничего не говорит о неспособности метода находить или предотвращать будущие атаки.
Мы можем согласиться, что ничто не может полностью гарантировать безопасность. Тем не менее, мы постоянно разрабатываем новые методы для каждого нового типа уязвимости, который пока не обнаружен существующими средствами. Поскольку некоторая степень небезопасности всегда будет сохраняться, новые методы могут вовсе не означать прогресс. Вместо этого для каждой атаки, оказавшейся успешной несмотря на применённые защиты, мы должны задавать себе вопрос: что именно вызвало сбой в нашей защите и что можно улучшить, чтобы обнаружить или предотвратить наиболее общий вариант этой атаки в будущем? Таким образом, наша защита может эмпирически «сходиться» к состоянию, где контрпримеры больше не находятся в пределах разумной стоимости. На самом деле существующие инструменты всегда не находят определённый тип уязвимости по конкретной причине. Почему бы не локализовать и устранить именно эту причину? Такой подход закалки на основе контрпримеров предлагает долгосрочную стратегию, при которой существующие методы систематически расширяются, а не бесконечно дополняются.
При отсутствии успешных атак, которые можно использовать как контрпримеры для нашей защиты, мы можем искусственно увеличить «спрос» (то есть стимул сообщать об успешных атаках) и тем самым превратить наш реактивный подход в проактивный. Например, используя эффективную программу bug bounty, мы приглашаем отчёты об уязвимостях и получаем сигнал о силе нашей защиты. При отсутствии успешных атак мы знаем, что программная система как минимум настолько защищена, насколько наша программа готова платить за доказательства её небезопасности.
Закалка программного обеспечения и защит
Как же окончательно решить проблему кибербезопасности? Абсолютных гарантий в безопасности не существует (всегда может появиться неизвестная атака, которая окажется успешной), но мы можем хотя бы асимптотически приближаться к максимальной защищённости, используя метод контрпримеров. Рассмотрим метафору рыбалки: сеть символизирует инструменты и процессы, которые мы применяем для обеспечения безопасности, а рыбы — это уязвимости в наших программных системах. Я утверждаю, что для каждой сети всегда найдётся рыба, которая проскользнёт сквозь её ячейки. Но это вовсе не умаляет полезности сети. Мы должны осознать, что «идеальной» сети никогда не будет. Вместо этого необходимо развивать систематическую поддержку постепенной, контрпримерно‑ориентированной эволюции наших защит, чтобы эмпирически максимизировать их эффективность. Фокус смещается от концептуальной разработки новых защит к выявлению и устранению ограничений существующих.
Для сообщества специалистов по оборонительной безопасности это означает, что при оценке новой методики вместо доказательств её эффективности следует активно искать свидетельства, которые её опровергают. По крайней мере, необходимо добавлять обсуждения или оценки риска потенциальных атак, которые остаются возможными несмотря на применение данной методики. Техники должны проектироваться так, чтобы их можно было адаптировать для противодействия новым, ещё неизвестным типам атак (например, как CodeQL позволяет добавлять новые правила обнаружения).
Для сообщества специалистов по наступательной безопасности это означает, что любой новый тип атаки фактически становится контрпримером для всех существующих защит. Поэтому, помимо анализа самой атаки, следует добавлять анализ того, почему существующие защиты не сработали. Необходимо давать конкретные рекомендации, как эти защиты можно закалить против наиболее общей версии данной атаки.
Для сообщества инженеров‑программистов это означает наличие возможностей автоматизировать процесс закалки с помощью методов автоматизированной инженерии программного обеспечения [6], [7]. Мы можем разрабатывать техники, которые автоматически локализуют корневую причину сбоя в защите при успешной атаке (как в автоматизированной отладке) и далее автоматически делают наши инструменты эффективными против новой угрозы (как в автоматическом исправлении программ).
Для широкого круга читателей это означает, что нам следует перестать пытаться подтвердить эффективность наших защит и начать безуспешно искать контрпримеры к их эффективности. Именно так мы будем решать проблему кибербезопасности — по одному контрпримеру за раз.
Благодарности
Данная статья является кратким изложением приглашённого пленарного доклада на 27‑м Международном симпозиуме по исследованиям атак, вторжений и защитных механизмов (RAID 2024) с тем же названием.
Список литературы
1. M. Бёме, E. Бодден, T. Бултан, C. Кадар, Y. Лю и G. Сканиелло, «Анализ программной безопасности в 2030 году и далее: исследовательская дорожная карта», ACM Transactions on Software Engineering and Methodology, стр. 1–24, 2025, doi: 10.1145/3708533.
2. T. Орманди, «Zenbleed». cmpxchg8b.com. Дата обращения: 24 июля 2023 г. [Онлайн]. Доступно по ссылке: https://lock.cmpxchg8b.com/zenbleed.html
3. «0day в дикой природе», Google Project Zero, 20 апреля 2023 г. [Онлайн]. Доступно по ссылке: https://docs.google.com/spreadsheets/d/1lkNJ0uQwbeC1ZTRrxdtuPLCIl7mlUreoKfSIgajnSyY/edit
4. «Языки с защитой памяти в Android 13», Google Android Security, 1 декабря 2022 г. [Онлайн]. Доступно по ссылке: https://security.googleblog.com/2022/12/memory-safe-languages-in-android-13.html
5. «Безопасность памяти», Google Chromium Security, 2022 г. [Онлайн]. Доступно по ссылке: https://chromium.org/Home/chromium-security/memory-safety/
6. A. Целлер, «Книга об отладке», CISPA Helmholtz Center for Information Security, Саарбрюккен, Германия, 2024 г. [Онлайн]. Доступно по ссылке: https://www.debuggingbook.org/
7. К. Ле Гуэс, М. Прадель и А. Ройчоудхури, «Автоматизированное исправление программ», Коммуникации Ассоциации вычислительной техники (ACM), т. 62, № 12, стр. 56–65, ноябрь 2019 г., doi: 10.1145/3318162.
Марсель Бёме — преподаватель Института Макса Планка по безопасности и конфиденциальности (Бохум, Германия, индекс 44799), где он руководит исследовательской группой по программной безопасности. Его научные интересы включают основы кибербезопасности, фаззинг (автоматизированное тестирование) и ML4Sec. Бёме получил степень Ph.D. в Национальном университете Сингапура. В 2024 году он выиграл грант Европейского исследовательского совета (ERC Consolidator Grant) и является приглашённым главным редактором и ассоциированным редактором журнала ACM Transactions on Software Engineering and Methodology. Также он выступает в роли председателя программного комитета конференции ACM/IEEE Automated Software Engineering 2025 и симпозиума ACM SIGSOFT International Symposium on Software Testing and Analysis 2026. Контакт: marcel.boehme@acm.org.