Содержание


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

Защита IoT-приложений

В статье рассматриваются такие темы, как защищенное хранение IoT-данных, предоставление этих хранимых данных через защищенные API-интерфейсы и вызов защищенных API-интерфейсов из мобильных и веб-приложений

Comments

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

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

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

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

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

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

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

Рисунок 1. Подходы к защите веб-приложений и мобильных приложений, имеющих дело с данными IoT-устройств
Approaches to securing web and mobile apps that                     deal with IoT device data
Approaches to securing web and mobile apps that deal with IoT device data

В статье предполагается, что читатель умеет создавать и развертывать Bluemix-приложения. Если вы еще недостаточно знакомы с Bluemix, вы можете проработать учебные пособия серии Bluemix Fundamentals (Основы Bluemix) на сайте developerWorks. Эта статья не содержит пошаговых инструкций по созданию веб-приложения или мобильного приложения; она лишь объясняет, как добавить средства защиты к вашим веб- или мобильным приложениям.

Защита IoT-данных, получаемых приложением

Серверное приложение для демонстрации IoT-безопасности, которое мы создали для этой статьи, подписывается на данные событий Watson IoT и передает полученные данные на хранение в базу данных Cloudant. Для демонстрации IoT-безопасности приложение шифрует некоторые конфиденциальные атрибуты хранящихся данных, когда передает данные на хранение - например, полезную нагрузку, поступившую от IoT-устройств. Полезная нагрузка представлена в формате JSON и хранится в зашифрованном по стандарту AES виде в базе данных Cloudant. Она подвергается дешифрованию в то время, когда производится извлечение авторизованных пользователей. Шифрование/дешифрование осуществляются незаметно для пользователя.

Защищенный доступ к сервису Watson IoT Platform

Когда вы добавляете сервис Internet of Things Platform в среду Bluemix, для вашего приложения генерируются учетные данные доступа (например, такие, как показано ниже); эти учетные данные предоставляют вам право на доступ к IBM Watson IoT Platform.

 { "iotf-service":[ { "name":"your-iotf-service-name", "label":"iotf-service", "tags":[ "internet_of_things", "Internet of Things", "ibm_created", "ibm_dedicated_public" ], "plan":"iotf-service-free", "credentials":{ "iotCredentialsIdentifier":"your-credentials", "mqtt_host":"your-org-name.messaging.internetofthings.ibmcloud.com", "mqtt_u_port":1883, "mqtt_s_port":8883, "base_uri":"https://your-org-name.internetofthings.ibmcloud.com:443/api/v0001", "http_host":" your-org-name.internetofthings.ibmcloud.com", "org":" your-org-name ", "apiKey":"a-your-org-name-your-key", "apiToken":"your-token" } } ] }

Подписка на события и обновления состояния Watson IoT Platform осуществляется достаточно просто, как показано в следующем фрагменте кода:

 var IotfClient = require("ibmiotf").IotfApplication; var iotConfig = { "org" : org, "id" : "iotsecuritydemo", "auth-key" : apiKey, "auth-token" : apiToken }; var iotfClient = new IotfClient(iotConfig); iotfClient.connect(); iotfClient.on("error", function (err) { console.log("IoTF client error: "+JSON.stringify(err, null, 4)); }); iotfClient.on("connect", function () { // Подписка на получение состояния от всех устройств iotfClient.subscribeToDeviceStatus(); // Подписка на получение всех событий от всех устройств iotfClient.subscribeToDeviceEvents(); }); iotfClient.on("deviceEvent", function (deviceType, deviceId, eventType, format, payload) { // Обработка событий от устройств console.log("Device event from:"+deviceType+", "+deviceId+" of event "+eventType); insertEventToDB(deviceType, deviceId, eventType, format, payload); }); iotfClient.on("deviceStatus", function (deviceType, deviceId, payload, topic) { // Обработка обновлений состояния от устройств console.log("Device status from:"+deviceType+", "+deviceId); insertStatusToDB(deviceType, deviceId, payload, topic); });

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

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

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

Шифрование/дешифрование с целью сохранения и извлечения данных может базироваться на стандартном для отрасли алгоритме AES-256, как показано в следующем фрагменте кода:

 var crypto = require('crypto'); var algorithm = 'aes-256-ctr'; var cryptoKey = 'your-key'; function encrypt(text){ var cipher = crypto.createCipher(algorithm, cryptoKey); var crypted = cipher.update(text,'utf8','hex'); crypted += cipher.final('hex'); return crypted; } function decrypt(text){ var decipher = crypto.createDecipher(algorithm, cryptoKey); var dec = decipher.update(text,'hex','utf8'); dec += decipher.final('utf8'); return dec; }

Шифрование данных снижает производительность, особенно если необходимо манипулировать большими объемами данных; этот аспект необходимо учитывать при выборе и реализации надлежащего подхода.

Безопасное обращение к данным, хранящимся в базе данных Cloudant

К IoT-данным, хранящимся в базе данных Cloudant, можно напрямую обратиться одним из двух способов:

  • Через стороннее приложение, которому предоставлен ваш ключ Cloudant API Key
  • Через разработанные самостоятельно API-интерфейсы, защищенные с помощью сервиса безопасности Bluemix

Доступ третьей стороны с помощью ключа Cloudant API Key

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

 { "cloudantNoSQLDB": [ { "name": "Your name", "label": "cloudantNoSQLDB", "plan": "Shared", "credentials": { "username": "Your user id", "password": "Your password", "host": "Your host id", "port": 443, "url": "https://username:password@.cloudant.com" } } ] }

Эти данные (имя пользователя и пароль) можно использовать для генерации ключей Cloudant API Key следующим образом:

POST _api/v2/api_keys

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

POST https://<username:password>.cloudant.com/_api/v2/api_keys

Ответ будет выглядеть примерно следующим образом:

 "password": "YPNCaIX1sJRX5upaL3eqvTfi", "ok": true, "key": "blentfortedsionstrindigl"

Ключу API Key нужно предоставить разрешение на обращение к определенной базе данных. Для обращения к своей базе данных используйте запрос PUT, как показано ниже.

https://<username>.cloudant.com/_api/v2/db/<database>/_security),

Обратите внимание, что ключ API Key также можно сгенерировать на инструментальной панели Cloudant, как показано на следующем рисунке.

Cloudant dashboard                     where you can generate an API key
Cloudant dashboard where you can generate an API key

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

Ответ API-интерфейса содержит следующие свойства:

  • ok - Возвращается, если запрос был успешен.
  • error - Возвращается, если запрос завершился неудачей; содержит код ошибки.
  • key - Возвращает ключ API Key.
  • password - Возвращает пароль API.

Доступ третьей стороны с помощью специальных API-интерфейсов

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

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

http://<app_route>/iotf/devices

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

iotsecuritydemo.mybluemix.net/iotf/devices

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

http://<app_route>/iotf/devices/<device>

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

iotsecuritydemo.mybluemix.net/iotf/devices/j.patra

Наконец, вы можете запросить конкретное количество событий, указав в запросе дополнительный параметр:

http://<app_route>/iotf/devices/<device>?count=<count>

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

iotsecuritydemo.mybluemix.net/iotf/devices/j.patra?count=5

Защита API-интерфейсов с помощь сервиса Mobile Client Access

После того, как данные защищены и выставлены с помощью специальных API-интерфейсов, следующий шаг состоит в том, чтобы защитить эти API-интерфейсы с помощью аутентификации и авторизации. Чтобы вызвать эти API-интерфейсы, веб-приложения и мобильные приложения должны предоставить необходимые аутентификационные учетные данные; они смогут обращаться к данным лишь тех устройств, на обращение к которым они авторизированны. Для защиты специальных API-интерфейсов в этой статье используется сервис Mobile Client Access на платформе IBM Bluemix.

Специальные API-интерфейсы, используемые в этой статье, созданы с помощью среды Node.js. Чтобы защитить конечную точку API-интерфейса, метод API handler в Node.js дополняется связующим программным обеспечением для аутентификации с паспорта. Связующее программное обеспечение для аутентификации паспорта - это экземпляр паспорта Node.js, который был инициализирован с помощью объекта стратегии Mobile Client Access таким образом, чтобы запрос на аутентификацию перехватывался и обрабатывался SDK сервера Mobile Client Access.

Реализация защиты API-интерфейса

Следующий фрагмент кода из серверного приложения для демонстрации IoT-безопасности показывает, как использовать сервис Mobile Client Access со связующим программным обеспечением аутентификации паспорта для защиты конечной точки специального API-интерфейса (/iotf/devices).

 // --------- Защита бэкенда мобильного приложения с помощью сервиса Mobile Client Access ----------- // Загрузка паспорта (http://passportjs.org) var passportMCA = require('passport'); // Получение стратегии паспорта MCA для использования var MCABackendStrategy = require('bms-mca-token-validation-strategy').MCABackendStrategy; // Указание паспорту использовать стратегию MCA passportMCA.use(new MCABackendStrategy()); // Указание приложению использовать паспорт app.use(passportMCA.initialize()); // Функции сериализации-десериализации сохраняют объект user в сеансе passportMCA.serializeUser(function(user, done) { done(null, user); }); passportMCA.deserializeUser(function(obj, done) { done(null, obj); }); var iotRouteBase = '/iotf'; // Это защищенный API-интерфейс app.get(iotRouteBase + '/devices', passportMCA.authenticate('mca-backend-strategy', {session: true}), function(req, res) { // Проверка userGroup и отфильтровывание устройств, к которым пользователь не имеет доступа executeIfAllowed(req.user, '', 'reader', function(error, userGroup) { if(error) { res.status(400).json(error); } else { getDeviceList(req.user, userGroup, function(error, result) { if(error) { res.status(400).json(error); } else { res.status(200).send(result); } }); } }); } );

Объекты user и userGroup - это специальные объекты аутентификации и авторизации, которые предоставляют основанный на ролях защищенный доступ к данным, хранящимся в базе данных Cloudant. В этой статье для использования этих объектов user и userGroup применяется специальный провайдер аутентификации.

Реализация специального провайдера идентификации

При использовании специальных API-интерфейсов вам может потребоваться более высокая степень контроля над авторизацией пользователя на обращение к данным устройства и отправку команд устройству. Чтобы получить такую степень контроля, создайте в Cloudant набор специальных документов для user и userGroup.

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

Пример специального документа user:

 { "_id": "joypatra@gmail.com", "recordType": "user", "userName": "joypatra", "password": "encrypted-password", "displayName": "Joy Patra", "attributes": { "Language": "English", "Country": "India", "emailAddress": "joypatra@gmail.com", "userGroup": "groupAdmin" } }

Пример специального документа userGroup:

 { "_id": "groupViewers", "recordType": "userGroup", "roleAdmin": "false", "roleReader": "true", "roleWriter": "false", "displayName": "Device administrator group", "devices": ["j.patra", "m.patra"] }

Специальный провайдер идентификации, созданный в этой статье, использует вышеупомянутые документы user и userGroup из базы данных Cloudant. Он исполняется на бэкенд-сервере Node.js в среде IBM Bluemix и предоставляет два интерфейса типа RESTful API, которые требуются сервису Mobile Client Access для аутентификации пользователей. Сервис Mobile Client Access предназначен для вызова этих двух API-интерфейсов в любом специальном провайдере аутентификации.

В сервисе Mobile Client Access заранее определены следующие два API-интерфейса: startAuthorization и handleChallengeAnswer. Сервис Mobile Client Access исходит из предположения, что эти два интерфейса типа RESTful API представлены в следующем формате:

POST <base_url>/apps/<tenant_id>/<realm_name>/<request_type>

КодОписание
base_url Задает базовый URL-адрес веб-приложения специального провайдера идентификации. Базовый URL-адрес - это адрес, подлежащий регистрации в инструментальной панели Mobile Client Access.
tenant_id Задает уникальный идентификатор арендатора. Когда сервис Mobile Client Access вызывает этот API-интерфейс, он всегда предоставляет идентификатор GUID приложения IBM Bluemix.
realm_name Задает имя специальной области (realm), которое определено в инструментальной панели Mobile Client Access.
request_type Задает один из следующих типов запроса:
  • startAuthorization: Задает первый шаг процесса аутентификации. Специальный провайдер идентификации должен в ответ сообщить текущее состояние: "challenge", "success" или "failure".
  • handleChallengeAnswer: Обрабатывает ответ от мобильного клиента на аутентификационный запрос.
Для получения дополнительной информации об этих типах запроса и о формате данных обратитесь к документации по IBM Bluemix.

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

 app.post('/apps/:tenantId/:realmName/startAuthorization', jsonParser, function(req, res){ var tenantId = req.params.tenantId; var realmName = req.params.realmName; var headers = req.body.headers; var response = { status: "failure" }; var validTenant = isValidTenant(tenantId); var validRealm = isValidRealm(realmName); if(validTenant && validRealm) { response = { status: "challenge", challenge: { text: "Enter username and password" } }; console.log("Responding with challenge."); res.status(200).json(response); } else { response.failure = { text: "Wrong tenant id or realm name." }; console.log("Responding with failure."); res.status(400).json(response); } }); app.post('/apps/:tenantId/:realmName/handleChallengeAnswer', jsonParser, function(req, res){ var tenantId = req.params.tenantId; var realmName = req.params.realmName; var challengeAnswer = req.body.challengeAnswer; var username = req.body.challengeAnswer["username"]; var password = req.body.challengeAnswer["password"]; var response = { status: "failure" }; var validTenant = isValidTenant(tenantId); var validRealm = isValidRealm(realmName); if(validTenant && validRealm) { // Retrieve custom user document from Cloudant db db.get(username, function(error, userIdentity) { var response = { status: "failure" }; if(! error) { if(encrypt(password) == userIdentity.password) { response.status = "success"; response.userIdentity = userIdentity; } else error = new Error("Username or password does not exist or does not match."); } response.failure = error; if(response.status == "success") { console.log("Responding with success."); res.status(200).json(response); } else { console.log("Responding with failure."); res.status(400).json(response); } }); } else { response.failure = { text: "Wrong tenant id or realm name." }; console.log("Responding with failure."); res.status(400).json(response); } });

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

 { status: "challenge", challenge: { message: "Enter username and password", customField_retriesLeft: 2, customField_minUsernameLength: 8 } }

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

 { username: "joypatra@gmail.com", password: "your-password", otherCustomField: "1234" }

Специальный провайдер идентификации, который в этой статье реализован на серверной стороне, использует для проверки учетных данных, содержащихся в ответе, специальные документы user и userGroup в базе данных Cloudant.

Если проверка пользователя завершилась успехом, специальный провайдер идентификации на серверной стороне возвращает документ user из базы данных Cloudant в сервис Mobile Client Access. Сервис Mobile Client Access создает объект session user, копируя определенные атрибуты из документа user. В следующем фрагменте кода показан пример объекта user, создаваемый сервисом Mobile Client Access.

 { "id": "joypatra", "authBy": "iotsecuritydemoRealm", "displayName": "Joy Patra", "attributes": { "userGroup":"groupAdmin", "emailAddress":"joypatra@gmail.com", "Language":"English", "Country":"India" } }

Кроме того, сервис Mobile Client Access копирует атрибут attributes "как есть" из документа user в объект session user. Таким образом, вы можете использовать атрибут attributes для включения всех специальных атрибутов, специфических для приложения.

Обращение к защищенным API-интерфейсам

Клиентское приложение может обращаться к описываемым специальным API-интерфейсам одним из двух способов:

  • Стратегия интеграции бэкенд - бэкенд
  • Стратегия интеграции на базе API-интерфейса

Эти два подхода сравниваются в таблице Таблица 1.

Таблица 1. Два подхода для обращения клиентских приложений к защищенным специальным API-интерфейсам
Стратегия интеграции бэкенд - бэкенд Стратегия интеграции на базе API-интерфейса
Чтобы взаимодействовать с фронтенд-приложением, целевому бэкенд-приложению нужно совместно с ним использовать значение секретного ключа. Все запросы из фронтенд-приложения, имеющие этот секретный ключ в заголовке запроса, принимаются API-интерфейсами бэкенд-приложения без дальнейшей аутентификации. "Секрет" не обязан быть известен фронтенд-приложению. Фронтенд-приложение передает учетные данные пользователя в API-интерфейс бэкенд-приложения, чтобы это бэкенд-приложение смогло аутентифицировать и авторизовать его.
Ответственность за аутентификацию и авторизацию конкретного пользователя лежит на фронтенд-приложении. Если фронтенд передает корректное "секретное" значение, то бэкенд попросту доверяет фронтенду. Фронтенд-приложение не осуществляет аутентификацию или авторизацию.
Интеграция весьма проста. При использовании специального провайдера аутентификации Mobile Client Access фронтенд-приложению нужно реализовать специального слушателя аутентификации для обработки запросов Mobile Client Access.
На момент написания этой статьи не существовало никакого явного метода "выхода из системы". На момент написания этой статьи не существовало никакого явного метода "выхода из системы".

Реализация стратегии интеграции бэкенд - бэкенд

В стратегии интеграции бэкенд - бэкенд для получения необходимого доступа к данным учетные данные безопасности (поле секрета) передаются в авторизационных заголовках http-запроса. Пример кода для вызова API-интерфейса показан ниже.

 var oauthSDK = require('bms-mca-oauth-sdk'); var request = require('request'); var mca_options = { cacheSize: 100, appId: "Your app id", clientId: "Your client id", secret: "Secret", serverUrl: "https://imf-authserver.ng.bluemix.net/imf-authserver" }; app.get('/devices', ensureAuthenticated, function(req, res) { var user_email = req.user._json.emailAddress; console.log("req.user - " + user_email); oauthSDK.getAuthorizationHeader(mca_options).then(function(authHeader){ req.headers.Authorization = authHeader; request({ url: 'iotsecuritydemo.mybluemix.net/iotf/devices', //URL to hit method: 'GET', //Specify the method, a POST is recommended headers: { //We can define headers too 'Authorization': authHeader } }, function(error, response, body){ if(error) { console.log(error); } else { var html = ""; html += "<p>IOTF Devices:</p>" html += "<pre>" + body + "</pre>"; res.send(html); } }); }); });

Реализация стратегии интеграции на базе API-интерфейса

В стратегии интеграции на базе API-интерфейса фронтенд-приложение получает доступ к бэкенду только при предоставлении требуемых учетных данных, уникальных для пользователя. Чтобы осуществить аутентификацию в Facebook или в Google, SDK клиентской стороны Mobile Client Access обрабатывает взаимодействие с библиотекой аутентификации Facebook или Google во фронтенде. Для работы со специальным провайдером аутентификации фронтенд-приложению нужна реализация слушателя аутентификации.

Например, SDK клиента Mobile Client Access для Cordova можно инициализировать следующим образом:

BMSClient.initialize(applicationRoute, applicationGUID);

Атрибуты applicationRoute и applicationGUID - это атрибуты Bluemix Application Route и Bluemix Application GUID в бэкенд-приложении, хостинг которого осуществляет платформа Bluemix.

Воспользуйтесь примером реализацию специального слушателя аутентификации (в Cordova), приведенным в документации сервиса Bluemix Mobile Client Access.

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

BMSClient.registerAuthenticationListener(realmName, customAuthenticationListener);

Атрибут realmName - это имя, которое вы задали при конфигурировании специального провайдера аутентификации в инструментальной панели Mobile Client Access в Bluemix.

Защита клиентских IoT-приложений

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

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

Вариант 1. Раздельная аутентификация для веб-приложений и мобильных приложений

Если применяется подход с раздельной аутентификацией, то веб-приложение использует для специальной аутентификации сервис Bluemix SSO (на основе Cloud Directory), а мобильное приложение использует для аутентификации пользователей сервис MobileFirst Client Access (на основе специального провайдера аутентификации). Однако и веб-приложения, и мобильные приложения используют один и тот же слой защиты приложений для авторизации доступа пользователя к данным устройства. См. Рисунок 2.

Рисунок 2. Раздельная аутентификация, один и тот же слой защиты приложений
Separate authentications, same secured application
Separate authentications, same secured application

Вариант 2. Унифицированная аутентификация для мобильных приложений и веб-приложений

Рисунок 3 иллюстрирует подход с унифицированной аутентификацией и авторизацией на основе сервиса Mobile Client Access. Если применяется подход с унифицированной аутентификацией, то и мобильные приложения, и веб-приложения используют один и того же специальный провайдер аутентификации и авторизации на базе Cloudant, чтобы защитить данные устройств и ограничить круг источников, способных направлять команды устройству.

Рисунок 3. Одинаковая аутентификация, одинаковая защита приложений
Same authentication, same secured application
Same authentication, same secured application

Защита веб-приложения

В нашем веб-приложении для демонстрации SSO и авторизации в IoT мы используем IBM Cloud Directory для поддержки специальной аутентификации для веб-приложения, как описано в параграфе Вариант 1. Раздельная аутентификация для веб-приложений и мобильных приложений. При использовании этого варианта наш пример веб-приложения должен поддерживать требуемые учетные данные (отображенные на учетные данные пользователя для входа в систему), чтобы вызвать API-интерфейс бэкенда. (Если вы хотите избежать этой конфигурации и использовать одни и те же учетные данные и для мобильных приложений, и для веб-приложений, используйте Вариант 2. Унифицированная аутентификация для мобильных приложений и веб-приложений.)

Аутентификация с помощью IBM Cloud Directory

Веб-приложения, предоставляющие доступ к данным IoT-устройства, должны быть защищены с помощью комбинации "имя пользователя / пароль" для пользователей приложения. В нашем веб-приложении для хранения всей информации о пользователях применяется бэкенд-реестр пользователей (а именно IBM Cloud Directory). Дополнительно в приложении реализуется функциональность Single Sign On (SSO) с помощью сервиса Bluemix SSO.

Сервис SSO поддерживает три источника идентификационных данных, способных хранить учетные данные пользователей:

  • SAML Enterprise: Реестр пользователей, применяющий обмен SAML-токенами для выполнения аутентификации.
  • Cloud Directory: Реестр пользователей, размещаемый в среде IBM Cloud. Он используется в примерах для этой статьи.
  • Источники идентификационных данных в социальных сетях: Реестры пользователей, сопровождаемые Google, Facebook и LinkedIn.

Прежде чем разработчик приложения сможет встроить возможности SSO в свое приложение, администратор должен создать экземпляры соответствующего сервиса и добавить источники идентификационных данных. Выполните следующие шаги, чтобы реализовать в веб-приложении возможности SSO (с помощью IBM Cloud Directory).

  1. Настройте сервис SSO в среде Bluemix и создайте новый каталог IBM Cloud Directory. Создайте список пользователей в CSV-файле и загрузите его в этот каталог. См. следующий рисунок: SSO screen shot
    SSO screen shot
  2. Используйте пакет пакет Node.js passport вместе с passport-idaas-openidconnect для вызова сервиса SSO из приложения Node.js, развернутого в среде Bluemix.
     var express = require('express'); var cookieParser = require('cookie-parser'); var session = require('express-session'); var passport = require('passport'); var app = express(); app.use(logger('dev')); app.use(cookieParser()); app.use(session({ secret : 'iotfCloud123456789', saveUninitialized : true, resave : true })); app.use(passport.initialize()); app.use(passport.session()); passport.serializeUser(function(user, done) { done(null, user); }); passport.deserializeUser(function(obj, done) { done(null, obj); }); var services = JSON.parse(process.env.VCAP_SERVICES || "{}"); var ssoConfig = services.SingleSignOn[0]; var client_id = ssoConfig.credentials.clientId; var client_secret = ssoConfig.credentials.secret; var authorization_url = ssoConfig.credentials.authorizationEndpointUrl; var token_url = ssoConfig.credentials.tokenEndpointUrl; var issuer_id = ssoConfig.credentials.issuerIdentifier; var callback_url = "https://IOTSSOAyan.mybluemix.net/auth/sso/callback"; var OpenIDConnectStrategy = require('passport-idaas-openidconnect').IDaaSOIDCStrategy; var Strategy = new OpenIDConnectStrategy({ authorizationURL : authorization_url, tokenURL : token_url, clientID : client_id, scope : 'openid', response_type : 'code', clientSecret : client_secret, callbackURL : callback_url, skipUserProfile : 'false', issuer : issuer_id }, function(iss, sub, profile, accessToken, refreshToken, params, done) { process.nextTick(function() { profile.accessToken = accessToken; profile.refreshToken = refreshToken; done(null, profile); }) });
  3. Воспользуйтесь функцией ensureAuthenticated(), чтобы в каждом вызове проверять вход в систему.
     function ensureAuthenticated(req, res, next) { if (!req.isAuthenticated()) { req.session.originalUrl = req.originalUrl; res.redirect('/login'); } else { return next(); } }

Защита мобильного приложения

В дополнение к средствам защиты веб-приложений платформа IBM Bluemix предоставляет сервисы, гарантирующие безопасность мобильных приложений. Важнейшим среди них является сервис Mobile Client Access, который управляет доступом к приложению со стороны мобильного устройства.

На следующей схеме показан поток для защищенного мобильного приложения, использующего сервис Mobile Client Access при обращении к IoT-данным (для сценария, в котором проверка учетных данных пользователя осуществляется с помощью специальной базы данных Cloudant). Под рисунком приведено описание каждого шага на этой схеме.

Picture 5
Picture 5
  1. Мобильное устройство использует SDK клиентской стороны Mobile Client Access для инициализации сеанса с бэкендом мобильного приложения путем предоставления атрибутов Bluemix Application Route и Bluemix Application GUID бэкенда мобильного приложения.
  2. Мобильное устройство вызывает защищенную конечную точку на бэкенде мобильного приложения (/iotf/devices, в нашем серверном приложении для демонстрации защиты). Защита конечной точки на серверной стороне осуществляется путем вставки аутентификации на основе Node.js passport, которая использует стратегию Mobile Client Access из SDK серверной стороны Mobile Client Access.
  3. SDK серверной стороны Mobile Client Access перехватывает запрос на конечной точке и проверяет, существует ли токен OAuth Mobile Client Access для этого сеанса.
  4. Если этот токен уже существует, SDK серверной стороны Mobile Client Access разрешает вызов обработчика (handler) конечной точки. В противном случае он возвращает ответ с ошибкой HTTP 401.
  5. SDK клиентской стороны Mobile Client Access знает, как перехватить ответ HTTP 401, возвращенный SDK серверной стороны Mobile Client Access, благодаря чему этот SDK клиентской стороны переключает поток аутентификации.
  6. Если сервис Mobile Client Access был сконфигурирован с Facebook, с Google или со специальным провайдером аутентификации, то поток аутентификации принимает это во внимание. Он возвращает результат в SDK серверной стороны Mobile Client Access.
  7. Когда поток аутентификации успешно добирается для SDK серверной стороны Mobile Client Access, он вызывает обработчика этой защищенной конечной точки (/iotf/devices, в нашем серверном приложении для демонстрации защиты).

Инициализация SDK клиента Mobile Client Access (для Android)

Вам нужно инициализировать этот SDK путем передачи контекста, идентификатора GUID приложения и параметров маршрута приложения в метод initialize() (см. фрагмент кода ниже).

Значения Application Route и Application GUID можно найти в разделе Mobile Options в верхней части инструментальной панели Bluemix-приложения.

 try { // Инициализация SDK с помощью идентификатора и маршрута приложения IBM Bluemix // // Значения Application Route и Application GUID можно найти в разделе // Mobile Options в верхней части инструментальной панели Bluemix-приложения BMSClient.getInstance().initialize( getApplicationContext(), DeviceIoTDemoApplication.APPLICATION_ROUTE, DeviceIoTDemoApplication.APPLICATION_ID); } catch (MalformedURLException e) { Log.e(TAG, "Error initializing Bluemix Mobile Service Client: "+e); }

Обращение с запросами к защищенным API-интерфейсам

После того как клиентский SDK Mobile Client Access инициализирован, вы можете обращаться с запросами к защищенным API-интерфейсам:

http://<app-route>/iotf/devices

Наш пример Android-приложения снабжен клиентским SDK Mobile Client Access. Чтобы обратиться с запросом к вышеупомянутой конечной точке из мобильного приложения, добавьте следующий код после того, как вы инициализировали экземпляр BMSClient:

 Request request = new Request( BMSClient.getInstance().getBluemixAppRoute()+"/iotf/devices", Request.GET); // BMSResponseListener - это специальный класс, который реализовал ResponseListener request.send(OAuthLoginActivity.this, new BMSResponseListener() { @Override public void onSuccess(final Response response) { super.onSuccess(response); runOnUiThread( new Runnable() { @Override public void run() { Log.i(TAG, "Success Response=" + response.getResponseText()); // Исполнение задач, относящихся в интерфейсу пользователя вашего Android-приложения } } ); } });

Пример Android-приложения мобильного клиента

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

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

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

В Android-приложении реализован специальный слушатель аутентификации. В этой статье мобильное приложение получает имя пользователя и пароль, а затем создает экземпляр класса CustomAuthenticationListener с этими именем пользователя и паролем. В вашей реализации вы, вероятно, захотите получать имя пользователя и пароль после прихода аутентификационного запроса со стороны SDK клиентской стороны Mobile Client Access (мы отметили это место комментариями в следующем коде).

Кроме того, вам нужно перехватывать исключения, которые инициируются в методе onAuthenticationChallengeReceived(), и сообщать в AuthenticationContext о неудачах. Если вы не перехватите эти исключения, SDK клиентской стороны Mobile Client Access навсегда останется в состоянии "waiting-for-credentials" (ожидание учетных данных), что отмечено в комментариях в следующем коде.

 public class CustomAuthenticationListener implements AuthenticationListener { private final String TAG = getClass().getSimpleName(); private Context context; private String username = ""; private String password = ""; public CustomAuthenticationListener(Context context, String username, String password) { this.context = context; this.username = username; this.password = password; } @Override public void onAuthenticationChallengeReceived ( AuthenticationContext authContext, JSONObject challenge, Context context) { // В этом месте разработчик будет показывать экран входа в систему, собирать учетные данные и вызывать API-интерфейс // authContext.submitAuthenticationChallengeAnswer() JSONObject challengeResponse = new JSONObject(); try { challengeResponse.put("username", username); challengeResponse.put("password", password); authContext.submitAuthenticationChallengeAnswer( challengeResponse); } catch (JSONException e){ // В случае если имела место неудача при сборе учетных данных, вам необходимо сообщить об этом в AuthenticationContext. // В противном случае SDK клиента Mobile Client Access навсегда останется в состоянии waiting-for-credentials authContext.submitAuthenticationFailure(null); } }

Сканирование и мониторинг приложений на наличие уязвимостей в защите

Платформа IBM Bluemix также предоставляет сервисы, которые помогут вам сканировать свои приложения или осуществлять их мониторинг. Например, сервис IBM Application Security on Cloud помогает гарантировать отсутствие брешей в защите приложения. Разработчики могут использовать эти сервисы, чтобы до развертывания просканировать свои веб-приложения и мобильные приложения на наличие уязвимостей, выявить проблемы с безопасностью и установить рекомендованные исправления. Кроме того, сервис Mobile Client Access имеет инструментальную панель, которая обеспечивает мониторинг и анализ использования мобильного приложения и событий безопасности в устройстве (таким как успешные и неудачные аутентификации).

Заключение

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

Благодарности

Благодарим Субрата Саха (Subrata Saha), архитектора по Bluemix-решениям, за рецензирование этой статьи и за существенные предложения по ее совершенствованию.


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


Похожие темы


Комментарии

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

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