JPCERT コーディネーションセンター

MSC00-J. セキュアなデータ交換には Socket クラスではなく SSLSocket クラスを使用する

安全でない通信経路を使ってセンシティブなデータを送信する場合、java.net.Socket ではなく javax.net.ssl.SSLSocket を使用しなくてはならない。SSLSocket クラスが提供するセキュリティプロトコル Secure Sockets Layer/Transport Layer Security (SSL/TLS)を使用することで、通信経路におけるデータの盗聴や改ざんを防ぐことができる。

Scoket クラスでは提供されず、SSLSocket クラスで提供される主要なセキュリティ機能を以下に示す[API 2006]。

  • 完全性保護: SSL は能動的な盗聴(active wiretapper)によるメッセージ改ざんを防止する。
  • 認証: ほとんどのモードにおいて、SSLは通信相手の認証を行う。通常、サーバ認証は行われる。サーバの要求によってはクライアントも認証される場合がある。
  • 機密性(プライバシの保護): ほとんどのモードにおいて、SSLはクライアントとサーバの間でやり取りされるデータを暗号化する。これによりデータの機密性は保証され、受動的な盗聴(passive wiretapper)によって金融情報や個人情報等センシティブなデータが取得されることを防ぐ。

また、Remote Method Invocation (RMI) には SSL を使用することが重要である。RMI はオブジェクトのシリアライズを前提に動作するが、シリアライズされたデータの送信中の安全を保証しなくてはならないからである。Gong、Ellison、Dageforde らは SSLSocket を使用してセキュアな RMI 通信を行う方法について説明している[Gong 2003]。

本ルールではソケットを通じて送信されるデータの完全性については何ら考慮していないことに注意。データの完全性保護については、「SER02-J. センシティブなオブジェクトは信頼境界を越えて送信する前に署名し暗号化する」を参照。

違反コード

以下の違反コード例では、通常のソケットを使い、送信されるセンシティブな情報を保護していないサーバアプリケーションの例を示している。サーバのコードに続き、対応するクライアントアプリケーションのコードを示す。

// コードの簡略化のため、例外処理は省略
class EchoServer {
  public static void main(String[] args) throws IOException {
    ServerSocket serverSocket = null;
    try {
      serverSocket = new ServerSocket(9999);
      Socket socket = serverSocket.accept();
      PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
      BufferedReader in = new BufferedReader(
          new InputStreamReader(socket.getInputStream()));
      String inputLine;
      while ((inputLine = in.readLine()) != null) {
        System.out.println(inputLine);
        out.println(inputLine);
      }
    } finally {
      if (serverSocket != null) {
        try {
          serverSocket.close();
        } catch (IOException x) {
          // エラー処理
        }
      }
    }
  }
}

class EchoClient {
  public static void main(String[] args) 
                          throws UnknownHostException, IOException {
    Socket socket = null;
    try {
      socket = new Socket("localhost", 9999);
      PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
      BufferedReader in = new BufferedReader(
          new InputStreamReader(socket.getInputStream()));
      BufferedReader stdIn = new BufferedReader(
          new InputStreamReader(System.in));
      String userInput;
      while ((userInput = stdIn.readLine()) != null) {
        out.println(userInput);
        System.out.println(in.readLine());
      } 
    } finally {
      if (socket != null) {
        try {
          socket.close();
        } catch (IOException x) {
          // エラー処理
        }
      }
    }
  }
}

ERR05-J. チェック例外を finally ブロックの外に伝播させない」に従い、オープンされたソケットは適切にクローズされている。

適合コード

以下の適合コードでは、SSLSocket クラスを使用し、送信するパケットを SSL/TLS セキュリティプロトコルを使って保護している。

// コードの簡略化のため、例外処理は省略
class EchoServer {
  public static void main(String[] args) throws IOException {
    SSLServerSocket sslServerSocket = null;
    try {
      SSLServerSocketFactory sslServerSocketFactory =
          (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
      sslServerSocket = (SSLServerSocket) sslServerSocketFactory.
                        createServerSocket(9999);
      SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
      PrintWriter out = new PrintWriter(sslSocket.getOutputStream(),true);
      BufferedReader in = new BufferedReader(
          new InputStreamReader(sslSocket.getInputStream()));
      String inputLine;
      while ((inputLine = in.readLine()) != null) {
        System.out.println(inputLine); 
        out.println(inputLine); 
      } 
    } finally {
      if (sslServerSocket != null) {
        try {
          sslServerSocket.close();
        } catch (IOException x) {
          // エラー処理
        }
      }
    }
  }
}

class EchoClient {
  public static void main(String[] args) throws IOException {
    SSLSocket sslSocket = null;
    try {
      SSLSocketFactory sslSocketFactory =
          (SSLSocketFactory) SSLSocketFactory.getDefault();
      sslSocket =
          (SSLSocket) sslSocketFactory.createSocket("localhost", 9999);
      PrintWriter out = new PrintWriter(sslSocket.getOutputStream(), true);
      BufferedReader in = new BufferedReader(
          new InputStreamReader(sslSocket.getInputStream()));
      BufferedReader stdIn = new BufferedReader(
          new InputStreamReader(System.in));
      String userInput;
      while ((userInput = stdIn.readLine()) != null) {
        out.println(userInput);
        System.out.println(in.readLine());
      }
    } finally {
      if (sslSocket != null) {
        try {
          sslSocket.close();
        } catch (IOException x) {
          // エラー処理
        }
      }
    }
  }
}

SSLSocket を使用するプログラムは、SSL を使用しないポートに接続しようとすると、無期限の待ち状態に入ってしまう。同様に、SSLSocket を使用しないプログラムは SSL を使用するポートに接続しようとすると待ち状態に入ってしまう。

例外

MSC00-EX0: SSLSocket はセキュアなパケット通信を保証するため、性能上のオーバーヘッドを伴う。以下に挙げる通信を行う場合、通常のソケット通信で十分である。

リスク評価

単なるソケットを使用すると、データの機密性と完全性が保証されない。

ルール 深刻度 可能性 修正コスト 優先度 Level
MSC00-J P6 L2
自動検出

どのようなデータがソケットを通じて送信されるかを静的に判別することはできないため、自動検出は一般に不可能である。独自に用意したAPIを使いセキュアなソケットを通じてセンシティブなデータを送信するアプローチであれば検出は可能であろう。その場合、送信するデータがセンシティブなデータであるという印をあらかじめ付けておく必要がある。

関連ガイドライン
MITRE CWE CWE-311. Failure to encrypt sensitive data
参考文献
[API 2006]  
[Gong 2003] 11.3.3, Securing RMI Communications
[Ware 2008]  
翻訳元

これは以下のページを翻訳したものです。

MSC00-J. Use SSLSocket rather than Socket for secure data exchange (revision 74)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter