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
はユーザー名で、HR
はjeff
のプロキシです。パスワード・オプションは、セキュリティを強化するためのものです。
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
オブジェクトをプロキシ接続のクローズ後に実行しようとすると、その文は失敗します。