第3章 JBoss セキュリティモデル
他の JBoss アーキテクチャと同様に、最低限レベルのセキュリティは代替の実装が提供されるインターフェースのセットとして定義されます。次のインターフェースは JBoss サーバーセキュリティレイヤを定義します。
org.jboss.security.AuthenticationManager
org.jboss.security.RealmMapping
org.jboss.security.SecurityProxy
org.jboss.security.AuthorizationManager
org.jboss.security.AuditManager
org.jboss.security.MappingManager
図3.1「JBoss サーバー EJB コンテナ要素とのセキュリティモデルのインターフェース関係」 はセキュリティインターフェースと EJB コンテナアーキテクチャとの関係のクラスダイアグラムを示しています。
図3.1 JBoss サーバー EJB コンテナ要素とのセキュリティモデルのインターフェース関係
EJB コンテナレイヤはクラス
org.jboss.ejb.Container
、org.jboss.SecurityInterceptor
、org.jboss.SecurityProxyInterceptor
で表されます。その他のクラスは JBoss セキュリティサブシステムにより提供されるインターフェースとクラスです。
J2EE セキュリティモデルの実装に必要な 2 つのインターフェースは以下のとおりです。
org.jboss.security.AuthenticationManager
org.jboss.security.AuthorizationManager
図3.1「JBoss サーバー EJB コンテナ要素とのセキュリティモデルのインターフェース関係」 で示されたセキュリティインターフェースのロールは以下のとおり要約されます。
セキュリティインターフェースのロール
- AuthenticationManager
- このインターフェースの役割は プリンシパル と関連付けられる資格情報を検証することです。Principal とはユーザー名、雇用者番号、ソーシャルセキュリティ番号などのアイデンティティです。資格情報 はパスワード、セッションキー、デジタル署名などアイデンティティの証明です。
isValid
メソッドは呼び出されて、ユーザーアイデンティティと動作環境で既知の関連付けられた資格情報がユーザーアイデンティティの有効な証明となるかを決定します。 - AuthorizationManager
- このインターフェースの役割は Java EE 仕様により指示されたアクセス制御です。このインターフェースの実装により、プラグ可能な承認に役立つ Policy Provider のセットをスタックできます。
- SecurityProxy
- このインターフェースはカスタムの
SecurityProxyInterceptor
プラグインの要件を記述します。SecurityProxy
により EJB ホームとリモートインターフェースメソッドの両方に対してメソッドベースでカスタムセキュリティチェックの外部化が可能になります。 - AuditManager
- このインターフェースの役割はセキュリティイベントの監査証跡を提供することです。
- MappingManager
- このインターフェースの役割は Principal、Role、Attribute のマッピングを提供することです。AuthorizationManager の実装は内部的にマッピングマネージャを呼び出し、アクセス制御を実行する前にロールをマップすることができます。
- SecurityDomain
- これは
AuthenticationManager
、RealmMapping、SubjectSecurityManager
インターフェースの拡張です。SecurityDomain
がコンポーネントでセキュリティを実装するための推奨される方法です。その理由として、JAAS Subject が持つ利点、ASP スタイルアプリケーションとリソースデプロイメントに提供される強化したサポートがあげられます。java.security.KeyStore
、Java Secure Socket Extension (JSSE)com.sun.net.ssl.KeyManagerFactory
、com.sun.net.ssl.TrustManagerFactory
インターフェースはクラスに含まれます。 - RealmMapping
- このインターフェースの役割はプリンシパルマッピングとロールマッピングです。
getPrincipal
メソッドは動作環境でユーザーアイデンティティを既知として取り、アプリケーションドメインアイデンティティを返します。doesUserHaveRole
メソッドは動作環境のユーザーアイデンティティがアプリケーションドメインから指定されたロールを割り当てられていることを検証します。
AuthenticationManager
、RealmMapping
、SecurityProxy
インターフェースは JAAS 関連クラスと関連がないことに注意してください。JBossSX フレームワークは JAAS に大きく依存していますが、Java EE セキュリティモデルの実装に必要な基本的なセキュリティインターフェースは依存していません。JBossSX フレームワークは JAAS に基づく基本的なセキュリティプラグインインターフェースの単なる実装です。
これについては 図3.2「JBossSX フレームワーク実装クラスと JBoss サーバー EJB コンテナレイヤ」 のコンポーネント図で示されています。このプラグインアーキテクチャでは、JAAS ベースの JBossSX 実装クラスを自由に非 JAAS カスタムセキュリティマネージャ実装に置き換えることができることを表しています。この方法については 図3.2「JBossSX フレームワーク実装クラスと JBoss サーバー EJB コンテナレイヤ」 の JBossSX 設定に使用できる JBossSX MBean を参照してください。
図3.2 JBossSX フレームワーク実装クラスと JBoss サーバー EJB コンテナレイヤ
3.1. 宣言型セキュリティの有効化の再確認
本項の冒頭で Java EE 標準セキュリティモデルに関する説明は、セキュリティを有効にする JBoss サーバー固有のデプロイメント記述子を使用する要件を説明して終わりました。この設定に関する詳細は 図3.3「jboss.xml と jboss-web.xml セキュリティ要素のサブセット」 に示されています。ここでは、JBoss 固有の EJB と Web アプリケーションのデプロイメント記述子のセキュリティ関連要素を示しています。
図3.3 jboss.xml と jboss-web.xml セキュリティ要素のサブセット
<security-domain> 要素の値は、JBoss が EJB と Web コンテナに使用するセキュリティマネージャインターフェース実装の JNDI 名を指定します。これは
AuthenticationManager
と RealmMapping
インターフェースの両方を実装するオブジェクトです。トップレベルの要素として指定すると、デプロイメントユニットのすべての EJB に対しどのセキュリティドメインを指定するかを定義します。デプロイメントユニット内で複数のセキュリティマネージャを混在させると、内部コンポーネントの動作や管理が複雑になるため、これが一般的な使用方法です。
個別の EJB にセキュリティドメインを指定するには、コンテナの設定レベルで <security-domain> を指定します。これによりすべてのトップレベルの <security-domain> 要素を上書きします。
<unauthenticated-principal> 要素は、認証されていないユーザーが EJB を呼び出す場合に
EJBContext.getUserPrincipal
メソッドにより返される Principal
オブジェクトに使用する名前を指定します。これにより認証されていない呼び出し側に特別なパーミッションを伝えることはありません。主要な目的は、セキュアでないサーブレットと JSP ページがセキュアでない EJB を呼び出すことができ、目的の EJB が getUserPrincipal
メソッドを使用して呼び出し側に対して nul でない Principal
を取得できるようにすることです。これは J2EE 仕様要件です。
<security-proxy> 要素はカスタムのセキュリティプロキシ実装を特定します。これにより、セキュリティロジックを EJB 実装に組み込むことなく EJB 宣言型セキュリティモデルのスコープ外で要求ごとのセキュリティチェックが可能になります。
org.jboss.security.SecurityProxy
インターフェースの実装を使用できます。別の方法として、EJB のホーム、リモート、ローカルホーム、またはローカルインターフェースでメソッドを実装するオブジェクトを使用する共通のインターフェースを使用することもできます。特定のクラスが SecurityProxy
インターフェースを実装しない場合は、インスタンスはメソッド呼び出しをオブジェクトに委譲する SecurityProxy
実装でラップされる必要があります。org.jboss.security.SubjectSecurityProxy
はデフォルトの JBossSX インストールで使用される SecurityProxy
実装の一例です。
簡易ステートレスセッション Bean のコンテキストのカスタム
SecurityProxy
の簡単な例を見てみましょう。カスタムの SecurityProxy
は、Bean の echo
メソッドを 4 文字の単語を使用してその引数として呼び出す者がいないことを検証します。これはロールベースのセキュリティでは不可能な確認です。セキュリティコンテキストはメソッド引数であり、呼び出し側のプロパティではないため FourLetterEchoInvoker
ロールを定義することはできません。カスタムの SecurityProxy
のコードは 例3.1「カスタムの EchoSecurityProxy 実装」 を参照してください。
例3.1 カスタムの EchoSecurityProxy 実装
package org.jboss.book.security.ex1; import java.lang.reflect.Method; import javax.ejb.EJBContext; import org.apache.log4j.Category; import org.jboss.security.SecurityProxy; /** A simple example of a custom SecurityProxy implementation * that demonstrates method argument based security checks. * @author Scott.Stark@jboss.org * @version $Revision: 1.4 $ */ public class EchoSecurityProxy implements SecurityProxy { Category log = Category.getInstance(EchoSecurityProxy.class); Method echo; public void init(Class beanHome, Class beanRemote, Object securityMgr) throws InstantiationException { log.debug("init, beanHome="+beanHome + ", beanRemote="+beanRemote + ", securityMgr="+securityMgr); // Get the echo method for equality testing in invoke try { Class[] params = {String.class}; echo = beanRemote.getDeclaredMethod("echo", params); } catch(Exception e) { String msg = "Failed to find an echo(String) method"; log.error(msg, e); throw new InstantiationException(msg); } } public void setEJBContext(EJBContext ctx) { log.debug("setEJBContext, ctx="+ctx); } public void invokeHome(Method m, Object[] args) throws SecurityException { // We don't validate access to home methods } public void invoke(Method m, Object[] args, Object bean) throws SecurityException { log.debug("invoke, m="+m); // Check for the echo method if (m.equals(echo)) { // Validate that the msg arg is not 4 letter word String arg = (String) args[0]; if (arg == null || arg.length() == 4) throw new SecurityException("No 4 letter words"); } // We are not responsible for doing the invoke } }
EchoSecurityProxy
は、Bean インスタンスで呼び出されるメソッドが init
メソッドをロードした echo(String)
メソッドと一致することを確認します。一致があると、メソッド引数は取得され、その長さが 4 または null に対して比較されます。どちらの場合でも SecurityException
がスローされます。
これは確かに不自然な例ですが、不自然なのはそのアプリケーション内のみです。アプリケーションがメソッド引数の値に基づいてセキュリティチェックを実行する必要があることは一般的な要件です。この例のポイントは、標準宣言型セキュリティモデルの範囲を越えたカスタムのセキュリティが Bean の実装に依存せずに導入できる方法を示していることです。これによりセキュリティ要件の仕様とコーディングをセキュリティの専門家に委譲することが可能です。セキュリティプロキシレイヤは Bean 実装に依存せずに行うことが可能なため、セキュリティはデプロイメント環境の要件と一致するよう変更できます。
EchoBean
に対するカスタムのプロキシとして EchoSecurityProxy
をインストールする関連付けられた jboss.xml
記述子は 例3.2「jboss.xml 記述子」 で示されています。
例3.2 jboss.xml 記述子
<jboss> <security-domain>java:/jaas/other</security-domain> <enterprise-beans> <session> <ejb-name>EchoBean</ejb-name> <security-proxy>org.jboss.book.security.ex1.EchoSecurityProxy</security-proxy> </session> </enterprise-beans> </jboss>
さて、この一部で図解されたように引数
Hello
と Four
を付けて EchoBean.echo
メソッドを呼び出そうとするクライアントを実行して、カスタムのプロキシをテストしてみましょう。
public class ExClient { public static void main(String args[]) throws Exception { Logger log = Logger.getLogger("ExClient"); log.info("Looking up EchoBean"); InitialContext iniCtx = new InitialContext(); Object ref = iniCtx.lookup("EchoBean"); EchoHome home = (EchoHome) ref; Echo echo = home.create(); log.info("Created Echo"); log.info("Echo.echo('Hello') = "+echo.echo("Hello")); log.info("Echo.echo('Four') = "+echo.echo("Four")); } }
最初の呼び出しは成功するはずですが、2 番目の呼び出しは、
Four
が 4 文字の単語であるため失敗するはずです。examples ディレクトリから Ant を使用して次のようにクライアントを実行します。
[examples]$ ant -Dchap=security -Dex=1 run-example run-example1: ... [echo] Waiting for 5 seconds for deploy... [java] [INFO,ExClient] Looking up EchoBean [java] [INFO,ExClient] Created Echo [java] [INFO,ExClient] Echo.echo('Hello') = Hello [java] Exception in thread "main" java.rmi.AccessException: SecurityException; nested exception is: [java] java.lang.SecurityException: No 4 letter words ... [java] Caused by: java.lang.SecurityException: No 4 letter words ...
予想通り
echo('Hello')
メソッド呼び出しは成功し、echo('Four')
メソッド呼び出しは乱雑な例外という結果となりました。上記の出力は本書では紙面の都合上省略されています。例外で重要な箇所は、EchoSecurityProxy
により生成された SecurityException("No 4 letter words")
が希望通り試行されたメソッド呼び出しを中止するために投げられたということです。