Java SASLの概要

  • Java

  • link:/category/security-2/ [セキュリティ]

1. 概要

このチュートリアルでは、https://tools.ietf.org/html/rfc4422 [Simple Authentication and Security Layer](SASL)の基本について説明します。 Javaが通信を保護するためにSASLの採用をサポートする方法を理解します。
このプロセスでは、単純なクライアントとサーバーの通信を使用し、SASLで保護します。

2. SASLとは何ですか?

SASLは、*インターネットプロトコルの認証およびデータセキュリティのフレームワーク*です。 インターネットプロトコルを特定の認証メカニズムから分離することを目的としています。 進むにつれて、この定義の一部をよりよく理解できます。
通信におけるセキュリティの必要性は暗黙的です。 *クライアントとサーバーの通信のコンテキストでこれを理解してみましょう*。 通常、クライアントとサーバーはネットワークを介してデータを交換します。 両当事者が互いに信頼し、安全にデータを送信できることが不可欠です。

2.1. SASLはどこに収まりますか?

アプリケーションでは、SMTPを使用して電子メールを送信し、LDAPを使用してディレクトリサービスにアクセスできます。 ただし、これらの各プロトコルは、Digest-MD5やKerberosなどの別の認証メカニズムをサポートする場合があります。
プロトコルが認証メカニズムをより宣言的に交換する方法があった場合はどうなりますか? これはまさにSASLが登場する場所です。 SASLをサポートするプロトコルは、常にSASLメカニズムをサポートできます。
したがって、*アプリケーションは適切なメカニズム*をネゴシエートし、それを認証および安全な通信に採用できます。

2.2. SASLの仕組み

さて、SASLがセキュリティの全体的なスキームのどこに当てはまるかを見てきたので、その仕組みを理解しましょう。
SASL *はチャレンジ/レスポンスフレームワーク*です。 ここで、サーバーはクライアントにチャレンジを発行し、クライアントはチャレンジに基づいて応答を送信します。 チャレンジとレスポンスは、任意の長さのバイト配列であるため、メカニズム固有のデータを保持できます。
link:/uploads/SASL-Exchange.jpg []
この*交換は複数の反復*で継続でき、サーバーがそれ以上チャレンジを発行しなくなると最終的に終了します。
さらに、クライアントとサーバーは、認証後のセキュリティレイヤーをネゴシエートできます。 その後のすべての通信は、このセキュリティ層を活用できます。 ただし、一部のメカニズムは認証のみをサポートする場合があることに注意してください。
ここで、SASLはチャレンジとレスポンスのデータ交換のためのフレームワークのみを提供することを理解することが重要です。 データ自体やデータの交換方法については何も言及していません。 これらの詳細は、SASLの使用を採用しているアプリケーションに残されています。

3. JavaでのSASLサポート

SASLを使用したクライアント側とサーバー側の両方のアプリケーションの開発をサポートするJavaのAPIがあります。 APIは実際のメカニズム自体に依存しません。 Java SASL APIを使用するアプリケーションは、必要なセキュリティ機能に基づいてメカニズムを選択できます。

3.1. Java SASL API

パッケージ「javax.security.sasl」の一部として注目すべき重要なインターフェースは、_SaslServer_および_SaslClient_です。
_SaslServer_は、SASLのサーバー側のメカニズムを表します。
_SaslServer_をインスタンス化する方法を見てみましょう。
SaslServer ss = Sasl.createSaslServer(
  mechanism,
  protocol,
  serverName,
  props,
  callbackHandler);
ファクトリクラス_Sasl_を使用して_SaslServerをインスタンス化します。_メソッド_createSaslServer_はいくつかのパラメーターを受け入れます。
  • mechanism – SASLがサポートするメカニズムのIANA登録名

  • protocol –認証の対象となるプロトコルの名前
    行われている

  • serverName –サーバーの完全修飾ホスト名

  • props –認証の構成に使用される一連のプロパティ
    交換

  • callbackHandler –選択したユーザーが使用するコールバックハンドラー
    詳細情報を取得するメカニズム

    上記のうち、最初の2つのみが必須で、残りはNULL可能です。
    _SaslClient_は、SASLのクライアント側のメカニズムを表します。 _SaslClient_をインスタンス化する方法を見てみましょう。
SaslClient sc = Sasl.createSaslClient(
  mechanisms,
  authorizationId,
  protocol,
  serverName,
  props,
  callbackHandler);
ここでも、ファクトリクラス_Sasl_を使用して、_SaslClient_をインスタンス化します。 _createSaslClient_が受け入れるパラメーターのリストは、以前とほとんど同じです。
ただし、いくつかの微妙な違いがあります。
  • mechanisms –ここで、これは試してみるメカニズムのリストです

  • authorizationId –これは、プロトコルに依存するIDです
    認可に使用

    残りのパラメーターの意味とオプションは似ています。

3.2. Java SASLセキュリティプロバイダー

Java SASL APIの下には、セキュリティ機能を提供する実際のメカニズムがあります。 これらのメカニズムの実装は、https://docs.oracle.com/javase/9​​/security/java-cryptography-architecture-jca-reference-guide.htm [Java Cryptography Architecture]( JCA)。
JCAに登録されているセキュリティプロバイダーは複数存在する場合があります。 これらのそれぞれは、1つ以上のSASLメカニズムをサポートする場合があります*。
JavaはSunSASLとともにセキュリティプロバイダーとして出荷され、デフォルトでJCAプロバイダーとして登録されます。 ただし、これは他の利用可能なプロバイダーで削除または再注文することができます。
さらに、カスタムセキュリティプロバイダーを提供することは常に可能です。 これには、_SaslClient_および_SaslServer_インターフェイスを実装する必要があります。 そうすることで、カスタムセキュリティメカニズムも実装できます。

4. 例によるSASL

_SaslServer_と_SaslClient_の作成方法を確認したので、今度はそれらの使用方法を理解します。 クライアントコンポーネントとサーバーコンポーネントを開発します。 これらは、認証を達成するためにチャレンジとレスポンスを繰り返し交換します。 ここでの簡単な例では、DIGEST-MD5メカニズムを使用します。

4.1. クライアントとサーバー_CallbackHandler_

前に見たように、_CallbackHandler_の実装を_SaslServer_および_SaslClient_に提供する必要があります。 現在、_CallbackHandler_は、単一のメソッド_handle_を定義するシンプルなインターフェイスです。 このメソッドは、_Callback_の配列を受け入れます。
ここで、* _ Callback_は、セキュリティメカニズムが呼び出し側アプリケーションから認証データを収集する方法を示しています*。 たとえば、セキュリティメカニズムにはユーザー名とパスワードが必要な場合があります。 _NameCallback_や_PasswordCallback_など、使用可能な_Callback_実装がかなりあります。
まず、サーバーの_CallbackHandler_を定義する方法を見てみましょう。
public class ServerCallbackHandler implements CallbackHandler {
    @Override
    public void handle(Callback[] cbs) throws IOException, UnsupportedCallbackException {
        for (Callback cb : cbs) {
            if (cb instanceof AuthorizeCallback) {
                AuthorizeCallback ac = (AuthorizeCallback) cb;
                //Perform application-specific authorization action
                ac.setAuthorized(true);
            } else if (cb instanceof NameCallback) {
                NameCallback nc = (NameCallback) cb;
                //Collect username in application-specific manner
                nc.setName("username");
            } else if (cb instanceof PasswordCallback) {
                PasswordCallback pc = (PasswordCallback) cb;
                //Collect password in application-specific manner
                pc.setPassword("password".toCharArray());
            } else if (cb instanceof RealmCallback) {
                RealmCallback rc = (RealmCallback) cb;
                //Collect realm data in application-specific manner
                rc.setText("myServer");
            }
        }
    }
}
では、_Callbackhandler_のクライアント側を見てみましょう。
public class ClientCallbackHandler implements CallbackHandler {
    @Override
    public void handle(Callback[] cbs) throws IOException, UnsupportedCallbackException {
        for (Callback cb : cbs) {
            if (cb instanceof NameCallback) {
                NameCallback nc = (NameCallback) cb;
                //Collect username in application-specific manner
                nc.setName("username");
            } else if (cb instanceof PasswordCallback) {
                PasswordCallback pc = (PasswordCallback) cb;
                //Collect password in application-specific manner
                pc.setPassword("password".toCharArray());
            } else if (cb instanceof RealmCallback) {
                RealmCallback rc = (RealmCallback) cb;
                //Collect realm data in application-specific manner
                rc.setText("myServer");
            }
        }
    }
}
明確にするために、* Callback_配列をループし、特定の配列のみを処理しています*。 処理しなければならないものは、使用中のメカニズムに固有のもので、ここではDIGEST-MD5です。

4.2. SASL認証

そのため、クライアントとサーバー_CallbackHandler_を作成しました。 また、DIGEST-MD5メカニズム用に_SaslClient_および_SaslServer_をインスタンス化しました。
今こそ、それらが実際に動いているのを見る時です。
@Test
public void givenHandlers_whenStarted_thenAutenticationWorks() throws SaslException {
    byte[] challenge;
    byte[] response;

    challenge = saslServer.evaluateResponse(new byte[0]);
    response = saslClient.evaluateChallenge(challenge);

    challenge = saslServer.evaluateResponse(response);
    response = saslClient.evaluateChallenge(challenge);

    assertTrue(saslServer.isComplete());
    assertTrue(saslClient.isComplete());
}
ここで何が起こっているのかを理解してみましょう:
  • まず、クライアントはサーバーからデフォルトのチャレンジを取得します

  • 次に、クライアントはチャレンジを評価し、応答を準備します

  • このチャレンジ/レスポンス交換はもう1サイクル続きます

  • プロセスでは、クライアントとサーバーはコールバックハンドラーを使用して
    メカニズムの必要に応じて追加データを収集します

  • これでここでの認証は終了しますが、実際には繰り返し処理できます
    複数のサイクルにわたって

    *チャレンジおよびレスポンスのバイト配列の通常の交換は、ネットワーク上で行われます*。 ただし、ここでは簡単にするために、ローカルコミュニケーションを想定しています。

4.3. SASLセキュア通信

前述したように、SASLは認証だけでなく安全な通信をサポートできるフレームワークです。 ただし、*これは、基礎となるメカニズムがサポートしている場合にのみ可能です*。
まず、安全な通信をネゴシエートできたかどうかを最初に確認しましょう。
String qop = (String) saslClient.getNegotiatedProperty(Sasl.QOP);

assertEquals("auth-conf", qop);
ここで、* QOPは保護の品質*を表しています。 これは、クライアントとサーバーが認証中にネゴシエートするものです。 「auth-int」の値は、認証と整合性を示します。 一方、「auth-conf」の値は、認証、整合性、および機密性を示します。
セキュリティ層ができたら、それを活用して通信を保護できます。
クライアントで発信通信を保護する方法を見てみましょう。
byte[] outgoing = "Baeldung".getBytes();
byte[] secureOutgoing = saslClient.wrap(outgoing, 0, outgoing.length);

// Send secureOutgoing to the server over the network
また、同様に、サーバーは着信通信を処理できます。
// Receive secureIncoming from the client over the network
byte[] incoming = saslServer.unwrap(secureIncoming, 0, netIn.length);

assertEquals("Baeldung", new String(incoming, StandardCharsets.UTF_8));

5. 現実世界のSA​​SL

そのため、SASLとは何か、JavaでSASLを使用する方法については、かなり理解できました。 しかし、通常は、少なくとも毎日のルーチンではSASLを使用することになりません。
前に見たように、* SASLは主にLDAPやSMTP *などのプロトコルを対象としています。 ただし、Kafkaなど、より多くのアプリケーションがSASLに搭載されています。 それでは、SASLを使用してこのようなサービスで認証するにはどうすればよいでしょうか?
選択メカニズムとしてPLAINを使用してSASL用にKafka Brokerを設定したとします。 PLAINは、プレーンテキストのユーザー名とパスワードの組み合わせを使用して認証することを意味します。
SASL / PLAINを使用してKafka Brokerに対して認証するようにJavaクライアントを構成する方法を見てみましょう。
まず、単純なJAAS構成「kafka_jaas.conf」を提供します。
KafkaClient {
  org.apache.kafka.common.security.plain.PlainLoginModule required
  username="username"
  password="password";
};
JVMの起動中にこのJAAS設定を利用します。
-Djava.security.auth.login.config=kafka_jaas.conf
最後に、プロデューサーとコンシューマーのインスタンスに渡すプロパティをいくつか追加する必要があります。
security.protocol=SASL_SSL
sasl.mechanism=PLAIN
それだけです。 ただし、これはlink:/kafka-connectors-guide[Kafka client configuration]のほんの一部です。 PLAINとは別に、Kafkaは認証のためにGSSAPI / Kerberosもサポートしています。

6. SASLの比較

SASLは、クライアントとサーバーの通信を認証および保護するメカニズムに中立な方法を提供するのに非常に効果的ですが。 ただし、この点で利用できるソリューションは* SASLだけではありません。
Java自体は、この目的を達成するための他のメカニズムを提供します。 それらについて簡単に説明し、SASLに対抗する方法を理解します。
  • Java
    Secure Socket Extension
    (JSSE):* JSSEは、Java *用のSecure Sockets Layer(SSL)を実装するJavaのパッケージのセットです。 データの暗号化、クライアントとサーバーの認証、およびメッセージの整合性を提供します。 SASLとは異なり、JSSEは機能するためにPublic Key Infrastructure(PKI)に依存しています。 したがって、SASLはJSSEよりも柔軟で軽量であることがわかります。

  • Java
    GSS API
    (JGSS):* JGGSは、汎用セキュリティサービスアプリケーションプログラミングインターフェース(GSS-API)*用のJava言語バインディングです。 GSS-APIは、アプリケーションがセキュリティサービスにアクセスするためのIETF標準です。 Javaでは、GSS-APIの下で、Kerberosがサポートされている唯一のメカニズムです。 Kerberosが機能するには、Kerberosインフラストラクチャが再び必要です。 SASLと比較すると、ここではまだ選択肢が限られており、重量があります。

    全体として、SASLは非常に軽量なフレームワークであり、プラグイン可能なメカニズムを通じてさまざまなセキュリティ機能を提供します。 SASLを採用するアプリケーションには、ニーズに応じて、適切なセキュリティ機能セットを実装するための多くの選択肢があります。

7. 結論

要約すると、このチュートリアルでは、認証と安全な通信を提供するSASLフレームワークの基本を理解しました。 また、SASLのクライアント側およびサーバー側を実装するためにJavaで使用可能なAPIについても説明しました。
JCAプロバイダーを介してセキュリティー機構を使用する方法を見ました。 最後に、さまざまなプロトコルやアプリケーションを操作する際のSASLの使用法についても話しました。
いつものように、コードはhttps://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-security[GitHub上]にあります。