日本語PDF

10 プロキシ認証

Oracle Java Database Connectivity(JDBC)は、n層認証とも呼ばれるプロキシ認証を提供しています。この機能は、JDBC Oracle Call Interface(OCI)ドライバとJDBC Thinドライバの両方を介してサポートされています。この章の構成は、次のとおりです。

ノート:

Oracle Databaseはプロキシ認証機能を3つの層のみでサポートしています。複数の中間層を横断してのサポートはありません。

10.1 プロキシ認証について

プロキシ認証はユーザー認証に中間層を使用する処理です。次の3つの形式のプロキシ認証を使用することによって、クライアントのセキュアなプロキシとして動作する中間層サーバーを設計することができます。

  • 中間層サーバーはデータベース・サーバーおよびクライアントに対してそれ自身を認証します。この場合、アプリケーション・ユーザーまたは別のアプリケーションは中間層サーバーに対して自分自身を認証します。クライアントの識別情報は、データベースに到達するまで確実に保持されます。

  • クライアント、つまりデータベース・ユーザーは、中間層サーバーによっては認証されません。クライアントのIDとデータベース・パスワードは、中間層サーバーを経由してデータベース・サーバーに渡され、認証に使用されます。

  • クライアント、つまりグローバル・ユーザーは中間層サーバーによって認証され、クライアントのユーザー名を取得するために中間層を経由して識別名(DN)または証明書を渡します。

    ノート:

    中間層サーバーがクライアントの代理として行った処理は監査できます。

すべての場合で、クライアントのプロキシ(代理)として動作するために、管理者は中間層サーバーを認証する必要があります。たとえば、中間層サーバーが最初にユーザーHRとしてデータベースに接続し、プロキシ接続をユーザーjeffとしてアクティブにしてから、次の文を発行して、クライアントのプロキシとして動作するように中間層サーバーを認証するとします。

ALTER USER jeff GRANT CONNECT THROUGH HR;

また、次のことも可能です。

  • クライアントとして接続する場合に中間層がアクティブにすることを許可されるロールを指定すること。たとえば、

    CREATE ROLE role1; 
    GRANT SELECT ON employees TO role1;
    ALTER USER jeff GRANT CONNECT THROUGH HR ROLE role1;
    

    このrole句は、ロール・リストに指定されたデータベース・オブジェクトのみにアクセスを制限します。空のロール・リストを指定することも可能です。

  • PROXY_USERSデータ・ディクショナリ・ビューに問い合せて、中間層への接続を現在認可されているユーザーを検索すること。

  • ALTER USER文のREVOKE CONNECT THROUGH句を使用してプロキシ接続の認可を取り消すこと。

ノート:

プロキシ認証の場合、認証中に、データベースへのJDBC接続でデータベース・セッションが作成され、接続の存続中に他のセッションが作成されます。

各種のプロキシ接続を設定するには、oracle.jdbc.OracleConnectionインタフェースにある、各種のフィールドやメソッドを使用する必要があります。

10.2 各種のプロキシ接続

プロキシ接続は、次のいずれかのオプションを使用して作成できます。

  • USER NAME

    このオプションは、ユーザー名またはパスワード(あるいはその両方)を指定して実行します。パスワードを使用する認証を指定するには、次のSQL文を使用します。

    ALTER USER jeff GRANT CONNECT THROUGH HR AUTHENTICATED USING PASSWORD;
    

    この場合、jeffはユーザー名で、HRjeffのプロキシです。

    パスワード・オプションは、セキュリティを強化するためのものです。authenticated句がない場合、パスワードがなく、ユーザー名のみを使用するデフォルトの認証が使用されます。デフォルト認証を指定するには、次のSQL文を使用します。

    ALTER USER jeff GRANT CONNECT THROUGH HR
    
  • DISTINGUISHED NAME

    この識別名は、プロキシされるユーザーのパスワードにかわるグローバル名です。識別名を使用する場合のSQL文の例を次に示します。

    CREATE USER jeff IDENTIFIED GLOBALLY AS 'CN=jeff,OU=americas,O=oracle,L=redwoodshores,ST=ca,C=us';
    

    identified globally as句の後に続く文字列が識別名です。次に、この識別名を使用して認証する必要があります。識別名を使用して認証を指定するには、次のSQL文を使用します。

    ALTER USER jeff GRANT CONNECT THROUGH HR AUTHENTICATED USING DISTINGUISHED NAME;
    
  • CERTIFICATE

    これは、プロキシされるユーザーの資格証明をデータベースに渡す、より暗号化された方法です。証明書には、エンコードされた識別名が含まれます。証明書を生成する1つの方法として、ウォレットを作成した後、そのウォレットをデコードして証明書を取得する方法があります。ウォレットはrunutl mkwalletを使用して作成できます。その後、生成した証明書を使用して認証を受ける必要があります。証明書を使用する認証を指定するには、次のSQL文を使用します。

    ALTER USER jeff GRANT CONNECT THROUGH HR AUTHENTICATED USING CERTIFICATE;
    

    ノート:

    証明書によるプロキシ認証は、将来のOracle Databaseリリースではサポート対象外となります。

ノート:

  • すべてのオプションは、ロールと関連付けることができます。

  • プロキシとしての役割を果す新しい接続をオープンすると、新しいセッションがデータベース・サーバー上で開始されます。グローバル・トランザクションを開始してから、openProxySessionメソッドをコールした場合、この時点で、グローバル・トランザクションの一部ではなくなります。そのかわり、新たに作成されたJDBC接続に含まれているようになります。通常は、グローバル・トランザクションを作成または再開する前にopenProxySessionメソッドがコールされるため、これが発生することはありません。この場合、まだグローバル・トランザクションの一部です。

10.3 プロキシ接続の作成

ユーザー(たとえば、jeff)は、別のユーザー(たとえば、HR)を介してデータベースに接続する必要があります。プロキシ・ユーザーHRには、アクティブな認証済接続が必要です。ドライバがサーバーにコマンドを送信してユーザーjeffのセッションを作成することで、このアクティブな接続上にプロキシ・セッションが作成されます。サーバーは新しいセッションIDを戻し、ドライバはセッション切替えコマンドを送信してこの新しいセッションに切り替えます。

JDBC OCIとThinドライバは、同じ方法でセッションを切り替えます。ドライバは、永続的に新しいセッションjeffに切り替えます。その結果、プロキシ・セッションHRは、新しいセッションjeffがクローズされるまで使用できません。

ノート:

oracle.jdbc.OracleConnectionインタフェースからisProxySessionメソッドを使用して、接続に関連付けられている現在のセッションがプロキシ・セッションであるかどうかをチェックすることができます。接続に関連付けられている現在のセッションがプロキシ・セッションの場合、このメソッドはtrueを戻します。

oracle.jdbc.OracleConnectionインタフェースから、次のメソッドを使用することで、新しいプロキシ・セッションが開かれます。

void openProxySession(int type, java.util.Properties prop) throws SQLExceptionOpens

ここで、

typeはプロキシ・セッションのタイプで、次の値を指定できます。

  • OracleConnection.PROXYTYPE_USER_NAME

    このタイプのセッションは、ユーザー名を指定するために使用されます。

  • OracleConnection.PROXYTYPE_DISTINGUISHED_NAME

    このタイプのセッションは、ユーザーの識別名を指定するために使用されます。

  • OracleConnection.PROXYTYPE_CERTIFICATE

    このタイプのセッションは、プロキシ証明書を指定するために使用されます。

propはプロキシ・セッションのプロパティ値で、次の値を指定できます。

  • PROXY_USER_NAME

    このプロパティ値はタイプOracleConnection.PROXYTYPE_USER_NAMEで使用できます。値はjava.lang.Stringになります。

  • PROXY_DISTINGUISHED_NAME

    このプロパティ値はタイプOracleConnection.PROXYTYPE_DISTINGUISHED_NAMEで使用できます。値はjava.lang.Stringになります。

  • PROXY_CERTIFICATE

    このプロパティ値はタイプOracleConnection.PROXYTYPE_CERTIFICATEで使用できます。値は、証明書が含まれるbytep[]配列です。

  • PROXY_ROLES

    このプロパティ値は次のタイプで使用できます。

    • OracleConnection.PROXYTYPE_USER_NAME

    • OracleConnection.PROXYTYPE_DISTINGUISHED_NAME

    • OracleConnection.PROXYTYPE_CERTIFICATE

    値はjava.lang.Stringになります。

  • PROXY_SESSION

    このプロパティ値は、プロキシ・セッションを閉じるためにcloseメソッドで使用されます。

  • PROXY_USER_PASSWORD

    このプロパティ値はタイプOracleConnection.PROXYTYPE_USER_NAMEで使用できます。値はjava.lang.Stringになります。

次のコードは、openProxySessionメソッドの使用方法を示します。

    java.util.Properties prop = new java.util.Properties();
    prop.put(OracleConnection.PROXY_USER_NAME, "jeff");
    String[] roles = {"role1", "role2"};
    prop.put(OracleConnection.PROXY_ROLES, roles);
    conn.openProxySession(OracleConnection.PROXYTYPE_USER_NAME, prop);
    

10.4 プロキシ・セッションのクローズ

OracleConnection.closeメソッドにOracleConnection.PROXY_SESSIONパラメータを渡すことによって、OracleConnection.openProxySessionメソッドでオープンしたプロキシ・セッションをクローズするには、次の操作を行います。

OracleConnection.close(OracleConnection.PROXY_SESSION);

これは、キャッシュされていない接続でのプロキシ・セッションのクローズと同じです。接続自体をクローズするには、標準のcloseメソッドを明示的にコールする必要があります。プロキシ・セッションをクローズせずにcloseメソッドを直接コールすると、プロキシ・セッションと接続の両方がクローズします。その方法は次のとおりです。

OracleConnection.close(OracleConnection.INVALID_CONNECTION);

10.5 プロキシ接続のキャッシュ

プロキシ接続は、通常の接続と同様にキャッシュできます。プロキシ接続をキャッシュすると、パフォーマンスが向上します。プロキシ接続をキャッシュするには、getConnectionメソッドのいずれかを使用して、キャッシュ対応のOracleDataSourceオブジェクトで接続を作成する必要があります。

プロキシ接続は、接続キャッシュの接続属性機能を使用して、接続キャッシュ内にキャッシュできます。接続属性はユーザー定義の名前/値ペアで、接続を接続キャッシュに戻して再利用する前に接続にタグを付けるのに役立ちます。タグ付けされた接続が取得されると、その接続を直接使用してプロキシ・セッションを作成またはクローズできるため、ラウンドトリップは必要ありません。ユニバーサル接続プールは、すべてのユーザー/パスワード認証接続のキャッシュをサポートします。したがって、ユーザー認証のすべてのプロキシ接続はキャッシュおよび取得できます。

接続属性を適用せずにプロキシ接続をクローズすることはお薦めしません。接続属性を適用せずにプロキシ接続をクローズすると、その接続は再利用のために接続キャッシュに戻されますが取得できません。接続キャッシュ・メカニズムでは、セッション状態を記憶したりリセットしたりしません。

プロキシ接続は、直接クローズすることで、接続キャッシュから削除できます。

10.6 プロキシ接続の制限

プロキシ接続をクローズすると、プロキシ・セッション中またはプロキシ・セッションより前にプロキシ接続によって作成されたすべてのSQL文が自動的にクローズされます。これがアプリケーション・プーリングまたは文キャッシュに予期しない結果を招く可能性があります。次のサンプル・コードは、プロキシ接続のこの制限について説明しています。

例1

....
public void displayName(String N)  // Any function using the Proxy feature
{
     Properties props = new Properties();
     props.put("PROXY_USER_NAME", proxyUser);
     c.openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);
     .......
     c.close(OracleConnection.PROXY_SESSION);
}
 
public static void main (String args[]) throws SQLException
{
    ............
    PreparedStatement pstmt = conn.prepareStatement("SELECT first_name FROM EMPLOYEES WHERE employee_id = ?");
    pstmt.setInt(1, 205);
    ResultSet rs = pstmt.executeQuery();
    while (rs.next())
    {
        displayName(rs.getString(1));
         if (rs.isClosed() // The ResultSet is already closed while closing the connection!
         {
             throw new Exception("Your ResultSet has been prematurely closed! 
Your Statement object is also dead now.");
         }
    }
}

この例では、displayNameメソッドでプロキシ接続をクローズすると、PreparedStatementオブジェクトとResultSetオブジェクトもクローズされます。したがって、ループ内でResultSetオブジェクトのステータスをチェックしないと、そのループはnextメソッドが2度目にコールされたときに失敗します。

例2

    ....
    PreparedStatement pstmt = conn.prepareStatement("SELECT first_name FROM EMPLOYEES WHERE employee_id = ?");
    pstmt.setString(1, "205");
    ResultSet rs = pstmt.executeQuery();
    while (rs.next())
    {
        ....
    }
 
    Properties props = new Properties();
    props.put("PROXY_USER_NAME", proxyUser);
 
    conn.openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);
    .......
    conn.close(OracleConnection.PROXY_SESSION);
 
    // Try to use the PreparedStatement again
    pstmt.setString(1, "28960");
// This line of code will fail because the Statement is already closed while closing the connection!
    rs = pstmt.executeQuery(); 

この例では、PreparedStatementオブジェクトとResultSetオブジェクトは、プロキシ接続をオープンする前はうまく機能します。しかし、同じPreparedStatementオブジェクトをプロキシ接続のクローズ後に実行しようとすると、その文は失敗します。