Безопасность на производстве: устраняем уязвимости в интернете вещей

В статье рассматриваются потенциальные угрозы, которые могут возникнуть в процессе проектирования, построения и тестирования устройств интернета вещей и методы предотвращения таких угроз
735
В избранное

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

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

Однако производитель хорошо подготовился, он предвидел появление недобросовестных пользователей и не жалел средств на создание системы безопасности. Реализовав ряд мер, от Cde reviews (своего рода рецензии кода человеком, который этот код не писал) до защиты от атак по сторонним каналам (Side-channel attack), а также всесторонних тестов на взлом устройства (Penetration test), производитель получил продукт с хорошо организованной защитой и прекрасно построенной архитектурой. Это серьезно помешало бы нам, если бы мы собирались атаковать сам замок, но имеется другой вариант – провести атаку на контрактных производителей (Contract manufacture), которые непосредственно проводят сборку и испытания замка.

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

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

Защита программного обеспечения

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

Возможные уязвимости файлов прошивки

Рис.1. Возможные уязвимости файлов прошивки

В том случае, если производитель решил все-таки зашифровать свой код и загрузить его через безопасный загрузчик (bootloader), мы можем провести атаку непосредственно на сам загрузчик, который хранится в виде обычного незашифрованного текста, как показано на рисунке 1. Если же производитель использует внешнее тестовое оборудование для проверки прошивки после ее загрузки на устройство, мы можем провести атаку как на саму прошивку, так и на код, который ее проверяет, как показано на рисунке 3. Независимо от того, сколько стадий загрузки и проверки будет добавлено, в конечном итоге все равно останется файл, который хранится в простом незащищенном формате и является уязвимостью системы.

Уязвимость кода при производстве – это далеко не единственная проблема безопасности. Полное решение проблемы «герметичности» кода относится и к другим источникам угроз.

Защита конфиденциальности кода прошивки

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

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

В первую очередь устройство должно быть заблокировано после завершения процесса производства, чтобы ненадежная производственная площадка больше не могла получать доступ к содержимому устройства или изменять его. Затем устройство выполняет обмен ключами с доверенным сервером, используя закрытый ключ, к которому производитель не имеет доступа (обычно такой ключ генерируется на самом устройстве после его блокировки). После завершения обмена ключами информация об устройстве может передаваться конфиденциально между доверенным сервером и устройством.

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

Безопасная отладка

Еще одна уязвимость производственного процесса – это необходимость диагностики проблем на местах или при возврате продукции, а также их исправление. Для выполнения этого процесса выпускающей компании и разработчику системы необходимо получить доступ к заблокированным устройствам. Традиционно это делается путем добавления возможности обхода блокировки, которая в свою очередь является уязвимостью в безопасности.

Наиболее распространенным решением в таком случае является выдача разрешения на выполнение операций типа «разблокировать + стереть» («Unlock + erase»), в результате чего устройство может быть разблокировано, но весь записанный код стирается во время процесса разблокировки. Однако это порождает ряд недостатков: в некоторых случаях доступ к содержимому Flash-памяти может потребоваться для отладки. Кроме того, такой подход открывает дыру в безопасности для атак, направленных на стирание и перепрограммирование устройства с измененным кодом.

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

Обеспечение безопасности кода прошивки

В настоящее время стало возможно реализовывать комплексы мер для решения приведенных выше проблем и тем самым сделать совершение атак на производственные процессы более сложным и менее прибыльным.

 Выборка

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

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

Таким образом, наш метод проверки должен предполагать, что любой код, записываемый в устройство, может быть скомпрометирован. Один из вариантов – это иметь функцию проверки, которая будет вычислять контрольную сумму или хэш кода прошивки через стандартный интерфейс (UART, I²C и так далее). К сожалению, в таком случае возникает угроза возможности замены хэш-функции: если злоумышленник смог провести замену прошивки, он также может заменить и хэш-функцию, чтобы та в свою очередь возвращала корректные значения для создания иллюзии корректности записанного кода прошивки.

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

Производство на двух площадках

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

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

Обновление по воздуху

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

Комплексное решение целостности прошивки

Для обеспечения комплексной защиты устройства необходимо иметь жестко запрограммированный ключ проверки подлинности и жестко запрограммированные инструкции по его использованию. Для этой цели отличным решением является использование памяти ROM (ПЗУ) . Хотя ROM, как известно, легко читается при помощи физических интерфейсов, в нее трудно внести изменения, не нарушив целостность данных.

Загружаемая на устройство прошивка должна быть подписана. После запуска центральный процессор начинает выполнение кода, записанного в ПЗУ, и способен проверить, содержится ли в данной памяти корректный ключ для аутентификации. Если злоумышленник попытается загрузить измененную версию кода, такой код не пройдет проверку подлинности и не будет записан на устройство. Чтобы загрузить измененный код, злоумышленник должен предоставить актуальную подпись для программы, которая может быть создана только с помощью хорошо защищенного закрытого ключа.

Однако при работе с реальными устройствами сложнее поддерживать меры безопасности и избегать появления уязвимостей, так как жестко закодированный открытый ключ (открытый ключ производителя) будет одинаковым для всех устройств. Кроме того, связанный закрытый ключ (закрытый ключ производителя) должен тщательно охраняться производителем микросхемы и никогда не предоставляться пользователям для подписания собственного кода.

При загрузке открытый ключ производителя будет использоваться для проверки любого кода, записанного во Flash-памяти. Это гарантирует, что код или другая информация, предоставленная производителем, не будут изменены (рис. 2, шаг 1).

Аутентификация в процессе загрузки

Рис. 2. Аутентификация в процессе загрузки

Для подписи и аутентификации кода программы пользователям требуется собственная пара ключей (закрытый и открытый ключи пользователя). Чтобы связать открытый ключ пользователя с правами доступа, изготовитель микросхемы должен подписать открытый ключ пользователя закрытым ключом производителя, создав сертификат пользователя. Сертификат представляет собой открытый ключ и подписанные метаданные. При загрузке проверка подлинности сертификата пользователя происходит при помощи открытого ключа производителя, как показано в шаге 2 на рис. 2. Пользовательское программное обеспечение может пройти аутентификацию с пользовательским открытым ключом только при условии наличия действующего сертификата пользователя, как показано в шаге 3 (рис. 2).

Однако для возможности использования устройства только конкретными пользователем требуется дополнительный шаг: алгоритм, описанный в шагах 1-3, может гарантировать только то, что сертификат пользователя был подписан именно производителем микросхемы. Несмотря на то, что такой алгоритм не позволяет случайному пользователю перепрограммировать устройство, другой легальный пользователь может написать свой код и загрузить его при помощи своего легального сертификата. Это, фактически, означает, что если злоумышленник может убедить производителя микросхемы в том, что он является законным пользователем и может создать подписанный сертификат пользователя, то он может и заставить устройство загрузить свой код.

Для того чтобы обеспечить доступ к устройству только для конкретного конечного пользователя, сертификат должен содержать открытый ключ пользователя и идентификатор пользователя (чтобы изменение ключа или идентификатора сделало сертификат недействительным). Производитель микросхемы может записать идентификатор пользователя в код производителя, где он защищен открытым ключом производителя. В таком случае в процессе загрузки происходит проверка подписи сертификата пользователя и сравнение идентификатора пользователя в сертификате и идентификатора в коде пользователя, как показано в шаге 4 на рис. 2.

Итак, вот что происходит, когда кто-то пытается внести изменения в какую-либо часть системы:

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

Теперь у нас есть система, которая дает разрешение на загрузку новой прошивки только при условии наличия корректной подписи. Система основывается всего на двух аспектах, это закрытый ключ производителя и закрытый ключ пользователя, каждый из которых доступен только для подписи новых версий прошивки и хорошо защищен.

Дополнительные средства защиты

Даже приведенной выше системе требуется наличие правильного алгоритма построения сертификата: закрытый ключ производителя чрезвычайно ценен, поскольку он применяется при прошивке каждого выпущенного устройства. Кроме того, закрытый ключ производителя доступен слишком часто – он постоянно используется для подписания сертификатов пользователей.

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

<р2 style="text-align: center;">Обеспечение безопасной разблокировки</р2>

Обеспечение безопасной разблокировки для входа в режим отладки является довольно тривиальной задачей. Каждый разработчик системы создает пару ключей для доступа и записывает открытый ключ отладки на устройство. Ключ может быть проверен тем же самым способом что и прошивка пользователя, предотвращая любое вмешательство в него (шаг 5, рис. 3).

Безопасная разблокировка устройства для его отладки

Рис. 3. Безопасная разблокировка устройства для его отладки

В настоящее время практически каждое устройство на базе микроконтроллера получает уникальный идентификатор. Чтобы разблокировать устройство, его уникальный идентификатор считывается (рис. 3, шаг 1) и подписывается закрытым ключом отладки (рис. 3, шаг 2), создавая тем самым сертификат разблокировки, который затем передается на устройство для аутентификации (рис. 3, шаг 3). Если он проходит проверку подлинности, устройство разблокируется. Такой подход гарантирует, что только пользователи с доступом к закрытому ключу отладки могут генерировать сертификат разблокировки, и, в свою очередь, только пользователи с сертификатом разблокировки могут разблокировать само устройство.

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

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

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

Другие производственные аспекты

Уязвимости в системе тестирования

На сегодняшний день довольно широко распространена практика преднамеренного или непреднамеренного введения уязвимостей в систему безопасности. Примером непреднамеренного появления уязвимости может послужить пример, когда производитель устройства забывает отключить режим отладки в рамках своего теста и поставляет устройства с открытым портом отладки. Преднамеренные дыры в безопасности встречаются еще чаще: разработчик, например, может создать лазейку для повторного открытия доступа к режиму отладки после блокировки устройства путем помещения в код секретной команды или алгоритма, реагирующего на состояние определенного контакта микросхемы.

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

Оффшорный процесс

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

Разработка продукции

Помимо обеспечения безопасного процесса производства, необходимо также не забывать и о процессе разработки продукта. Меры, которые были рассмотрены ранее, не будут иметь должного эффекта, если злоумышленник может незаметно внести изменения в репозиторий исходного кода. Иногда это принимает форму внешнего проникновения (электронного или физического проникновения в здание) или компрометации одного из сотрудников.

Обеспечение безопасности ИТ-систем и кодирование информации играют огромную роль в предотвращении такого типа атак. Методы обеспечения безопасности ИТ-систем включают в себя обеспечение автоматической блокировки всех компьютеров, когда они не используются, наличие индивидуальных паролей для доступа к хранилищам данных, выполнение проверки кода при записи его в репозиторий и так далее.

Заключение

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

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

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

Сравнение позиций

  • ()