Realm 設定方法
目次
クイックスタート
このドキュメントでは、既存のユーザー名、パスワード、およびユーザーロールの「データベース」に接続することにより、Tomcatがコンテナ管理セキュリティをサポートするように構成する方法について説明します。これは、1つ以上の<security-constraint>
要素と、ユーザーが自分自身を認証する方法を定義する<login-config>
要素を含むWebアプリケーションを使用している場合にのみ必要です。これらの機能を利用していない場合は、このドキュメントをスキップして問題ありません。
コンテナ管理セキュリティに関する基本的な背景情報については、サーブレット仕様 (バージョン 2.4)のセクション12を参照してください。
Tomcatのシングルサインオン機能 (ユーザーが仮想ホストに関連付けられたWebアプリケーション全体で一度だけ認証できるようにする機能) の利用に関する情報については、こちらを参照してください。
概要
Realmとは?
Realmは、Webアプリケーション (またはWebアプリケーションのセット) の有効なユーザーを識別するユーザー名とパスワードの「データベース」であり、各有効なユーザーに関連付けられたロールのリストの列挙でもあります。特定のWebアプリケーションリソースへのアクセスは、特定のロールを持つすべてのユーザーに付与されるため (関連付けられたユーザー名のリストを列挙するのではなく)、ロールはUnixライクなオペレーティングシステムにおけるグループに似ていると考えることができます。特定のユーザーは、自分のユーザー名に関連付けられた任意の数のロールを持つことができます。
サーブレット仕様では、アプリケーションがセキュリティ要件を宣言するためのポータブルなメカニズム (web.xml
デプロイメントディスクリプタ内) が記述されていますが、サーブレットコンテナと関連するユーザーおよびロール情報の間のインターフェースを定義するポータブルAPIはありません。しかし、多くの場合、サーブレットコンテナを本番環境に既に存在する認証データベースまたはメカニズムに「接続」することが望ましいです。そのため、Tomcatは、この接続を確立するために「プラグイン」コンポーネントによって実装できるJavaインターフェース (org.apache.catalina.Realm
) を定義しています。認証情報の様々なソースへの接続をサポートする6つの標準プラグインが提供されています。
- DataSourceRealm - 名前付きJNDI JDBC DataSourceを介してアクセスされるリレーショナルデータベースに格納された認証情報にアクセスします。
- JNDIRealm - JNDIプロバイダを介してアクセスされるLDAPベースのディレクトリサーバーに格納された認証情報にアクセスします。
- UserDatabaseRealm - UserDatabase JNDIリソースに格納された認証情報にアクセスします。これは通常、XMLドキュメント (
conf/tomcat-users.xml
) によってバックアップされます。 - MemoryRealm - インメモリオブジェクトコレクションに格納された認証情報にアクセスします。これはXMLドキュメント (
conf/tomcat-users.xml
) から初期化されます。 - JAASRealm - Java Authentication & Authorization Service (JAAS) フレームワークを介して認証情報にアクセスします。
独自のRealm
実装を記述し、Tomcatと統合することも可能です。そのためには、以下を行う必要があります。
org.apache.catalina.Realm
を実装する、- コンパイル済みのRealmを
$CATALINA_HOME/lib
に配置する、 - 以下の「Realmの設定」セクションで説明されているようにRealmを宣言する、
- RealmをMBeans Descriptorsに宣言する。
Realmの設定
標準Realm実装の詳細に入る前に、Realmがどのように構成されるか、一般的な用語で理解することが重要です。一般的に、conf/server.xml
構成ファイルに、次のようなXML要素を追加します。
<Realm className="... class name for this implementation"
... other attributes for this implementation .../>
<Realm>
要素は、以下のいずれかのContainer
要素内にネストできます。Realm要素の場所は、そのRealmの「スコープ」(つまり、どのWebアプリケーションが同じ認証情報を共有するか)に直接影響します。
<Engine>
要素内 - このRealmは、下位の<Host>
または<Context>
要素内にネストされたRealm要素によってオーバーライドされない限り、すべての仮想ホスト上のすべてのWebアプリケーションで共有されます。<Host>
要素内 - このRealmは、下位の<Context>
要素内にネストされたRealm要素によってオーバーライドされない限り、この仮想ホストのすべてのWebアプリケーションで共有されます。<Context>
要素内 - このRealmは、このWebアプリケーションにのみ使用されます。
共通機能
ダイジェストパスワード
標準のRealm
実装のそれぞれにおいて、ユーザーのパスワードは(デフォルトで)クリアテキストで保存されます。多くの環境では、認証データを偶発的に閲覧した者が、正常にログオンして他のユーザーになりすますのに十分な情報を収集できるため、これは望ましくありません。この問題を回避するために、標準実装ではユーザーパスワードをダイジェスト化する概念をサポートしています。これにより、パスワードの保存バージョンをエンコードする(簡単に元に戻せない形式で)ことができますが、Realm
実装は認証のために引き続き利用できます。
標準のRealmが、保存されたパスワードを取得し、ユーザーが提示した値と比較して認証する場合、<Realm>
要素内にCredentialHandler
要素を配置することで、ダイジェストパスワードを選択できます。SSHA、SHA、またはMD5のいずれかのアルゴリズムをサポートする簡単な選択肢は、MessageDigestCredentialHandler
を使用することです。この要素は、java.security.MessageDigest
クラスがサポートするダイジェストアルゴリズム(SSHA、SHA、またはMD5)のいずれかに設定する必要があります。このオプションを選択すると、Realm
に保存されるパスワードの内容は、指定されたアルゴリズムによってダイジェスト化されたパスワードのクリアテキストバージョンである必要があります。
Realmのauthenticate()
メソッドが呼び出されると、ユーザーによって指定された(クリアテキストの)パスワード自体が同じアルゴリズムでダイジェスト化され、その結果がRealm
によって返された値と比較されます。一致した場合、元のパスワードのクリアテキストバージョンがユーザーによって提示されたものと同じであるため、このユーザーは認証されるべきであると判断されます。
クリアテキストパスワードのダイジェスト値を計算するには、2つの便利な手法がサポートされています。
- ダイジェストパスワードを動的に計算する必要があるアプリケーションを作成している場合は、
org.apache.catalina.realm.RealmBase
クラスの静的メソッドDigest()
を呼び出し、クリアテキストパスワード、ダイジェストアルゴリズム名、およびエンコーディングを引数として渡します。このメソッドはダイジェストパスワードを返します。 - コマンドラインユーティリティを実行してダイジェストパスワードを計算したい場合は、単に以下を実行します。すると、このクリアテキストパスワードのダイジェストバージョンが標準出力に返されます。
CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} {cleartext-password}
ダイジェスト認証でダイジェストパスワードを使用する場合、ダイジェストを生成するために使用されるクリアテキストは異なり、ダイジェストはソルトなしでMD5アルゴリズムを1回繰り返して使用する必要があります。上記の例では、{cleartext-password}
を{username}:{realm}:{cleartext-password}
に置き換える必要があります。たとえば、開発環境では、これはtestUser:Authentication required:testPassword
のような形式になります。{realm}
の値は、Webアプリケーションの<login-config>
要素の<realm-name>
要素から取得されます。web.xmlで指定されていない場合、デフォルト値のAuthentication required
が使用されます。
プラットフォームのデフォルト以外のエンコーディングを使用するユーザー名やパスワードは、以下を使用してサポートされます。
CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} -e {encoding} {input}
しかし、入力がダイジェスタに正しく渡されることを確認するために注意が必要です。ダイジェスタは{input}:{digest}
を返します。戻り値で入力が破損しているように見える場合、ダイジェストは無効になります。
ダイジェストの出力形式は{salt}${iterations}${digest}
です。ソルト長がゼロで反復回数が1の場合、出力は{digest}
に簡略化されます。
CATALINA_HOME/bin/digest.[bat|sh]
の完全な構文は以下のとおりです。
CATALINA_HOME/bin/digest.[bat|sh] [-a <algorithm>] [-e <encoding>]
[-i <iterations>] [-s <salt-length>] [-k <key-length>]
[-h <handler-class-name>] [-f <password-file> | <credentials>]
- -a - 格納される資格情報を生成するために使用するアルゴリズム。指定されていない場合、ハンドラに設定されたデフォルトが使用されます。ハンドラもアルゴリズムも指定されていない場合、デフォルトの
SHA-512
が使用されます。 - -e - 必要なバイトから文字への変換、または文字からバイトへの変換に使用するエンコーディング。指定されていない場合、システムエンコーディング(
Charset#defaultCharset()
)が使用されます。 - -i - 格納される資格情報を生成する際に使用する反復回数。指定されていない場合、CredentialHandlerに設定されたデフォルトが使用されます。
- -s - 資格情報の一部として生成および保存するソルトの長さ(バイト単位)。指定されていない場合、CredentialHandlerに設定されたデフォルトが使用されます。
- -k - 資格情報生成時に作成されたキー(複数可)の長さ(ビット単位)。指定されていない場合、CredentialHandlerに設定されたデフォルトが使用されます。
- -h - 使用するCredentialHandlerの完全修飾クラス名。指定されていない場合、組み込みのハンドラが順にテストされ(MessageDigestCredentialHandler、次にSecretKeyCredentialHandler)、指定されたアルゴリズムを受け入れる最初のハンドラが使用されます。
- -f - エンコードするパスワードを含むファイルの名前。ファイル内の各行には、1つのパスワードのみが含まれている必要があります。このオプションを使用すると、他のパスワード入力は無視されます。
サンプルアプリケーション
Tomcatに同梱されているサンプルアプリケーションには、フォームベースのログインを利用してセキュリティ制約で保護された領域が含まれています。アクセスするには、ブラウザでhttps://:8080/examples/jsp/security/protected/にアクセスし、デフォルトのUserDatabaseRealmについて説明されているユーザー名とパスワードのいずれかを使用してログインしてください。
マネージャーアプリケーション
実行中のTomcatインストールでアプリケーションをデプロイおよびアンデプロイするためにマネージャーアプリケーションを使用したい場合は、選択したRealm実装の少なくとも1つのユーザー名に「manager-gui」ロールを追加する必要があります。これは、マネージャーWebアプリケーション自体が、そのアプリケーションのHTMLインターフェース内の任意の要求URIにアクセスするために「manager-gui」ロールを必要とするセキュリティ制約を使用しているためです。
セキュリティ上の理由から、デフォルトのRealm (つまり、conf/tomcat-users.xml
を使用) のどのユーザー名にも「manager-gui」ロールは割り当てられていません。したがって、Tomcat管理者がこのロールを1人以上のユーザーに明示的に割り当てるまで、誰もこのアプリケーションの機能を利用することはできません。
標準Realm実装
DataSourceRealm
はじめに
DataSourceRealmは、JNDIに名前付きのJDBC DataSourceを介してアクセスされるリレーショナルデータベースでユーザーを検索するTomcat Realm
インターフェースの実装です。データベース構造が以下の要件に準拠している限り、既存のテーブル名やカラム名に適応できるかなりの設定の柔軟性があります。
- この
Realm
が認識すべき有効なユーザーごとに1行を含むテーブル(以下、usersテーブルと呼びます)が必要です。 - usersテーブルには少なくとも2つのカラムが含まれている必要があります(既存のアプリケーションで必要とされる場合は、それ以上含まれていても構いません)。
- ユーザーがログインしたときにTomcatが認識するユーザー名。
- ユーザーがログインしたときにTomcatが認識するパスワード。この値はクリアテキストまたはダイジェスト化されたものである場合があります - 詳細については以下を参照してください。
- 特定のユーザーに割り当てられた有効なロールごとに1行を含むテーブル(以下、user rolesテーブルと呼びます)が必要です。ユーザーが0個、1個、または複数個の有効なロールを持つことは合法です。
- user rolesテーブルには少なくとも2つのカラムが含まれている必要があります(既存のアプリケーションで必要とされる場合は、それ以上含まれていても構いません)。
- Tomcatが認識するユーザー名(usersテーブルで指定されているのと同じ値)。
- このユーザーに関連付けられた有効なロールのロール名。
クイックスタート
TomcatがDataSourceRealmを使用するように設定するには、以下の手順に従う必要があります。
- まだ行っていない場合は、上記の要件に準拠するテーブルとカラムをデータベースに作成してください。
- Tomcatが使用するためのデータベースのユーザー名とパスワードを設定してください。これは、上記のテーブルに対する少なくとも読み取り専用アクセス権を持っている必要があります。(Tomcatがこれらのテーブルに書き込もうとすることはありません。)
- データベース用のJNDIに名前付きのJDBC DataSourceを設定してください。JNDIに名前付きのJDBC DataSourceの設定方法については、JNDI DataSource設定例を参照してください。JNDI DataSourceがどこで定義されているかに応じて、
Realm
のlocalDataSource
属性を適切に設定するようにしてください。 - 以下で説明するように、
$CATALINA_BASE/conf/server.xml
ファイルに<Realm>
要素を設定してください。 - Tomcatが既に実行中の場合は、再起動してください。
Realm要素の属性
DataSourceRealmを設定するには、<Realm>
要素を作成し、上記で説明したように、$CATALINA_BASE/conf/server.xml
ファイルにネストします。DataSourceRealmの属性は、Realm設定ドキュメントで定義されています。
例
必要なテーブルを作成するためのSQLスクリプトの例は次のようになります(使用する特定のデータベースに合わせて構文を調整してください)。
create table users (
user_name varchar(15) not null primary key,
user_pass varchar(15) not null
);
create table user_roles (
user_name varchar(15) not null,
role_name varchar(15) not null,
primary key (user_name, role_name)
);
以下は、「authority」という名前のMySQLデータベースを使用する例です。上記のテーブルで構成されており、「java:/comp/env/jdbc/authority」という名前のJNDI JDBC DataSourceでアクセスされます。
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/authority"
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name"/>
追加の注意事項
DataSourceRealmは以下のルールに従って動作します。
- ユーザーが保護されたリソースに初めてアクセスしようとすると、Tomcatはこの
Realm
のauthenticate()
メソッドを呼び出します。したがって、データベースに直接加えた変更(新規ユーザー、パスワードやロールの変更など)は直ちに反映されます。 - ユーザーが認証されると、そのユーザー(および関連するロール)は、ユーザーのログイン期間中Tomcat内にキャッシュされます。(FORMベース認証の場合、セッションがタイムアウトするか無効になるまでを意味します。BASIC認証の場合、ユーザーがブラウザを閉じるまでを意味します)。キャッシュされたユーザーは、セッションのシリアル化をまたいで保存および復元されることはありません。既に認証されたユーザーのデータベース情報への変更は、そのユーザーが次回再びログインするまで反映されません。
- usersテーブルおよびuser rolesテーブルの情報の管理は、ご自身のアプリケーションの責任です。Tomcatは、ユーザーとロールを維持するための組み込み機能を提供していません。
JNDIRealm
はじめに
JNDIRealmは、JNDIプロバイダ(通常、JNDI APIクラスで利用可能な標準LDAPプロバイダ)を介してアクセスされるLDAPディレクトリサーバーでユーザーを検索するTomcat Realm
インターフェースの実装です。このRealmは、ディレクトリを認証に使用するための様々なアプローチをサポートしています。
ディレクトリへの接続
Realmのディレクトリへの接続は、connectionURL構成属性によって定義されます。これはJNDIプロバイダによってフォーマットが定義されたURLです。通常は、接続先のディレクトリサーバーのドメイン名、オプションでポート番号と必要なルートネーミングコンテキストの識別名(DN)を指定するLDAP URLです。
プロバイダが複数ある場合、alternateURLを設定できます。connectionURLにあるプロバイダへのソケット接続が確立できない場合、alternateURLを使用する試みが行われます。
ディレクトリを検索し、ユーザーおよびロール情報を取得するために接続を行う際、RealmはconnectionNameおよびconnectionPasswordプロパティで指定されたユーザー名とパスワードを使用してディレクトリに自身を認証します。これらのプロパティが指定されていない場合、接続は匿名になります。これは多くの場合で十分です。
ユーザーのディレクトリエントリの選択
認証可能な各ユーザーは、connectionURL属性によって定義された初期のDirContext
内の要素に対応する個別のエントリとしてディレクトリに表現されている必要があります。このユーザーエントリには、認証のために提示されるユーザー名を含む属性が必要です。
多くの場合、ユーザーエントリの識別名には認証のために提示されるユーザー名が含まれますが、それ以外はすべてのユーザーで同じです。この場合、userPattern属性を使用してDNを指定でき、「{0}」はユーザー名が置換される場所を示します。
そうでない場合、Realmはディレクトリを検索して、ユーザー名を含む一意のエントリを見つける必要があります。以下の属性がこの検索を設定します。
- userBase - ユーザーを含むサブツリーのベースとなるエントリ。指定されていない場合、検索ベースはトップレベルコンテキストになります。
- userSubtree - 検索スコープ。userBaseエントリをルートとするサブツリー全体を検索したい場合は
true
に設定します。デフォルト値のfalse
は、トップレベルのみを含む単一レベル検索を要求します。 - userSearch - ユーザー名の置換後に使用するLDAP検索フィルターを指定するパターン。
ユーザーの認証
-
バインドモード
デフォルトでは、RealmはユーザーのエントリのDNとユーザーが提示したパスワードを使用してディレクトリにバインドすることでユーザーを認証します。この単純なバインドが成功した場合、ユーザーは認証されたと見なされます。
セキュリティ上の理由から、ディレクトリはユーザーのパスワードのクリアテキストバージョンではなく、ダイジェストを保存する場合があります(詳細についてはダイジェストパスワードを参照してください)。その場合、単純なバインド操作の一部として、ディレクトリはユーザーが提示したプレーンテキストパスワードの正しいダイジェストを自動的に計算し、保存された値に対して検証します。したがって、バインドモードでは、Realmはダイジェスト処理に関与しません。digest属性は使用されず、設定されていても無視されます。
-
比較モード
あるいは、Realmはディレクトリから保存されたパスワードを取得し、ユーザーが提示した値と明示的に比較することもできます。このモードは、userPassword属性を、ユーザーのエントリ内でパスワードを含むディレクトリ属性の名前に設定することで構成されます。
比較モードにはいくつかの欠点があります。まず、connectionNameおよびconnectionPassword属性は、Realmがディレクトリ内のユーザーのパスワードを読み取れるように設定する必要があります。セキュリティ上の理由から、これは一般的に望ましくありません。実際、多くのディレクトリ実装では、ディレクトリマネージャーでさえこれらのパスワードを読み取ることを許可しません。さらに、Realmはパスワードダイジェスト自体を処理する必要があり、使用されるアルゴリズムのバリエーションやディレクトリでのパスワードハッシュの表現方法も含まれます。ただし、Realmは、HTTPダイジェストアクセス認証(RFC 2069)をサポートするためなど、保存されたパスワードへのアクセスが必要になる場合があります。(上記のユーザー情報のリポジトリでのパスワードダイジェストの保存とは、HTTPダイジェスト認証が異なることに注意してください)。
ユーザーへのロールの割り当て
ディレクトリRealmは、ディレクトリ内のロールの表現に対して2つのアプローチをサポートしています。
-
明示的なディレクトリエントリとしてのロール
ロールは、明示的なディレクトリエントリによって表現できます。ロールエントリは通常、ロール名を含む1つの属性と、そのロール内のユーザーの識別名またはユーザー名が値となる別の属性を持つLDAPグループエントリです。以下の属性は、認証されたユーザーに関連付けられたロールの名前を見つけるためのディレクトリ検索を設定します。
- roleBase - ロール検索のベースエントリ。指定されていない場合、検索ベースはトップレベルディレクトリコンテキストになります。
- roleSubtree - 検索スコープ。
roleBase
エントリをルートとするサブツリー全体を検索したい場合はtrue
に設定します。デフォルト値のfalse
は、トップレベルのみを含む単一レベル検索を要求します。 - roleSearch - ロールエントリを選択するためのLDAP検索フィルター。認証されたユーザーの識別名に対して「{0}」、ユーザー名に対して「{1}」、および/またはユーザーのディレクトリエントリからの属性に対して「{2}」のパターン置換をオプションで含みます。「{2}」の値を提供する属性名を指定するにはuserRoleAttributeを使用します。
- roleName - ロールエントリ内の、そのロールの名前を含む属性。
- roleNested - ネストされたロールを有効にします。ロール内にロールをネストしたい場合は
true
に設定します。設定されている場合、新しく見つかったすべてのroleNameとdistinguished Nameは、新しいロール検索のために再帰的に試行されます。デフォルト値はfalse
です。
-
ユーザーエントリの属性としてのロール
ロール名は、ユーザーのディレクトリエントリ内の属性の値としても保持できます。この属性名を指定するには、userRoleNameを使用します。
ロール表現の両方のアプローチの組み合わせを使用できます。
クイックスタート
TomcatがJNDIRealmを使用するように設定するには、以下の手順に従う必要があります。
- ディレクトリサーバーが上記の要件に合致するスキーマで構成されていることを確認してください。
- 必要に応じて、Tomcatが使用するためのユーザー名とパスワードを設定してください。これは、上記の情報に対する読み取り専用アクセス権を持っている必要があります。(Tomcatがこの情報を変更しようとすることはありません。)
- 以下で説明するように、
$CATALINA_BASE/conf/server.xml
ファイルに<Realm>
要素を設定してください。 - Tomcatが既に実行中の場合は、再起動してください。
Realm要素の属性
JNDIRealmを設定するには、<Realm>
要素を作成し、上記で説明したように、$CATALINA_BASE/conf/server.xml
ファイルにネストします。JNDIRealmの属性は、Realm設定ドキュメントで定義されています。
例
ディレクトリサーバーに適切なスキーマを作成することは、各ディレクトリサーバーの実装に固有のものであるため、このドキュメントの範囲外です。以下の例では、https://www.openldap.orgからダウンロードできるOpenLDAPディレクトリサーバー(バージョン2.0.11以降)のディストリビューションを使用していると仮定します。slapd.conf
ファイルに以下の設定(その他を含む)が含まれていると仮定します。
database ldbm
suffix dc="mycompany",dc="com"
rootdn "cn=Manager,dc=mycompany,dc=com"
rootpw secret
connectionURL
については、ディレクトリサーバーがTomcatと同じマシンで実行されていると仮定します。JNDI LDAPプロバイダーの設定と使用に関する詳細については、http://docs.oracle.com/javase/7/docs/technotes/guides/jndi/index.htmlを参照してください。
次に、このディレクトリサーバーに以下に示す要素(LDIF形式)が入力されていると仮定します。
# Define top-level entry
dn: dc=mycompany,dc=com
objectClass: dcObject
dc:mycompany
# Define an entry to contain people
# searches for users are based on this entry
dn: ou=people,dc=mycompany,dc=com
objectClass: organizationalUnit
ou: people
# Define a user entry for Janet Jones
dn: uid=jjones,ou=people,dc=mycompany,dc=com
objectClass: inetOrgPerson
uid: jjones
sn: jones
cn: janet jones
mail: j.jones@mycompany.com
userPassword: janet
# Define a user entry for Fred Bloggs
dn: uid=fbloggs,ou=people,dc=mycompany,dc=com
objectClass: inetOrgPerson
uid: fbloggs
sn: bloggs
cn: fred bloggs
mail: f.bloggs@mycompany.com
userPassword: fred
# Define an entry to contain LDAP groups
# searches for roles are based on this entry
dn: ou=groups,dc=mycompany,dc=com
objectClass: organizationalUnit
ou: groups
# Define an entry for the "tomcat" role
dn: cn=tomcat,ou=groups,dc=mycompany,dc=com
objectClass: groupOfUniqueNames
cn: tomcat
uniqueMember: uid=jjones,ou=people,dc=mycompany,dc=com
uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com
# Define an entry for the "role1" role
dn: cn=role1,ou=groups,dc=mycompany,dc=com
objectClass: groupOfUniqueNames
cn: role1
uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com
上記のように設定されたOpenLDAPディレクトリサーバーのRealm
要素の例は、ユーザーがアプリケーションにログインする際に自身のuid(例: jjones)を使用し、ディレクトリを検索してロール情報を取得するのに匿名接続で十分であると仮定すると、次のようになります。
<Realm className="org.apache.catalina.realm.JNDIRealm"
connectionURL="ldap://:389"
userPattern="uid={0},ou=people,dc=mycompany,dc=com"
roleBase="ou=groups,dc=mycompany,dc=com"
roleName="cn"
roleSearch="(uniqueMember={0})"
/>
この構成により、Realmはユーザー名をuserPattern
に代入してユーザーの識別名を決定し、このDNとユーザーから受け取ったパスワードを使用してディレクトリにバインドすることで認証し、ディレクトリを検索してユーザーのロールを見つけます。
ここで、ユーザーがログイン時にユーザーIDではなくメールアドレスを入力することを想定してみましょう。この場合、Realmはディレクトリ内でユーザーのエントリを検索する必要があります(異なる組織単位や会社の場所に相当する複数のサブツリーにユーザーエントリが保持されている場合も検索が必要です)。
さらに、グループエントリに加えて、ユーザーエントリの属性を使用してロールを保持したいと仮定します。この場合、ジャネット・ジョーンズのエントリは次のようになります。
dn: uid=jjones,ou=people,dc=mycompany,dc=com
objectClass: inetOrgPerson
uid: jjones
sn: jones
cn: janet jones
mail: j.jones@mycompany.com
memberOf: role2
memberOf: role3
userPassword: janet
このRealm構成は、新しい要件を満たすでしょう。
<Realm className="org.apache.catalina.realm.JNDIRealm"
connectionURL="ldap://:389"
userBase="ou=people,dc=mycompany,dc=com"
userSearch="(mail={0})"
userRoleName="memberOf"
roleBase="ou=groups,dc=mycompany,dc=com"
roleName="cn"
roleSearch="(uniqueMember={0})"
/>
ジャネット・ジョーンズが「j.jones@mycompany.com」としてログインすると、Realmはディレクトリ内でその値をメール属性とする一意のエントリを検索し、与えられたパスワードでuid=jjones,ou=people,dc=mycompany,dc=com
としてディレクトリにバインドを試みます。認証が成功した場合、彼女には3つのロールが割り当てられます。それは、彼女のディレクトリエントリの「memberOf」属性の値である「role2」と「role3」、そして彼女が唯一のメンバーであるグループエントリの「cn」属性の値である「tomcat」です。
最後に、ディレクトリからパスワードを取得し、Realm内でローカル比較を行うことでユーザーを認証するには、このようなRealm構成を使用することができます。
<Realm className="org.apache.catalina.realm.JNDIRealm"
connectionName="cn=Manager,dc=mycompany,dc=com"
connectionPassword="secret"
connectionURL="ldap://:389"
userPassword="userPassword"
userPattern="uid={0},ou=people,dc=mycompany,dc=com"
roleBase="ou=groups,dc=mycompany,dc=com"
roleName="cn"
roleSearch="(uniqueMember={0})"
/>
しかし、上記で述べたように、認証のデフォルトのバインドモードが通常推奨されます。
追加の注意事項
JNDIRealmは以下のルールに従って動作します。
- ユーザーが保護されたリソースに初めてアクセスしようとすると、Tomcatはこの
Realm
のauthenticate()
メソッドを呼び出します。したがって、ディレクトリに直接加えた変更(新規ユーザー、パスワードやロールの変更など)は直ちに反映されます。 - ユーザーが認証されると、そのユーザー(および関連するロール)は、ユーザーのログイン期間中Tomcat内にキャッシュされます。(FORMベース認証の場合、セッションがタイムアウトするか無効になるまでを意味します。BASIC認証の場合、ユーザーがブラウザを閉じるまでを意味します)。キャッシュされたユーザーは、セッションのシリアル化をまたいで保存および復元されることはありません。既に認証されたユーザーのディレクトリ情報への変更は、そのユーザーが次回再びログインするまで反映されません。
- ディレクトリサーバー内の情報の管理は、ご自身のアプリケーションの責任です。Tomcatは、ユーザーとロールを維持するための組み込み機能を提供していません。
UserDatabaseRealm
はじめに
UserDatabaseRealmは、JNDIリソースを使用してユーザー情報を保存するTomcat Realm
インターフェースの実装です。デフォルトでは、JNDIリソースはXMLファイルによってバックアップされます。大規模な本番環境での使用には設計されていません。起動時に、UserDatabaseRealmはすべてのユーザーとその対応するロールに関する情報をXMLドキュメント(デフォルトでは、このドキュメントは$CATALINA_BASE/conf/tomcat-users.xml
から読み込まれます)からロードします。ユーザー、パスワード、およびロールはすべて、通常JMXを介して動的に編集できます。変更は保存され、XMLファイルに反映されます。
Realm要素の属性
UserDatabaseRealmを設定するには、<Realm>
要素を作成し、上記で説明したように、$CATALINA_BASE/conf/server.xml
ファイルにネストします。UserDatabaseRealmの属性は、Realm設定ドキュメントで定義されています。
ユーザーファイル形式
XMLファイルベースのUserDatabase
の場合、ユーザーファイルはMemoryRealmと同じ形式を使用します。
例
Tomcatのデフォルトインストールでは、<Engine>
要素内にUserDatabaseRealmがネストされており、すべての仮想ホストとWebアプリケーションに適用されます。conf/tomcat-users.xml
ファイルのデフォルトの内容は以下のとおりです。
<tomcat-users>
<user username="tomcat" password="tomcat" roles="tomcat" />
<user username="role1" password="tomcat" roles="role1" />
<user username="both" password="tomcat" roles="tomcat,role1" />
</tomcat-users>
追加の注意事項
UserDatabaseRealmは以下のルールに従って動作します。
- Tomcatが最初に起動する際、定義されたすべてのユーザーとその関連情報をユーザーファイルからロードします。このファイルのデータに加えられた変更は、Tomcatが再起動されるまで認識されません。変更はUserDatabaseリソースを介して行うことができます。Tomcatは、この目的のためにJMXを介してアクセスできるMBeansを提供しています。
- ユーザーが保護されたリソースに初めてアクセスしようとすると、Tomcatはこの
Realm
のauthenticate()
メソッドを呼び出します。 - ユーザーが認証されると、そのユーザーはログイン期間中Tomcat内に関連付けられます。(FORMベース認証の場合、セッションがタイムアウトするか無効になるまでを意味します。BASIC認証の場合、ユーザーがブラウザを閉じるまでを意味します)。ただし、ユーザーロールは他のRealmとは異なり、
UserDatabase
の内容を反映し続けます。ユーザーがデータベースから削除された場合、そのユーザーはロールを持たないと見なされます。UserDatabaseRealm
のuseStaticPrincipal
属性を使用すると、ユーザーをすべてのロールとともにキャッシュすることができます。キャッシュされたユーザーは、セッションのシリアル化をまたいで保存および復元されることはありません。ユーザーのプリンシパルオブジェクトが何らかの理由でシリアル化された場合、そのオブジェクトは、データベースの内容を反映しなくなるロールを持つ静的な同等オブジェクトに置き換えられます。
MemoryRealm
はじめに
MemoryRealmは、Tomcat Realm
インターフェースの単純なデモンストレーション実装です。本番環境での使用には設計されていません。起動時に、MemoryRealmはすべてのユーザーとその対応するロールに関する情報をXMLドキュメント(デフォルトでは、このドキュメントは$CATALINA_BASE/conf/tomcat-users.xml
から読み込まれます)からロードします。このファイルのデータへの変更は、Tomcatが再起動されるまで認識されません。
Realm要素の属性
MemoryRealmを設定するには、<Realm>
要素を作成し、上記で説明したように、$CATALINA_BASE/conf/server.xml
ファイルにネストします。MemoryRealmの属性は、Realm設定ドキュメントで定義されています。
ユーザーファイル形式
ユーザーファイル(デフォルトではconf/tomcat-users.xml
)は、ルート要素<tomcat-users>
を持つXMLドキュメントである必要があります。ルート要素の内部には、有効なユーザーごとに<user>
要素がネストされ、以下の属性で構成されます。
- name - このユーザーがログインに使用するユーザー名。
- password - このユーザーがログインに使用するパスワード(
<Realm>
要素にdigest
属性が設定されていない場合はクリアテキスト、そうでない場合はこちらで説明されているように適切にダイジェスト化されたもの)。 - roles - このユーザーに関連付けられたロール名のカンマ区切りリスト。
追加の注意事項
MemoryRealmは以下のルールに従って動作します。
- Tomcatが最初に起動する際、定義されたすべてのユーザーとその関連情報をユーザーファイルからロードします。このファイルのデータへの変更は、Tomcatが再起動されるまで認識されません。
- ユーザーが保護されたリソースに初めてアクセスしようとすると、Tomcatはこの
Realm
のauthenticate()
メソッドを呼び出します。 - ユーザーが認証されると、そのユーザー(および関連するロール)は、ユーザーのログイン期間中Tomcat内にキャッシュされます。(FORMベース認証の場合、セッションがタイムアウトするか無効になるまでを意味します。BASIC認証の場合、ユーザーがブラウザを閉じるまでを意味します)。キャッシュされたユーザーは、セッションのシリアル化をまたいで保存および復元されることはありません。
- ユーザーファイル内の情報の管理は、ご自身のアプリケーションの責任です。Tomcatは、ユーザーとロールを維持するための組み込み機能を提供していません。
JAASRealm
はじめに
JAASRealmは、標準のJava SE APIの一部として提供されるJava Authentication & Authorization Service (JAAS) フレームワークを介してユーザーを認証するTomcat Realm
インターフェースの実装です。
JAASRealmを使用すると、開発者は事実上考えられるあらゆるセキュリティRealmをTomcatのCMAと組み合わせる能力を得られます。
JAASRealmは、コンテナ管理セキュリティを強化し、実装がコンテナに依存しない「プラグ可能」な認証メカニズムを促進するためのJCP 仕様要求 196に基づいた、J2EE v1.4向けのJAASベースJ2EE認証フレームワークのTomcat用プロトタイプです。
JAASログインモジュールとプリンシパル(javax.security.auth.spi.LoginModule
およびjavax.security.Principal
を参照)に基づいて、独自のセキュリティメカニズムを開発したり、別のサードパーティメカニズムをTomcatによって実装されたCMAとの統合のためにラップしたりできます。
クイックスタート
Tomcatで独自のJAASログインモジュールを使用するようにJAASRealmを設定するには、以下の手順に従う必要があります。
- JAASベースの独自のLoginModule、User、Roleクラスを作成し(JAAS認証チュートリアルおよびJAASログインモジュール開発者ガイドを参照)、これらをJAAS Login Context(
javax.security.auth.login.LoginContext
)によって管理されるようにします。LoginModuleを開発する際、JAASRealmの組み込みCallbackHandler
は現在、NameCallback
とPasswordCallback
のみを認識することに注意してください。 - JAASでは指定されていませんが、Tomcatがログインモジュールから返されるプリンシパルのうちどれがユーザーでどれがロールかを区別できるように、ユーザーとロールを区別する個別のクラスを作成し、
javax.security.Principal
を拡張する必要があります(org.apache.catalina.realm.JAASRealm
を参照)。いずれにせよ、最初に返されたプリンシパルは常にユーザープリンシパルとして扱われます。 - コンパイル済みのクラスをTomcatのクラスパスに配置する
- Java用のlogin.configファイルを設定し(JAAS LoginConfigファイルを参照)、その場所をJVMに指定することでTomcatにファイルの場所を教えます。例えば、環境変数に以下を設定します:
JAVA_OPTS=$JAVA_OPTS -Djava.security.auth.login.config==$CATALINA_BASE/conf/jaas.config
- 保護したいリソースについて、web.xmlにセキュリティ制約を設定する
- server.xmlにJAASRealmモジュールを設定する
- Tomcatが既に実行中の場合は、再起動してください。
Realm要素の属性
上記のステップ6のようにJAASRealmを設定するには、<Realm>
要素を作成し、<Engine>
ノード内の$CATALINA_BASE/conf/server.xml
ファイルにネストします。JAASRealmの属性は、Realm設定ドキュメントで定義されています。
例
server.xmlのスニペットがどのように見えるかの例を以下に示します。
<Realm className="org.apache.catalina.realm.JAASRealm"
appName="MyFooRealm"
userClassNames="org.foobar.realm.FooUser"
roleClassNames="org.foobar.realm.FooRole"/>
ユーザー(javax.security.auth.Subject
)のプリンシパルを表すUserおよびRoleオブジェクトを作成して保存するのは、ログインモジュールの責任です。ログインモジュールがユーザーオブジェクトを作成せず、かつログイン例外もスローしない場合、Tomcat CMAは機能しなくなり、https://:8080/myapp/j_security_check URIまたはその他の未指定の場所にリダイレクトされます。
JAASアプローチの柔軟性は2つあります。
- 独自のログインモジュールで必要な処理をバックグラウンドで実行できます。
- アプリケーションのコードを変更することなく、設定を変更してサーバーを再起動するだけで、まったく異なるLoginModuleをプラグインできます。
追加の注意事項
- ユーザーが保護されたリソースに初めてアクセスしようとすると、Tomcatはこの
Realm
のauthenticate()
メソッドを呼び出します。したがって、セキュリティメカニズムに直接加えた変更(新規ユーザー、パスワードやロールの変更など)は直ちに反映されます。 - ユーザーが認証されると、そのユーザー(および関連するロール)は、ユーザーのログイン期間中Tomcat内にキャッシュされます。FORMベース認証の場合、セッションがタイムアウトするか無効になるまでを意味します。BASIC認証の場合、ユーザーがブラウザを閉じるまでを意味します。既に認証されたユーザーのセキュリティ情報への変更は、そのユーザーが次回再びログインするまで反映されません。
- 他の
Realm
実装と同様に、server.xml
の<Realm>
要素にdigest
属性が含まれている場合、ダイジェストパスワードがサポートされます。JAASRealmのCallbackHandler
は、パスワードをLoginModule
に渡す前にダイジェスト化します。
CombinedRealm
はじめに
CombinedRealmは、1つ以上のサブRealmを介してユーザーを認証するTomcat Realm
インターフェースの実装です。
CombinedRealmを使用すると、開発者は同じまたは異なるタイプの複数のRealmを組み合わせる能力を得られます。これは、異なるソースに対して認証したり、あるRealmが失敗した場合にフォールバックを提供したり、複数のRealmを必要とするその他の目的のために使用できます。
サブRealmは、CombinedRealmを定義するRealm
要素内にRealm
要素をネストすることによって定義されます。認証は、リストされている順序で各Realm
に対して試行されます。いずれかのRealmに対する認証が、ユーザーを認証するのに十分です。
Realm要素の属性
CombinedRealmを設定するには、<Realm>
要素を作成し、$CATALINA_BASE/conf/server.xml
ファイル内の<Engine>
または<Host>
内にネストします。context.xml
ファイル内の<Context>
ノード内にネストすることもできます。
例
以下は、UserDatabase RealmとDataSource Realmを使用するためにserver.xmlのスニペットがどのように見えるかの例です。
<Realm className="org.apache.catalina.realm.CombinedRealm" >
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<Realm className="org.apache.catalina.realm.DataSourceRealm"
dataSourceName="jdbc/authority"
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name"/>
</Realm>
LockOutRealm
はじめに
LockOutRealmは、CombinedRealmを拡張し、一定期間内に認証試行が多すぎる場合にユーザーロックアウトメカニズムを提供するロックアウト機能を提供するTomcat Realm
インターフェースの実装です。
正しい動作を保証するため、このRealmには合理的な程度の同期があります。
このRealmは、基盤となるRealmや関連するユーザー格納メカニズムの変更を必要としません。存在しないユーザーに対するログイン失敗も含め、すべての失敗したログインを記録することでこれを実現します。無効なユーザーで意図的にリクエストを行うことによるDOS攻撃(およびそれによってこのキャッシュが肥大化すること)を防ぐため、認証に失敗したユーザーのリストのサイズは制限されています。
サブRealmは、LockOutRealmを定義するRealm
要素内にRealm
要素をネストすることによって定義されます。認証は、リストされている順序で各Realm
に対して試行されます。いずれかのRealmに対する認証が、ユーザーを認証するのに十分です。
Realm要素の属性
LockOutRealmを設定するには、<Realm>
要素を作成し、$CATALINA_BASE/conf/server.xml
ファイル内の<Engine>
または<Host>
内にネストします。context.xml
ファイル内の<Context>
ノード内にネストすることもできます。LockOutRealmの属性は、Realm設定ドキュメントで定義されています。
例
以下は、UserDatabase Realmにロックアウト機能を追加するためのserver.xmlスニペットがどのように見えるかの例です。
<Realm className="org.apache.catalina.realm.LockOutRealm" >
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>