目次


セキュアな IoT ソリューションの設計と構築, 第 2 回

ネットワーク上の IoT データをセキュリティーで保護する

データ暗号化および IBM Watson IoT Platform の API における API セキュリティー

Comments

コンテンツシリーズ

このコンテンツは全3シリーズのパート#です: セキュアな IoT ソリューションの設計と構築, 第 2 回

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:セキュアな IoT ソリューションの設計と構築, 第 2 回

このシリーズの続きに乞うご期待。

モノのインターネット (IoT) ソリューションは、データを収集してネットワークやクラウド上で交換するデバイスとセンサーからなる複雑なネットワークです。より多くのアプリケーションにより多くのデータが公開されるようになることから、IoT 開発者にとってはセキュリティーが極めて大きな課題となります。

全 3 回からなるシリーズでは、IoT アプリケーションのアーキテクチャーを構成するデバイス層、データ層、アプリケーション層という 3 つの層のそれぞれにおけるセキュリティーに注目します。シリーズの第 1 回では、IoT セキュリティーの基本を紹介し、デバイスやゲートウェイをセキュリティーで保護するための各種の手法を説明しています。第 2 回 (この記事) では IBM Watson IoT Platform を含め、ネットワークとトランスポート層のセキュリティーの側面に目を向けます。第 3 回では、アプリケーション層のセキュリティー要件を説明し、IBM Bluemix プラットフォーム内で作成するアナリティクス IoT アプリケーションの実装手法を紹介します。

ネットワーク上のデータをセキュリティーで保護する

トランスポート層セキュリティー (TLS) は、ネットワーク上のデータ転送をセキュリティーで保護するためのものです。TLS では、データの内容を誰も傍受したり理解したりできないよう、データが暗号化されます。TLS (別名 SSL) は、多数の Web サイトへのセキュアなアクセスを可能にするために広く利用されています。TLS により、データ転送が行われる前にサーバーとクライアントの間で必ず信頼が確立されるようになります。それは、TLS ではクライアントによるサーバー証明書の検証が必要となるためです。場合によっては、サーバーもクライアント固有の証明書を検証します。

MQTT は転送プロトコルとして TCP を頼りにするため、デフォルトの接続では通信は暗号化されません。TLS を実装すると、データ転送のパフォーマンスや、サーバーに対する負荷に影響を及ぼすものの、ほとんどの MQTT ブローカー (IBM Watson IoT Platform を含む) では TLS の使用をサポートしているため、アプリケーションで TLS を使用して機密データの交換をセキュリティーで保護できるようになっています。Watson IoT Platform では MQTT を使用することで、デバイスとゲートウェイのデフォルトの接続セキュリティー設定として TLS を使用します。

一方、セキュリティーをさらに強化するには、IoT アプリケーションにデータを暗号化して送受信させるようにするという方法があります。例えば、IoT アプリケーションでデータを暗号化してから送信するには、MQTT PUBLISH を使用できます。このような実装は、信頼されていない環境や、デバイスと MQTT ブローカーの間で非セキュアなネットワーク接続が行われる場合には特に重要です。

MQTT CONNECT パケットの認証、許可用の username および password フィールドを使用する場合は、TLS の使用をぜひ検討してください。セキュアな MQTT 接続用にポート 8883 が標準化されています。

データの暗号化

大半の MQTT デプロイメントではトランスポート層セキュリティー (TLS) が使用されます。つまり、データは暗号化されて、その整合性が検証されるということです。IBM Watson IoT Platform では、ネットワーク上で転送されるデータをセキュリティーで保護するために、TLS 1.2 をサポートしています。この記事では、基礎となるネットワークが TLS をサポートしているかどうかに関わらず、アプリケーションが暗号化メッセージの交換をサポートすることで、セキュリティー・メカニズムを追加できる仕組みを説明します。

メッセージ内で暗号化する必要があるのは、ペイロード・データ (非公開のセンサー情報) の部分だけです。MQTT PUBLISH メッセージのメッセージ・フィールドは変更されません。ペイロード情報はバイナリーなので、メッセージ送信中の特殊なエンコーディング・メカニズムは不要です。また、MQTT メッセージ・フォーマットは変わらないことから、ブローカー側でも特定の変更を加える必要はありません。メッセージのペイロードを解釈するアプリケーションだけが、その内容を理解するためにメッセージを暗号解除する必要があります。

TLS はネットワーク層上でセキュリティーを提供する一方、MQTT ペイロード暗号化はアプリケーション層上でセキュリティーを提供するため、この 2 つを同時に利用しても競合は発生しません。MQTT ペイロード暗号化によって解決する問題は、アプリケーションのメッセージを盗聴者や (認証メカニズムが実装されていない場合は) 信頼されていない MQTT クライアントから保護するという問題だけです。TLS に基づくセキュア通信チャネルが確立されていなければ、攻撃者がメッセージを再生したり、メッセージの一部 (トピックなど) を改ざんしたりする可能性は依然として残ります。

TLS は使用できない一方、アプリケーションのデータを平文で送信したくない場合は、MQTT ペイロード暗号化が役立ちます。MQTT ペイロード暗号化を実装すると、アプリケーションのすべてのデータがセキュリティーで保護されるため、セキュリティー層が追加されることになります。ペイロード暗号化の利点と欠点を表 1 にまとめます。TLS の使用が制限されているデバイスには、ペイロード暗号化が最適なセキュリティー手法ですが、暗号化と暗号解除にかなりの処理能力が費やされる可能性があります。したがって、リソースに負担をかけずに高度なセキュリティーを提供するアルゴリズムを使用してください。

表 1. ペイロード暗号化の利点と欠点
利点欠点
  • エンド・ツー・エンドのメッセージ・セキュリティーを提供
  • 機密性の高いデータを送信するアプリケーションに、セキュリティー層を追加
  • TLS を使用できない場合に最適
  • リソースが極めて限られているデバイスには実装できない場合がある。
  • セキュアな通信チャネルが使用されなければ、攻撃者がメッセージを改ざんする可能性が残る。
  • ペイロードが暗号化されている場合、IBM Watson IoT Platform ではメッセージからデータ・ポイントを判断することができない。そのため、Real-Time Insights サービスでダッシュボードを通常の方法内で表示できない。

前述したように、MQTT over TLS を使用すると、CPU 使用率が上昇し、通信が増えるという点で、パフォーマンスに影響が及びます。このパフォーマンス・コストは、ブローカーにはそれほど大きな影響を与えませんが、リソースが限られたデバイスにおいては問題となる可能性があります。以下に、TLS パフォーマンスを向上させる効果があることがテストによって明らかになっている手法をいくつか紹介します。

  • 短時間存続する接続を避けること。
    データ転送ごとに新しい TLS を確立すると、不必要に帯域幅を増やすことになりかねません。短時間の接続ではなく、長時間存続する接続を使用してください。
  • セッション再開手法を取り入れること。
    TLS セッション再開手法を使用すると、クライアントがサーバーに再接続した後、ネゴシエーション済みの TLS セッションを再利用できます。したがって、クライアントとサーバーが完全な TLS ハンドシェイクを繰り返す必要がなくなります。
  • 入手できる最新の TLS バージョンを使用すること。
    TLS 1.0 および 1.1 に対するよく知られた攻撃がすでに何件か発生しています (このリンク先の IETF のメモと、このリンク先の知識ベースで公開されている RedHat Enterprise Linux 5 に関する記事を参照してください)。可能であれば TLS 1.2 を使用してください (Watson IoT Platform では TLS 1.0 のサポートを中止し、このプラットフォームの HTTP または MQTT API を使用するアプリ内での TLS 1.0 の使用を無効にしています)。

メッセージのペイロードを暗号化する

以下のサンプル・コードに、メッセージのペイロード暗号化が説明されています。

このサンプル・コードは、GitHub に保管されている DeviceSimulatorDemo からダウンロードできます。

リスト 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) {
		// Check if client is connected
		if (isMqttConnected()) {
			// Create a new MqttMessage from the message string
			MqttMessage mqttMsg = new MqttMessage(message);
			// Set retained flag
			mqttMsg.setRetained(retained);
			// Set quality of service
			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 {
			// Create key and cipher
			ivspec = new IvParameterSpec(iv.getBytes());
			keyspec = new SecretKeySpec(strKey.getBytes(), "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

			// encrypt the text
			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);
             // Further processing of decrypted message as per your need
       }
}
リスト 4. AES ベースの暗号化解除手法
public static String decryptString(byte[] byteMsg, String strKey, String iv) {
		String strReturn = null;
		IvParameterSpec ivspec = null;
		SecretKeySpec keyspec;
		try {
			// Create key and cipher
			ivspec = new IvParameterSpec(iv.getBytes());
			keyspec = new SecretKeySpec(strKey.getBytes(), "AES");
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			// Decrypt the payload
			cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
			strReturn = new String(cipher.doFinal(byteMsg));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return strReturn;
}

メッセージの整合性をチェックする

メッセージ整合性チェックによって、MQTT メッセージが攻撃者によって改ざんされていないことを確認します。デバイスとブローカー間の信頼されていない通信には、メッセージ整合性チェックを使用する必要があります。

メッセージ整合性をチェックするには、以下の手法を検討できます。

  • チェックサム
    暗号チェックサムは、MQTT メッセージの内容に基づいて計算された数値です。メッセージのペイロードをハッシュ値と呼ばれる固定長の数字文字列に変換する一連の計算が行われて、チェックサムが作成されます。チェックサムまたはハッシュのアルゴリズムの例には、MD5、巡回冗長検査 (CRC)、セキュア・ハッシュ・アルゴリズム (SHA-1 または SHA-2) があります。このチェックサムの値が MQTT メッセージ・ペイロードの先頭に追加されます。そのメッセージを受信したアプリケーションは、チェックサムを再計算して、メッセージの整合性を確認します。
  • メッセージ認証コード (MAC)
    メッセージ認証コード (MAC) とは、メッセージが信頼された送信者から受信したものであること、そして送信中に送信者以外の誰か、または何かによって改ざんされていないことを検証するために使用する情報のことです。MAC アルゴリズムは秘密鍵と認証対象の MQTT メッセージ・ペイロードの両方を入力として取り、MAC を出力します。この出力された MAC を MQTT メッセージに含めて送信しなければなりません。また、アプリケーションは、MAC を生成するために使用されたのと同じ秘密鍵にアクセスできなければなりません。
  • デジタル署名
    デジタル署名とは、MQTT メッセージの内容と送信者のアイデンティティーを検証するためにメッセージに追加できるデジタル・コードのことです。デジタル署名は、公開鍵暗号化によって生成および認証されます。

デジタル署名は最も包括的なメッセージ整合性の検証方式を可能にしますが、パフォーマンスに影響を与えるため、さらにリソースが必要になります。単純なチェックサムは実装するのも検証するのも簡単です。ただし、MAC またはデジタル署名のいずれにしても計算が増えるため、適切なシナリオ中で使用してください。適切なシナリオとは、基礎となるネットワークに信頼性が欠けている場合、あるいは送信するメッセージにさらにセキュリティー対策が必要になる場合などです。

Watson IoT Platform の REST API における API セキュリティー

Watson IoT Platform の REST API には、REST API へのアクセス、暗号化データの読み取り、メッセージ整合性の検証に関する様々なセキュリティー対策が講じられています。

REST API へのアクセス

IBM Watson IoT Platform では、デバイスの管理やデバイスから送信されるデータへのアクセスなどといった特定の機能をサポートするために、REST 風の API を提供しています。この API へのアクセスは、HTTPS と組み合わせた基本認証により、以下のように保護されています。

  • HTTP (ポート 80) ではなく、HTTPS (ポート 443) を使用
  • アプリケーションの API キーをユーザー名として使用
  • 対応する許可トークンをパスワードとして使用

API 呼び出しを認証するために、Watson IoT Platform 内で API キーを生成する必要があります。API キーを生成するには、このリンク先のレシピに記載されているステップバイステップの手順に従ってください。

API キーを生成した後は、その API キーを使用して API クライアント com.ibm.iotf.client.api.APIClient を呼び出し、このクライアントから IBM Watson IoT Platform API を呼び出すことができます。リスト 5 に、どのようにして APIClient インスタンスを作成するのかを示します。このコードに、プロパティー・ファイルから API クライアントをインスタンス化するために必要なプロパティーが記載されています。

リスト 5. APIClient のインスタンス化
public void doApp() {
		// Read properties from the conf file
		Properties props = MqttUtil.readProperties("MyData/application.prop");

		try {
			//Instantiate the class by passing the properties file
			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);
		}

	}

リスト 6 に、APIClient をインスタンス化するためのプロパティー・ファイルを記載します。サンプル値のプレースホルダーは、固有のアプリケーション ID、組織 ID、生成した API キー、および許可トークンで置き換えてください。

リスト 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 クライアント・ライブラリーを使用して、組織内のすべてのデバイスをどのようにして取得するのかを示します。

リスト 7. Java クライアント・ライブラリーを使用して組織内のすべてのデバイスを取得する例
  /**
  * This sample showcases how to retrieve all the devices in an organization using the Java Client Library.
  * @throws IoTFCReSTException
  */

  private void getAllDevices() throws IoTFCReSTException {
		// Get all the devices of type SampleDT
		try {
			/**
			 * The Java ibmiotf client library provides an one argument constructor
			 * which can be used to control the output, for example, lets try to retrieve
			 * the devices in a sorted order based on device ID.
			 */

			ArrayList<NameValuePair> parameters = new ArrayList<NameValuePair>();
			parameters.add(new BasicNameValuePair("_sort","deviceId"));

			JsonObject response = this.apiClient.retrieveDevices(DEVICE_TYPE, parameters);

			// The response will contain more parameters that will be used to issue
			// the next request. The result element will contain the current list of devices
			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());
			// Print if there is a partial response
			System.out.println(e.getResponse());
		}
	}

IBM Watson IoT Platform の REST API を使用して暗号化メッセージを読み取る

IBM Watson IoT Platform API を使用して、Watson IoT Platform メッセージ・ストアから履歴データを読み取ることもできます。データを保護するために、デバイスでペイロードを暗号化して、さらにチェックサムを追加できます。図 1 に、このシナリオのフローを示します。

図 1. IBM Watson IoT Platform API フロー

上の図に示されているように、デバイスはペイロードのデータ部分を暗号化して、そのメッセージを JSON 要素として送信します。また、データ部分のチェックサムを生成し (暗号化とエンコーディングの前)、そのチェックサムを別の JSON 要素として送信します。アプリケーションは IBM Watson IoT Platform API を使用して 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);
        }
    }

アプリケーション・コード: メッセージを読み取って暗号化解除し、チェックサムを検証する

以下のアプリケーション・コードでは、Watson IoT Platform API を使用して永続ストアからメッセージを読み取り、メッセージを暗号化解除してから、チェックサムを検証してメッセージの整合性を確認します。

	/**
	 * Method to get the latest historical event and process it
	 */
	private void getAllHistoricalEventsByDeviceID() {
		// Get the historical events
		try {
			//Get the list of historical events by device type and device id
			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();
		}
	}

IBM Watson IoT Platform REST API はメッセージ内に JSON 構造体があることを期待し、その構造体を Cloudant データベースに維持します。JSON フォーマットになっていない暗号化メッセージを完全に解析することはしません (したがって、保管することもしません)。Watson IoT Platform は JSON の解析を試みるため、ペイロードに含まれるバイナリー暗号化データはエンコードする必要があります。エンコードされていなければ、有効な文字列ではありません。

ベスト・プラクティスは、ペイロードを暗号して、それを JSON 構造体の中に組み込むことです。この場合、JSON メッセージの他のフィールドに、チェックサムとタイム・スタンプを格納できます。

まとめ

この全 3 回からなるシリーズの第 2 回では、IoT デバイスと MQTT ブローカー間でのデータ交換をセキュリティーでどのようにして保護するのかに注目しました。また、IBM Watson IoT Platform REST API がアプリケーション層と MQTT ブローカー間の追加のセキュリティー層をサポートするために、デバイスの管理とデバイス・データへのアクセスに使用する API をセキュリティーで保護する仕組みについても説明しました。

この記事で説明したように、患者レコードや自動車データなどの個人的で機密性の高いデータを扱うアプリケーションには、TLS を使用することを推奨します。基礎となるネットワークが TLS をサポートしていない場合や、追加のセキュリティー層が必要な場合には、デバイス・データがセキュリティーで保護されずに転送されることがないよう、ペイロード暗号化の手法を検討してください。ただし、暗号化やチェックサムのようなセキュリティー・メカニズムを実装すると、パフォーマンスが低下することを念頭に置く必要があります。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Internet of Things, セキュリティ
ArticleID=1039183
ArticleTitle=セキュアな IoT ソリューションの設計と構築, 第 2 回: ネットワーク上の IoT データをセキュリティーで保護する
publish-date=10112018