Oracle Java Database Connectivity(JDBC)は、n層認証とも呼ばれるプロキシ認証を提供しています。この機能は、JDBC Oracle Call Interface(OCI)ドライバとJDBC Thinドライバの両方を介してサポートされています。この章の内容は次のとおりです。
注意: Oracle Databaseはプロキシ認証機能を3つの層のみでサポートしています。複数の中間層を横断してのサポートはありません。 |
プロキシ認証はユーザー認証に中間層を使用する処理です。次の3つの形式のプロキシ認証を使用することによって、クライアントのセキュアなプロキシとして動作する中間層サーバーを設計することができます。
中間層サーバーはデータベース・サーバーおよびクライアントに対してそれ自身を認証します。この場合、アプリケーション・ユーザーまたは別のアプリケーションは中間層サーバーに対して自分自身を認証します。クライアントのIDはデータベースまですべての段階で維持されています。
クライアント、つまりデータベース・ユーザーは、中間層サーバーによっては認証されません。クライアントのIDとデータベース・パスワードは、中間層サーバーを経由してデータベース・サーバーに渡され、認証に使用されます。
クライアント、つまりグローバル・ユーザーは中間層サーバーによって認証され、クライアントのユーザー名を取得するために中間層を経由して識別名(DN)または証明書を渡します。
注意: 中間層サーバーがクライアントの代理として行った処理は監査できます。詳細は、『Oracle Databaseセキュリティ・ガイド』を参照してください。 |
すべての場合で、クライアントのプロキシ(代理)として動作するために、管理者は中間層サーバーを認証する必要があります。たとえば、中間層サーバーが最初にユーザーscott
としてデータベースに接続し、プロキシ接続をユーザーjeff
としてアクティブにしてから、次の文を発行して、クライアントのプロキシとして動作するように中間層サーバーを認証するとします。
ALTER USER jeff GRANT CONNECT THROUGH scott;
また、次のことも可能です。
クライアントとして接続する場合に中間層がアクティブにすることを許可されるロールを指定すること。たとえば、次のようになります。
CREATE ROLE role1; GRANT SELECT ON emp TO role1; ALTER USER jeff GRANT CONNECT THROUGH scott ROLE role1;
このrole句は、ロール・リストに指定されたデータベース・オブジェクトのみにアクセスを制限します。空のロール・リストを指定することも可能です。
PROXY_USERS
データ・ディクショナリ・ビューに問い合せて、中間層への接続を現在認可されているユーザーを検索すること。
ALTER USER
文のREVOKE CONNECT THROUGH
句を使用してプロキシ接続の認可を取り消すこと。
注意: この章では、データベースへのJDBC接続はデータベース内のユーザー・セッションであり、その逆の場合もあります。 |
各種のプロキシ接続を設定するには、oracle.jdbc.OracleConnection
インタフェースにある、各種のフィールドやメソッドを使用する必要があります。
プロキシ接続は、次のいずれかのオプションを使用して作成できます。
USER NAME
このオプションは、ユーザー名またはパスワード(あるいはその両方)を指定して実行します。パスワードを使用する認証を指定するには、次のSQL文を使用します。
ALTER USER jeff GRANT CONNECT THROUGH scott AUTHENTICATED USING password;
この場合、jeff
はユーザー名で、scott
はjeff
のプロキシです。
パスワード・オプションは、セキュリティを強化するためのものです。authenticated
句がない場合、パスワードがなく、ユーザー名のみを使用するデフォルトの認証が使用されます。デフォルト認証を指定するには、次のSQL文を使用します。
ALTER USER jeff GRANT CONNECT THROUGH scott
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 scott AUTHENTICATED USING DISTINGUISHED NAME;
CERTIFICATE
これは、プロキシとしての役割を果すユーザーの資格証明をさらに暗号化してデータベースに渡す方法です。証明書には、エンコードされた識別名が含まれています。証明書を生成する1つの方法は、ウォレットを作成してから、そのウォレットをデコード化して証明書を取得することです。ウォレットはrunutl mkwallet
を使用して作成できます。次に、生成された証明書を使用して認証する必要があります。証明書を使用する認証を指定するには、次のSQL文を使用します。
ALTER USER jeff GRANT CONNECT THROUGH scott AUTHENTICATED USING CERTIFICATE;
注意: 証明書によるプロキシ認証は、将来のOracle Databaseリリースではサポート対象外となります。 |
注意:
|
ユーザー(たとえば、jeff
)は、別のユーザー(たとえば、scott
)を介してデータベースに接続する必要があります。プロキシ・ユーザーscott
には、アクティブな認証済接続が必要です。ドライバがサーバーにコマンドを送信してユーザーjeff
のセッションを作成することで、このアクティブな接続上にプロキシ・セッションが作成されます。サーバーは新しいセッションIDを戻し、ドライバはセッション切替えコマンドを送信してこの新しいセッションに切り替えます。
JDBC OCIとThinドライバは、同じ方法でセッションを切り替えます。ドライバは、永続的に新しいセッションjeff
に切り替えます。その結果、プロキシ・セッションscott
は、新しいセッション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);
OracleConnection.close
メソッドにOracleConnection.PROXY_SESSION
パラメータを渡すことによって、OracleConnection.openProxySession
メソッドでオープンしたプロキシ・セッションをクローズするには、次の操作を行います。
OracleConnection.close
(OracleConnection.PROXY_SESSION);
これは、キャッシュされていない接続でのプロキシ・セッションのクローズと同じです。接続自体をクローズするには、標準のclose
メソッドを明示的にコールする必要があります。プロキシ・セッションをクローズせずにclose
メソッドを直接コールすると、プロキシ・セッションと接続の両方がクローズします。その方法は次のとおりです。
OracleConnection.close(OracleConnection.INVALID_CONNECTION);
プロキシ接続は、通常の接続と同様にキャッシュできます。プロキシ接続をキャッシュすると、パフォーマンスが向上します。プロキシ接続をキャッシュするには、getConnection
メソッドのいずれかを使用して、キャッシュ対応のOracleDataSource
オブジェクトで接続を作成する必要があります。
プロキシ接続は、接続キャッシュの接続属性機能を使用して、接続キャッシュ内にキャッシュできます。接続属性はユーザー定義の名前/値ペアで、接続を接続キャッシュに戻して再利用する前に接続にタグを付けるのに役立ちます。タグ付けされた接続が取得されると、その接続を直接使用してプロキシ・セッションを作成またはクローズできるため、ラウンドトリップは必要ありません。暗黙的接続キャッシュは、すべてのユーザー/パスワード認証接続のキャッシュをサポートします。したがって、ユーザー認証のすべてのプロキシ接続はキャッシュおよび取得できます。
接続属性を適用せずにプロキシ接続をクローズすることはお薦めしません。接続属性を適用せずにプロキシ接続をクローズすると、その接続は再利用のために接続キャッシュに戻されますが取得できません。接続キャッシュ・メカニズムでは、セッション状態を記憶したりリセットしたりしません。
プロキシ接続は、直接クローズすることで、接続キャッシュから削除できます。
プロキシ接続をクローズすると、プロキシ・セッション中またはプロキシ・セッションより前にプロキシ接続によって作成されたすべての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 empname FROM EMP WHERE empno = ?"); pstmt.setString(1, "28959"); 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 empname FROM EMP WHERE empno = ?"); pstmt.setString(1, "28959"); 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
オブジェクトをプロキシ接続のクローズ後に実行しようとすると、その文は失敗します。