Содержание


Проектирование и построение защищенных IoT-решений, часть 2

Защита IoT-данных на уровне сети/транспорта

В данной статье описывается шифрование данных и защита API-интерфейсов сервиса IBM Watson IoT Platform

Comments

Серия контента:

Этот контент является частью # из серии 3 статей: Проектирование и построение защищенных IoT-решений, часть 2

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Проектирование и построение защищенных IoT-решений, часть 2

Следите за выходом новых статей этой серии.

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

В этом цикле статей вопросы безопасности рассматриваются на трех архитектурных уровнях IoT-приложения: на уровне устройств, на уровне данных и на уровне приложений. В первой части данного цикла излагаются основы безопасности IoT-решений, а затем описываются различные подходы к защите устройств и шлюзов. Вторая часть (данная статья) посвящена различным аспектам защиты уровня сети/транспорта, включая сервис IBM Watson IoT Platform. В третьей части излагаются требования к защите уровня приложений, а также описывается подход к реализации этих требований для аналитического IoT-приложения, созданного на платформе IBM Bluemix.

Защита данных, передаваемых по сети

Технология Transport Layer Security (TLS) обеспечивает безопасность данных, передаваемых по сети. При этом данные подвергаются шифрованию, чтобы воспрепятствовать прослушиванию и пониманию контента любыми сторонними лицами. Технология TLS (также известная как SSL) широко применяется для защищенного доступа на многих веб-сайтах. TLS гарантирует установление доверительных отношений между сервером и клиентом до начала передачи данных. Это достигается при помощи сертификатов сервера, проверку которых должны осуществлять клиенты. В некоторых случаях сервер также проверяет специфические для клиента сертификаты.

Протокол MQTT задействует TCP в качестве транспортного протокола; по умолчанию соединение не использует шифрованный обмен данными. Реализация TLS ухудшает производительность при передаче данных и повышает нагрузку на сервер, однако большинство MQTT-брокеров (в том числе IBM Watson IoT Platform) поддерживает использование TLS, чтобы позволить приложениям защитить обмен конфиденциальными данными.

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

Если для аутентификации и авторизации вы используете поля username и password пакета MQTT CONNECT, вам настоятельно рекомендуется использовать технологию TLS. Стандартный порт для защищенного MQTT-соединения имеет номер 8883.

Шифрование данных

В большинстве MQTT-развертываний используется технология TLS (transport layer security), поэтому данные подвергаются шифрованию, а и их целостность - проверке. Сервис IBM Watson IoT Platform поддерживает протокол TLS 1.2 для защиты данных, передаваемых по сети. В этой статье мы описываем, каким образом приложения могут предоставлять дополнительные механизмы безопасности путем поддержки обмена шифрованными сообщениями, даже если обеспечивающая сеть не поддерживает TLS.

Шифрованию подлежат только данные полезной нагрузки сообщения (частная информация датчика). Поля сообщения в сообщениях MQTT PUBLISH не изменяются. Информация о полезной нагрузке является двоичной, поэтому при передаче сообщения не требуется никакого специального механизма кодирования. Кроме того, на стороне брокера не требуется никаких специфических изменений, поскольку формат MQTT-сообщения остается прежним. Лишь приложению, которое интерпретирует полезную нагрузку сообщения, нужно дешифровать сообщение, чтобы понять его содержимое.

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

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

Таблица 1. Преимущества и недостатки шифрования полезной нагрузки
Преимущества Недостатки
  • Полная безопасность сообщения на протяжении всего маршрута
  • Дополнительный слой защиты для приложений, передающих конфиденциальные данные
  • Подходит для ситуаций, в которых невозможно использовать TLS
  • Реализация может оказаться невозможной в устройствах с очень малым объемом ресурсов.
  • Если защищенный канал обмена данными не используется, то у атакующего сохраняется возможность изменять сообщения.
  • Если полезная нагрузка зашифрована, сервис IBM Watson IoT Platform не способен определить информационные точки в сообщении. Вследствие этого сервис Real-Time Insights не может отобразить инструментальную панель обычным способом.

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

  • Избегайте недолговечных соединений.
    Установление нового TLS-соединения для каждой передачи данных может привести к излишнему потреблению пропускной способности. Используйте долговечные соединения.
  • Внедрите методики возобновления сеанса.
    Методики возобновления TLS-сеанса разрешают повторное использование уже установленного TLS-сеанса после того, как он снова подключится к серверу, благодаря чему клиенту и серверу не нужно снова полностью выполнять процедуру TLS handshake.
  • Используйте актуальную версию TLS.
    Против версий TLS 1.0 и 1.1 было совершено несколько известных атак. По мере возможности используйте версию TLS 1.2.

Шифрование полезных нагрузок сообщений

В следующих примерах кода демонстрируется шифрование полезной нагрузки сообщения.

Вы можете загрузить этот пример кода из проекта DeviceSimulatorDemo на сайте GitHub.

В листинге Листинг 1 показано, как шифруются MQTT-сообщения перед их публикацией в теме. В листинге Листинг 2 демонстрируется один из подходов к шифрованию, представляющий собой основанный на AES метод с использованием секретного ключа.

Листинг 1. Публикация шифрованных сообщений
 handler.publishBytes("iot-2/evt/" + MqttUtil.DEFAULT_EVENT_ID + "/fmt/json", IOTSecurityUtil.encryptString(objtd.toString(), strKey, uniqueParam), false, 0); public void publishBytes(String topic, byte[] message, boolean retained, int qos) { // Проверка, подключен ли клиент if (isMqttConnected()) { // Создание нового сообщения MqttMessage из строки сообщения MqttMessage mqttMsg = new MqttMessage(message); // Установка флага retained mqttMsg.setRetained(retained); // Установка качества сервиса mqttMsg.setQos(qos); try { client.publish(topic, mqttMsg); } catch (MqttPersistenceException e) { e.printStackTrace(); } catch (MqttException e) { e.printStackTrace(); } } else { connectionLost(null); } }
Листинг 2. Метод шифрования на основе AES
 public static byte[] encryptString(String strMsg, String strKey, String iv) { byte[] encrypted = null; IvParameterSpec ivspec = null; SecretKeySpec keyspec; try { // Создание ключа и шифра ivspec = new IvParameterSpec(iv.getBytes()); keyspec = new SecretKeySpec(strKey.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // Шифрование текста cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec); encrypted = cipher.doFinal(strMsg.getBytes("UTF-8")); } catch (Exception e) { e.printStackTrace(); } return encrypted; }

Дешифрование полезной нагрузки сообщений

В следующих примерах кода демонстрируется дешифрование полезной нагрузки сообщения.

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

Листинг 3. Получение и дешифрование сообщения
 @Override public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { super.messageArrived(topic, mqttMessage); System.out.println("topic " + topic); Matcher matcher = pattern.matcher(topic); if (matcher.matches()) { String deviceid = matcher.group(1); byte[] rawPayload = mqttMessage.getPayload(); String payload = IOTSecurityUtil.decryptString( rawPayload, strKey, uniqueParam); // Дальнейшая обработка дешифрованного сообщения в случае необходимости } }
Листинг 4. Метод дешифрования на основе AES
 public static String decryptString(byte[] byteMsg, String strKey, String iv) { String strReturn = null; IvParameterSpec ivspec = null; SecretKeySpec keyspec; try { // Создание ключа и шифра ivspec = new IvParameterSpec(iv.getBytes()); keyspec = new SecretKeySpec(strKey.getBytes(), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // Дешифование полезной нагрузки cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec); strReturn = new String(cipher.doFinal(byteMsg)); } catch (Exception e) { e.printStackTrace(); } return strReturn; }

Проверка целостности сообщения

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

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

  • Контрольная сумма
    Криптографическая контрольная сумма - это математическая величина, вычисленная на основе контента MQTT-сообщения. Контрольная сумма создается посредством серии вычислений, которые преобразуют полезную нагрузку сообщения в фиксированную строку цифр, которая называется значением хэш-функции. Примеры алгоритмов вычисления контрольной суммы (хэш-алгоритмов): MD5, Cyclic Redundancy Check (CRC) и Secured Hash Algorithm (SHA) 1/2. Значение контрольной суммы можно добавить в начало полезной нагрузки MQTT-сообщения. Приложение, получившее такое сообщение, должно повторно вычислить контрольную сумму, чтобы верифицировать целостность этого сообщения.
  • Message Authentication Code (MAC)
    MAC (message authentication code) - это информация, предназначенная для верификации того, что данное сообщение поступило от доверенного отправителя и что в процессе передачи это сообщение не было изменено кем-либо или чем-либо. На вход MAC-алгоритма поступают секретный ключ и полезная нагрузка MQTT-сообщения, подлежащая аутентификации; выходом алгоритма является MAC-код. MAC-код отсылается в MQTT-сообщении. Кроме того, приложение должно иметь доступ к тому же секретному ключу, который использовался при генерации MAC-кода.
  • Цифровая подпись
    Цифровая подпись - это цифровой код (генерируемый и аутентифицируемый посредством шифрования с открытым ключом), который можно присоединить к MQTT-сообщению, чтобы осуществить проверку его контента и идентификационных данных его отправителя.

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

Защита API-интерфейса в API-интерфейсах REST-типа сервиса Watson IoT Platform

API-интерфейсы REST-типа (REST API) сервиса Watson IoT Platform предоставляют различные меры безопасности при обращении к интерфейсам REST API для чтения шифрованных данных и для проверки подлинности сообщения.

Обращение к интерфейсам REST API

Сервис IBM Watson IoT Platform предоставляет API-интерфейс REST-типа для поддержки различных функций, в том числе для управления устройствами и для обращения к данным, поступающим из устройств. Доступ к этому API-интерфейсу защищен посредством стандартной аутентификации в сочетании с технологией HTTPS:

  • Используйте HTTPS (порт 443), а не HTTP (порт 80)
  • В качестве имени пользователя используйте ключ API Key своего приложения
  • В качестве пароля используйте соответствующий токен Auth Token

Чтобы аутентифицировать API-вызовы, необходимо создать ключ API Key в среде Watson IoT Platform. Следуйте пошаговым инструкциям по созданию ключа API Key, изложенным в документации по Watson IoT Platform.

Затем с помощью этого ключа API Key можно вызвать клиент API Client com.ibm.iotf.client.api.APIClient, который, в свою очередь, может вызвать API-интерфейс сервиса IBM Watson IoT Platform. В листинге Листинг 5 демонстрируется создание экземпляра APIClient. Он читает из файла свойств те свойства, которые необходимы для создания экземпляра API-клиента.

Листинг 5. Создание экземпляра APIClient
 public void doApp() { // Чтение свойств из файла conf Properties props = MqttUtil.readProperties("MyData/application.prop"); try { // Создание экземпляра класса путем передачи файла свойств this.apiClient = new APIClient(props); System.out.println("Adding a new device.."); addDevice(); System.out.println("Get all devices.."); getAllDevices(); System.out.println("Delete a device.."); deleteDevice(); System.out.println("Success..Exiting.."); } catch (Exception e) { e.printStackTrace(); System.exit(-1); } }

Файл свойств с образцами значений или со значениями для создания экземпляра APIClient показан в листинге Листинг 6. Вам нужно заменить эти образцы значений ("заполнители") своими значениями идентификатора App ID, идентификатора Org ID, созданного ключа API Key и токена авторизации.

Листинг 6. Содержимое файла application.prop
 id=<App_ID> Organization-ID=<Your_org_ID> Authentication-Method=apikey API-Key=<API key> Authentication-Token=<Auth_token> Enable-Shared-Subscription=true Sample API call getAllDevices() method:

Следующий пример кода в листинге Листинг 7 демонстрирует, как извлечь все устройства в организации с помощью Java Client Library.

Листинг 7. Извлечение всех устройств в организации с помощью Java Client Library
 /** * В данном примере демонстрируется извлечение всех устройств в организации с помощью Java Client Library. * @throws IoTFCReSTException */ private void getAllDevices() throws IoTFCReSTException { // Get all the devices of type SampleDT try { /** * Клиентская Java-библиотека ibmiotf предоставляет один аргумент конструктора, * с помощью которого можно управлять выводом, например, разрешать попытку извлечения * устройств в сортированном порядке на основе идентификатора устройства. */ ArrayList<NameValuePair> parameters = new ArrayList<NameValuePair>(); parameters.add(new BasicNameValuePair("_sort","deviceId")); JsonObject response = this.apiClient.retrieveDevices(DEVICE_TYPE, parameters); // Ответ будет содержать больше параметров, чем будет использовано для выпуска //следующего запроса. Результирующий элемент будет содержать текущий список устройств JsonArray devices = response.get("results").getAsJsonArray(); for(Iterator<JsonElement> iterator = devices.iterator(); iterator.hasNext(); ) { JsonElement deviceElement = iterator.next(); JsonObject responseJson = deviceElement.getAsJsonObject(); System.out.println(responseJson); } } catch(IoTFCReSTException e) { System.out.println("HttpCode :" + e.getHttpCode() +" ErrorMessage :: "+ e.getMessage()); // Печатать, если имеет место частичный отклик System.out.println(e.getResponse()); } }

Чтение шифрованных сообщений с помощью интерфейса REST API сервиса IBM Watson IoT Platform

API-интерфейс сервиса IBM Watson IoT Platform также можно использовать для чтения ретроспективных данных из хранилища сообщений Watson IoT Platform. Устройства способны шифровать полезную нагрузку и добавлять дополнительные поля контрольной суммы, чтобы защитить данные. На рис. Рисунок 1 показан поток для этого сценария.

Рисунок 1. Поток API-интерфейса сервиса IBM Watson IoT Platform

Как показано выше, устройство шифрует относящуюся к данным часть полезной нагрузки и отсылает ее в виде JSON-элемента. Кроме того, устройство генерирует контрольную сумму относящейся к данным части (до шифрования и кодирования) и отсылает ее в виде другого JSON-элемента. Приложение использует API-интерфейсы IBM Watson IoT Platform для извлечения сообщения из Watson IoT Platform, дешифрует и декодирует относящуюся к данным часть, а затем, наконец, выполняет проверку контрольной суммы, сравнивая сгенерированную контрольную сумму и контрольную сумму, сохраненную в сообщении.

В следующих разделах описывается порядок реализации этого сценария.

Код устройства: Шифрование полезной нагрузки и добавление поля контрольной суммы к сообщению

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

 JSONObject message = new JSONObject(); JSONObject innerObj = new JSONObject(); try { innerObj.put("evt", "Test"); innerObj.put("fld", "This is a sensitive data"); String checkSum = IOTSecurityUtil.getMD5(innerObj.toString()); message.put("chksum", checkSum); message.put("ts", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date())); message.put("d", IOTSecurityUtil.encryptEncodeString(innerObj.toString(), strKey, uniqueParam)); } catch (JSONException e1) { e1.printStackTrace(); } handler.publish("iot-2/evt/" + "eid" + "/fmt/json", message.toString(), true, 1);

В листинге Листинг 8 показан пример программного кода для генерации контрольной суммы.

Листинг 8. Пример кода для генерации контрольной суммы
 public static String getMD5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] messageDigest = md.digest(input.getBytes()); BigInteger number = new BigInteger(1, messageDigest); String hashtext = number.toString(16); return hashtext; } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }

Код приложения: Чтение сообщения, дешифрование сообщения и проверка контрольной суммы

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

 /** * Метод получения последнего ретроспективного события и его обработки */ private void getAllHistoricalEventsByDeviceID() { // Получить список ретроспективных событий try { //Получить список ретроспективных событий по типу устройства и по идентификатору устройства JsonElement response = this.apiClient.getHistoricalEvents( DEVICE_TYPE, DEVICE_ID_TEST); JsonObject events = response.getAsJsonObject(); JsonArray eventArray = events.getAsJsonArray("events"); //Get the latest event JsonElement currentEvent = eventArray.get(0); JsonObject responseJson = currentEvent.getAsJsonObject(); System.out.println("Most recent event - " + responseJson.toString()); JsonObject evtObject = responseJson.getAsJsonObject("evt"); System.out.println("Complete raw payload -" + evtObject.toString()); String dString = evtObject.get("d").getAsString(); System.out.println("Encrypted data part -" + dString); String processedData = IOTSecurityUtil.decryptDecodeString(dString.getBytes(), strKey, uniqueParam); System.out.println("Data part after decryption and decoding - " + processedData); String generatedChkSum = IOTSecurityUtil.getMD5(processedData); System.out.println("Generated checksum - " + generatedChkSum); String chkSum = evtObject.get("chksum").getAsString(); if(generatedChkSum.equals(chkSum)) System.out.println("Checksum validation successful"); else System.out.println("Checksum validation failed"); } catch (Exception e) { e.printStackTrace(); } }

Интерфейс REST API сервиса IBM Watson IoT Platform предполагает наличие в сообщении JSON-структуры и помещает ее на хранение в базу данных Cloudant. Он не подвергает синтаксическому анализу (и, соответственно, не сохраняет) полностью зашифрованное сообщение, не представленное в формате JSON. Таким образом, шифрованные двоичные данные в полезной нагрузке должны быть закодированы, поскольку сервис Watson IoT Platform попытается интерпретировать их как JSON-данные. Если данные не закодированы, он не представляют собой валидную JSON-строку.

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

Заключение

Эта статья, вторая в цикле из трех частей, посвящена защите данных, передаваемых между IoT-устройствами и брокером. В статье также описывается, как интерфейс REST API сервиса IBM Watson IoT Platform поддерживает дополнительные слои безопасности между уровнем приложений и брокером - он защищает API-интерфейсы, которые используются для управления устройствами и обращения к данным устройств.

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


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Security, Internet of Things
ArticleID=1040075
ArticleTitle=Проектирование и построение защищенных IoT-решений, часть 2: Защита IoT-данных на уровне сети/транспорта
publish-date=11212016