Мне хотелось бы в очередной раз поговорить о том, как обеспечивается базовый (читай: минимально необходимый) уровень безопасности данных в беспроводных сетях, используемых в IoT-устройствах, на примере LoRaWAN.
Почему именно LoRaWAN? Во-первых, потому, что это хорошо описанный и хорошо проработанный стандарт, на который стоит ориентироваться как на референс, если вы разрабатываете какой-то свой беспроводной протокол. Во-вторых, потому, что это — очень родное и типовое для IoT решение; можно, конечно, разбирать обеспечение безопасности в Wi-Fi или LTE, но для большинства разработчиков это будет бесполезный разбор: вряд ли у вас возникнет необходимость писать свою реализацию Wi-Fi. В‑третьих, именно маломощные IoT-устройства, в которых разработчики экономят каждый байт, часто оказываются наиболее дырявыми — и здесь LoRaWAN даёт хорошее представление, как и байты экономить, и под атаки не подставляться. В‑четвёртых, наконец, потому что буквально за последние несколько дней несколько наших клиентов попросили рассказать им подробнее про защиту данных в LoRaWAN, и этой статьёй я убиваю двух зайцев.
Хотя схема обмена сообщениями в LoRaWAN на картинке выглядит довольно простой — эта простота обманчива: за ней стоит много работы, и ни один пиксель в ней не является лишним. Сейчас вы поймёте, почему.
Разбирать будем на примере LoRaWAN 1.0.2 и плясать от возможных угроз — ибо хороший разработчик всегда должен думать не о том, как его система защищена, а о том, как её могут сломать. Иначе за него об этом подумает кто-то другой.
Итак, какие основные угрозы есть в беспроводной сети — и как от них защищаться?
Перехват пользовательских данных
Самая простая угроза — обычный перехват данных: т.к. радиоволны распространяются неконтролируемо, то абсолютно любой желающий может взять приёмник, настроенный на нужный диапазон и тип модуляции, и слушать всё, что вы передаёте.
Простейший способ защиты от этого — шифрование данных.
В LoRaWAN пользовательские данные шифруются по алгоритму AES-128 с ключом длиной, соответственно, 128 бит (16 байт). AES — алгоритм надёжный, при этом на минимально современных микроконтроллерах, даже не имеющих блока аппаратного шифрования, его использование не влечёт существенных накладных расходов: на Cortex-M3 с частотой 48 МГц один 16-байтный блок шифруется примерно за 100 мкс «с нуля».
Повтор данных
В некоторых случаях злоумышленнику вообще не обязательно знать, что конкретно вы там передаёте. Например, если у вас датчик закрытого окна передаёт что-то одно, а открытого — что-то другое, то можно «одно» записать, не вникая в детали его содержания, заглушить датчик, а чтобы система не заподозрила неладное из-за отсутствия пакетов от датчика — передавать в эфир ранее записанное сообщение.
В LoRaWAN к каждому пакету добавляется счётчик. Если на сервер сети приходит пакет со счётчиком, равным или меньше предыдущему, то этот пакет просто отбрасывается. При двух байтах на счётчик и типичном для IoT-систем темпе передачи сообщений хватит его очень надолго — например, даже бытовая метеостанция, передающая температуру каждую минуту, переполнит его только через полтора месяца (LoRaWAN же позволяет и вовсе 4‑байтный счётчик).
Возникает, правда, очевидная проблема — после переполнения от устройства придёт пакет с номером 0, который, очевидно, будет меньше любого другого номера, но при этом сервер сети должен его воспринять правильно и начать отсчёт пакетов заново. Кроме того, устройство может обнулить счётчик, просто перезагрузившись.
Это достигается одним из двух способов:
- перед посылкой такого пакета устройство обязано пройти процедуру регистрации в сети (в сети LoRaWAN такая процедура называется Join)
- сервер допускает приход очередного пакета с номером 0, при этом отсчёт начинается заново
В LoRaWAN используются обе схемы в зависимости от способа активации устройства — OTAA или ABP (о них поговорим ниже). Для OTAA используется первый вариант, при этом ещё и устройству выдаются новые ключи шифрования — так что даже злоумышленник, просидевший полтора месяца под вашей метеостанцией, не сможет передать ни одного записанного ранее пакета так, чтобы система его приняла.
Для ABP, в котором процедуры регистрации в сети нет, используется второй вариант — впрочем, если срок переполнения счётчика заметно превышает расчётный срок службы устройства, и он может быть отключён. На случай случайной перезагрузки после отправки каждого пакета такое конечное устройство сохраняет значение счётчика в энергонезависимую память.
Вторая схема, конечно, менее безопасна, но на практике допустима — злоумышленнику в ней надо записать не любой пакет, а конкретно нулевой. При желании можно сделать его содержимое отличным от всех прочих пакетов — например, передавать в нём не данные, а информацию о типе и настройках устройства; тогда его перехват и повтор ничего разумного не даст.
Подделка счётчика
Однако тут же возникает вопрос — а если счётчик подделать? Можно положить его в зашифрованную часть пакета, но тогда реальный объём пользовательских данных уменьшится на два байта. Можно шифровать не только пользовательские данные, но тогда, во-первых, придётся подстроиться под 16-байтный размер блока, а во-вторых, увеличится нагрузка на сервер сети, которому для любых действий над пакетом придётся его сначала расшифровывать (в схеме же, когда шифруются только пользовательские данные, если пакет по формальным признакам игнорируется, то расшифровывать не надо ничего).
При этом очевидно, что нам неважно, знает злоумышленник номер пакета или нет — в схеме с перерегистрацией в сети (OTAA) это знание ему вообще ничем не поможет, а в ABP он будет очень долго ждать у моря погоды, т.е. следующего прихода пакета с номером N‑1.
Поэтому вполне достаточно не дать ему этот номер изменить.
Для этого весь пакет в LoRaWAN подписывается криптографической подписью — AES-CMAC, эта подпись в стандарте называется MIC, Message Integrity Code. По ней проверяется, что весь пакет, включая все заголовки и данные, дошёл до сервера в неизменном виде.
То есть, приняв очередной пакет, мы можем быстро посмотреть в его счётчик (адрес отправителя, etc), и если он наш и корректен — тогда уже проверить подпись (потратив на это дополнительные ресурсы), и если подпись тоже корректна — расшифровывать данные и передавать их дальше.
Отслеживание неизменяющихся данных
К сожалению для нас, мало не дать злоумышленнику понять данные или хотя бы повторить их — в ряде случаев ему будет достаточно понять, что они не изменяются. Хрестоматийный пример — домашние счётчики воды: если вы просто хотите узнать, дома ли хозяева, вам совершенно всё равно, сколько там конкретно литров, вам важно знать, увеличивается ли это значение.
Очевидно, что шифрование данных — процедура обратимая (их можно расшифровать), а значит, одни и те же данные, зашифрованные с одним и тем же ключом, всегда выглядят одинаково. Получая пакеты от водосчётчика, у которого не меняются показания, вы можете, не расшифровывая пакет, понять, что они не меняются.
Бороться с этим достаточно просто — должны изменяться либо данные, либо ключ. Чтобы данные изменялись, к ним можно добавлять соль — несколько случайных байт, которые после расшифровки просто выбрасываются. К сожалению, 16 байт пакета — и так негусто, поэтому в общем случае мы не хотим тратить 2–4 байта из них на фактически мусор.
В LoRaWAN применяется более хитрая схема. Помните, что у нас есть счётчик пакетов? Так вот, именно этот счётчик плюс информация об устройстве и пакете (короткий адрес устройства в сети LoRaWAN, направление передачи данных, счётчик 16-байтных отрезков ) шифруются по алгоритму AES, а результат XOR’ится с пакетом пользовательских данных.
В результате и байты полезной нагрузки не теряются впустую, и каждое сообщение выглядит иначе независимо от того, изменялась нагрузка или нет.
P.S. Есть ещё один вариант, чуть более простой: используйте счётчик сообщений в качестве последних N байт ключа. В этом случае ключ каждый раз будет новый, но т.к. сервер знает значение счётчика сообщений (оно в незашифрованной части сообщения), то сможет его восстановить. Минус — если у вас пакет состоит из нескольких 16-байтных блоков, и в них одинаковые данные, то и после шифрования они останутся одинаковыми.
Злоумышленник узнал ключ шифрования
Вполне реальная ситуация — IoT характеризуется применением большого количества устройств, над доступом посторонних к которым вы в общем случае можете не иметь достаточно надёжного контроля (а если вы ещё и оператор сети, то ваши клиенты по определению друг для друга — посторонние люди).
Поэтому, если все ваши устройства имеют один и тот же ключ шифрования, обладатель любого из них может слушать трафик любого другого устройства (вообще говоря, если у него есть возможность модификации прошивки, то для такой операции можно даже в явном виде не знать ключ — пусть новая прошивка берёт его оттуда же, откуда брала старая, и просто выдаёт нам чужие данные).
В LoRaWAN реализованы две схемы использования ключей, индивидуальных для каждого устройства:
- Over The Air Activation, OTAA — ключи генерируются сервером сети каждый раз, когда устройство в ней регистрируется
- Activation By Personalization — ключи заданы производителем и хранятся на устройстве, никогда не меняясь
Всего ключей используется как минимум два — AppSKey, которым шифруются пользовательские данные, и NwkSKey, которым подписывается сообщение.
Очевидно, схема с OTAA более удобная и надёжная — ключи могут меняться с той частотой, с какой хочется, они гарантированно уникальные и не известны никому, кроме сетевого сервера. В ABP ключи не меняются никогда, уникальность зависит от добросовестности производителя устройства (например, мы генерируем эти ключи из уникального ID микроконтроллера, поэтому вероятность их совпадения на двух устройствах ничтожна), и их надо где-то хранить в явном виде, чтобы при подключении устройства к сети прописать их на сервере.
Однако, сама процедура получению ключей в OTAA — это отдельная история, которая, будучи неаккуратно реализованной, может породить ещё несколько видов атак.
Перехват сгенерированных ключей
Очевидно, если при регистрации в сети каждый раз генерируются новые ключи, то их надо синхронизировать между устройством и сервером, а значит, злоумышленник может перехватить и их, обрушив тем самым всю защиту.
Поэтому у устройств LoRaWAN есть третий ключ — AppKey, намертво зашитый в устройство и используемый в один-единственный момент: при регистрации в сети. С его помощью подписывается обмен сессионными ключами между устройством и сервером.
В идеале AppKey должен быть уникален для каждого устройства, но во многих случаях допускается использование одного и того же AppKey — так как нужен он всего один раз, это может быть признано допустимым.
AppKey перед подключением устройства заносится в его настройки на сервере сети.
Итак, устройство формирует запрос на регистрацию (JoinRequest), не шифруя его (в нём нет особо секретной информации), но подписывая его ключом AppKey. Сервер сети, получив этот пакет и проверив адрес отправителя (наше ли это устройство вообще) и потом подпись, отвечает пакетом JoinAccept, в котором передаёт настройки сети — ну и собственно подтверждает регистрацию.
Откуда же берутся ключи AppSKey и NwkSKey?
Это — результат шифрования AES-128 с ключом AppKey комбинации из переданного сервером в ответе случайного числа AppNonce, номера ключа (1 или 2), ID сети и ещё одного случайного числа DevNonce:
NwkSKey = aes128_encrypt(AppKey, 0x01 | AppNonce | NetID | DevNonce)
AppSKey = aes128_encrypt(AppKey, 0x02 | AppNonce | NetID | DevNonce)
Так как и устройство, и сервер после обмена пакетами регистрации знают все эти параметры, то они сгенерируют одинаковые ключи. Таким образом, ни в какой момент никакие ключи по радио передаваться сами по себе не будут, но при и этом устройство, и сервер получат уникальные ключи шифрования и подписи пакетов.
Перехват потока данных на себя
Но и это ещё не всё!
Да, событие регистрации в сети — обычно штука нечастая, но представьте, что злоумышленник смог его инициировать и перехватить.
Тогда, если он просто пошлёт записанный им пакет JoinRequest, ничего в нём не меняя, сервер ответит на него пакетом JoinAccept, сгенерировав новые ключи. После этого атакуемое устройство просто перестанет общаться с сервером, ведь оно-то JoinRequest не инициировало и никаких оснований обновлять ключи не видит. То есть, та же атака повтором — но уже на процедуру регистрации в сети.
Злоумышленник не сможет подделать данные, так как для этого нужно знать ключи, а для их получения нужно знать AppKey, который он не знает. Но выбить устройство из сети — сможет.
Чтобы избежать этого, при регистрации устройство передаёт на сервер случайное число DevNonce (посмотрите, оно выше есть в пакетах). Помимо того, что на его базе генерируются ключи, оно служит ещё одной цели — сервер LoRaWAN хранит архив DevNonce. Если от устройства пришёл повторный запрос регистрации с уже использованным DevNonce, сервер его просто проигнорирует.
В свою очередь, устройство обязано при каждой регистрации генерировать новый DevNonce (то есть, схема с ретрансмитом обычных пакетов — «не получили ответ, выплюнули тот же пакет в радио второй раз» — здесь не работает, JoinRequest каждый раз создаётся заново).
Заключение
Хотя текста получилось много, а картинок — мало, даже это — не полная схема возможных атак на одно только радио (вопросы же о том, почему, например, на устройстве надо шифровать хранимые настройки, причём индивидуальным для каждого устройства ключом, мы и вовсе оставили за скобками, это уже не про радио). Например, в LoRaWAN 1.1 схемы защиты усложнились ещё больше.
Тем не менее, это — джентльменский набор любого радиопротокола, претендующего на минимально достойную защиту информации и при этом предназначенного для работы на маломощных устройствах в низкоскоростных сетях. Более того, это очень хороший пример реализации не просто защищённого протокола, а протокола, написанного для маломощных устройств и минимизирующего расход и эфирного времени, и вычислительных мощностей везде, где это возможно без значимого ущерба для безопасности.
И если вы проектируете свой собственный протокол для IoT, то прекрасно специфицированный LoRaWAN в сочетании с пониманием основных способов проведения атак даёт прекрасную возможность изучить, как правильно эту защиту организовать.