Realm 設定方法

目次

クイックスタート

このドキュメントでは、既存のユーザー名、パスワード、ユーザーロールの「データベース」に接続することにより、コンテナ管理セキュリティをサポートするようにTomcatを設定する方法について説明します。これは、1つ以上の<security-constraint>要素と、ユーザーが自身を認証する必要がある方法を定義する<login-config>要素を含むWebアプリケーションを使用している場合にのみ重要です。これらの機能を使用していない場合は、このドキュメントをスキップしても安全です。

コンテナ管理セキュリティに関する基本的な背景情報については、Servlet 仕様(バージョン 2.4)のセクション12を参照してください。

Tomcatのシングルサインオン機能(ユーザーが仮想ホストに関連付けられたWebアプリケーション全体の1回限りの認証を可能にする)の使用方法については、こちらを参照してください。

概要

Realm とは何ですか?

Realm は、Webアプリケーション(またはWebアプリケーションのセット)の有効なユーザーを識別するユーザー名とパスワードの「データベース」、および各有効ユーザーに関連付けられたロールのリストです。Unixライクなオペレーティングシステムのグループと同様に、特定のWebアプリケーションリソースへのアクセスは、関連付けられたユーザー名のリストを列挙するのではなく、特定のロールを持つすべてのユーザーに付与されます。特定のユーザーには、ユーザー名に関連付けられた任意の数のロールを含めることができます。

Servlet仕様では、アプリケーションがセキュリティ要件を(web.xml展開記述子で)宣言するためのポータブルなメカニズムについて説明していますが、サーブレットコンテナと関連付けられたユーザーおよびロール情報の間のインターフェースを定義するポータブルなAPIはありません。ただし、多くの場合、運用環境に既に存在する既存の認証データベースまたはメカニズムにサーブレットコンテナを「接続」することが望ましいです。そのため、TomcatはJavaインターフェース(org.apache.catalina.Realm)を定義しており、これは「プラグイン」コンポーネントによって実装され、この接続を確立できます。6つの標準プラグインが提供されており、さまざまな認証情報ソースへの接続をサポートしています。

  • DataSourceRealm - 名前付きJNDI JDBCデータソースを介してアクセスされるリレーショナルデータベースに格納されている認証情報にアクセスします。
  • JNDIRealm - JNDIプロバイダーを介してアクセスされるLDAPベースのディレクトリサーバーに格納されている認証情報にアクセスします。
  • UserDatabaseRealm - 通常はXMLドキュメント(conf/tomcat-users.xml)によってバックアップされるUserDatabase JNDIリソースに格納されている認証情報にアクセスします。
  • MemoryRealm - XMLドキュメント(conf/tomcat-users.xml)から初期化されるインメモリオブジェクトコレクションに格納されている認証情報にアクセスします。
  • JAASRealm - Java Authentication & Authorization Service(JAAS)フレームワークを介して認証情報にアクセスします。

独自のRealm実装を作成し、Tomcatと統合することも可能です。そのためには、

  • org.apache.catalina.Realmを実装する必要があります。
  • $CATALINA_HOME/libにコンパイル済みrealmを配置する必要があります。
  • 下記の「Realmの設定」セクションに記載されているように、realmを宣言する必要があります。
  • MBeans記述子にrealmを宣言する必要があります。

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)によってサポートされているダイジェストアルゴリズムの1つに設定する必要があります。このオプションを選択した場合、Realmに格納されているパスワードの内容は、指定されたアルゴリズムでダイジェストされたパスワードのクリアテキストバージョンである必要があります。

Realmのauthenticate()メソッドが呼び出されると、ユーザーによって指定された(クリアテキスト)パスワードは同じアルゴリズムでダイジェストされ、その結果はRealmによって返された値と比較されます。一致する場合は、元のパスワードのクリアテキストバージョンがユーザーによって提示されたものと同じであることを意味するため、このユーザーを認証する必要があります。

クリアテキストパスワードのダイジェスト値を計算するには、2つの便利な手法がサポートされています。

  • ダイジェストパスワードを動的に計算する必要があるアプリケーションを作成する場合は、クリアテキストパスワード、ダイジェストアルゴリズム名、エンコーディングを引数として渡して、org.apache.catalina.realm.RealmBaseクラスの静的Digest()メソッドを呼び出します。このメソッドは、ダイジェストされたパスワードを返します。
  • コマンドラインユーティリティを実行してダイジェストパスワードを計算する場合は、次のように実行します。
    CATALINA_HOME/bin/digest.[bat|sh] -a {algorithm} {cleartext-password}
    このクリアテキストパスワードのダイジェストバージョンは標準出力に返されます。

DIGEST認証でダイジェストパスワードを使用する場合は、ダイジェストを生成するために使用されるクリアテキストが異なり、ダイジェストはソルトを使用せずに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に付属するサンプルアプリケーションには、セキュリティ制約によって保護され、フォームベースのログインを使用する領域が含まれています。http://localhost: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 ログ

Realmによってログに記録されたデバッグメッセージと例外メッセージは、realmのコンテナ(それを囲むContextHost、またはEngine)に関連付けられたロギング構成によって記録されます。

標準 Realm 実装

DataSourceRealm

はじめに

DataSourceRealmは、JNDI名前付きJDBCデータソースを介してアクセスされるリレーショナルデータベースでユーザーを検索するTomcat Realmインターフェースの実装です。データベース構造が次の要件を満たしている限り、既存のテーブル名と列名に適応できるかなりの設定柔軟性があります。

  • このRealmが認識すべき有効なユーザーごとに1行を含む、以下でusersテーブルと参照されるテーブルが必要です。
  • usersテーブルには、少なくとも2つの列を含める必要があります(既存のアプリケーションで必要であれば、さらに多くの列を含めることができます)。
    • ユーザーがログインしたときにTomcatが認識するユーザー名。
    • ユーザーがログインしたときにTomcatが認識するパスワード。この値はクリアテキストまたはダイジェストにすることができます - 詳細については下記を参照してください。
  • 特定のユーザーに割り当てられた有効なロールごとに1行を含む、以下でユーザーロールテーブルと参照されるテーブルが必要です。ユーザーは、0個、1個、または複数の有効なロールを持つことができます。
  • ユーザーロールテーブルには、少なくとも2つの列を含める必要があります(既存のアプリケーションで必要であれば、さらに多くの列を含めることができます)。
    • Tomcatが認識するユーザー名(usersテーブルで指定されている値と同じ)。
    • このユーザーに関連付けられた有効なロールのロール名。
クイックスタート

DataSourceRealmを使用するようにTomcatを設定するには、次の手順に従ってください。

  1. まだ行っていない場合は、上記の要件に準拠したテーブルと列をデータベースに作成します。
  2. 上記のテーブルへの少なくとも読み取り専用のアクセス権を持つ、Tomcatが使用するデータベースのユーザー名とパスワードを設定します。(Tomcatはこれらのテーブルへの書き込みを試行することはありません)。
  3. データベースのJNDI名前付きJDBCデータソースを設定します。JNDI名前付きJDBCデータソースの設定方法については、JNDIデータソースの例に関するハウツーを参照してください。JNDIデータソースが定義されている場所に応じて、RealmlocalDataSource属性を適切に設定してください。
  4. 以下のように記述されている<Realm>要素を、$CATALINA_BASE/conf/server.xmlファイルに設定します。
  5. 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データソースでアクセスする例です。

<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はこのRealmauthenticate()メソッドを呼び出します。したがって、データベースに対して直接行った変更(新しいユーザー、パスワードまたはロールの変更など)は、すぐに反映されます。
  • ユーザーが認証されると、ユーザー(および関連付けられたロール)は、ユーザーのログイン期間中、Tomcat内にキャッシュされます。(フォームベースの認証の場合、セッションのタイムアウトまたは無効化まで。BASIC認証の場合、ユーザーがブラウザを閉じるまで)。キャッシュされたユーザーはセッションのシリアル化全体で保存および復元されません。既に認証されたユーザーのデータベース情報の変更は、そのユーザーが次回ログインするまで反映されません
  • usersテーブルとユーザーロールテーブルの情報管理は、独自のアプリケーションの責任です。Tomcatは、ユーザーとロールを維持するための組み込み機能を提供しません。

JNDIRealm

はじめに

JNDIRealmは、JNDIプロバイダー(通常はJNDI APIクラスで使用可能な標準LDAPプロバイダー)によってアクセスされるLDAPディレクトリサーバーでユーザーを検索するTomcat Realmインターフェースの実装です。このrealmは、認証にディレクトリを使用するためのさまざまなアプローチをサポートしています。

ディレクトリへの接続

realmのディレクトリへの接続は、connectionURL設定属性によって定義されます。これは、JNDIプロバイダーによって形式が定義されるURLです。通常は、接続するディレクトリサーバーのドメイン名、およびオプションでポート番号と必要なルートネーミングコンテキストの識別名(DN)を指定するLDAP URLです。

複数のプロバイダーがある場合は、alternateURLを設定できます。connectionURLのプロバイダーへのソケット接続ができない場合、alternateURLを使用しようとします。

ディレクトリを検索してユーザーとロール情報を取得するために接続を作成する際、realmは、connectionNameconnectionPasswordプロパティで指定されたユーザー名とパスワードを使用してディレクトリに対して自身を認証します。これらのプロパティが指定されていない場合、接続は匿名です。多くの場合、これで十分です。

ユーザーのディレクトリエントリの選択

認証できる各ユーザーは、connectionURL属性によって定義された初期DirContextの要素に対応する個々のエントリによってディレクトリに表される必要があります。このユーザーエントリには、認証のために提示されるユーザー名を含む属性が必要です。

多くの場合、ユーザーのエントリの識別名には、認証のために提示されるユーザー名が含まれていますが、それ以外はすべてのユーザーで同じです。この場合、userPattern属性を使用してDNを指定でき、 "{0}" はユーザー名を置換する必要がある場所を示します。

それ以外の場合は、realmはディレクトリを検索して、ユーザー名を含む一意のエントリを見つける必要があります。次の属性は、この検索を設定します。

  • userBase - ユーザーを含むサブツリーのベースとなるエントリ。指定されていない場合、検索ベースは最上位のコンテキストです。
  • userSubtree - 検索範囲。userBaseエントリにルート付けされたサブツリー全体を検索する場合はtrueに設定します。デフォルト値のfalseは、最上位レベルのみを含む単一レベルの検索を要求します。
  • userSearch - ユーザー名を置換した後に使用するLDAP検索フィルターを指定するパターン。
ユーザーの認証
  • バインドモード

    デフォルトでは、realmはそのユーザーのエントリのDNとユーザーによって提示されたパスワードを使用してディレクトリにバインドすることでユーザーを認証します。この単純なバインドが成功した場合、ユーザーは認証されたと見なされます。

    セキュリティ上の理由から、ディレクトリはクリアテキストバージョンではなく、ユーザーのパスワードのダイジェストを保存する場合があります(詳細についてはダイジェストパスワードを参照)。その場合、単純なバインド操作の一部として、ディレクトリはユーザーによって提示されたプレーンテキストパスワードの正しいダイジェストを自動的に計算してから、保存された値に対して検証します。したがって、バインドモードでは、realmはダイジェスト処理に関与しません。digest属性は使用されず、設定されていても無視されます。

  • 比較モード

    あるいは、realmはディレクトリから保存されたパスワードを取得し、ユーザーによって提示された値と明示的に比較できます。このモードは、userPassword属性を、パスワードを含むユーザーのエントリのディレクトリ属性の名前に設定することで設定されます。

    比較モードにはいくつかの欠点があります。まず、realmがディレクトリ内のユーザーのパスワードを読み取ることができるように、connectionNameconnectionPassword属性を設定する必要があります。セキュリティ上の理由から、これは一般的に望ましくありません。実際、多くのディレクトリ実装では、ディレクトリマネージャーでさえこれらのパスワードを読み取ることができません。さらに、realmは、使用されるアルゴリズムとディレクトリ内のパスワードハッシュの表現方法のバリエーションを含む、パスワードダイジェスト自体を処理する必要があります。ただし、realmは、HTTPダイジェストアクセス認証(RFC 2069)をサポートするために、保存されたパスワードへのアクセスが必要になる場合があります。(HTTPダイジェスト認証は、上記のようにユーザー情報のレポジトリにパスワードダイジェストを格納することとは異なります)。

ユーザーへのロールの割り当て

ディレクトリrealmは、ディレクトリ内のロールの表現に2つのアプローチをサポートしています。

  • 明示的なディレクトリエントリとしてのロール

    ロールは、明示的なディレクトリエントリで表すことができます。ロールエントリは通常、ロールの名前を含む属性と、そのロールのユーザーの識別名またはユーザー名である値を含む別の属性を持つLDAPグループエントリです。次の属性は、認証されたユーザーに関連付けられたロールの名前を見つけるためのディレクトリ検索を設定します。

    • roleBase - ロール検索のベースエントリ。指定されていない場合、検索ベースは最上位のディレクトリコンテキストです。
    • roleSubtree - 検索範囲。roleBaseエントリにルート付けされたサブツリー全体を検索する場合はtrueに設定します。デフォルト値のfalseは、最上位レベルのみを含む単一レベルの検索を要求します。
    • roleSearch - ロールエントリを選択するためのLDAP検索フィルター。オプションで、認証されたユーザーの識別名、ユーザー名、ユーザーのディレクトリエントリの属性の "{2}" のパターン置換 "{0}"、"{1}" を含みます。userRoleAttributeを使用して、"{2}" の値を提供する属性の名前を指定します。
    • roleName - そのロールの名前を含むロールエントリの属性。
    • roleNested - ネストされたロールを有効にします。ロールをロールにネストする場合はtrueに設定します。設定されている場合、新しく見つかったroleNameとdistinguished Nameはすべて、新しいロール検索に対して再帰的に試行されます。デフォルト値はfalseです。
  • ユーザーエントリの属性としてのロール

    ロール名は、ユーザーのディレクトリエントリの属性の値として保持することもできます。この属性の名前を指定するには、userRoleNameを使用します。

ロール表現の両方のアプローチを組み合わせて使用​​できます。

クイックスタート

JNDIRealmを使用するようにTomcatを設定するには、次の手順に従ってください。

  1. ディレクトリサーバーが、上記の要件に一致するスキーマで設定されていることを確認します。
  2. 必要に応じて、上記の情報を参照できる読み取り専用のアクセス権を持つ、Tomcatが使用するユーザー名とパスワードを設定します。(Tomcatはこれらの情報を変更しようとはしません)。
  3. 以下のように記述されている<Realm>要素を、$CATALINA_BASE/conf/server.xmlファイルに設定します。
  4. 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://localhost:389"
       userPattern="uid={0},ou=people,dc=mycompany,dc=com"
          roleBase="ou=groups,dc=mycompany,dc=com"
          roleName="cn"
        roleSearch="(uniqueMember={0})"
/>

この構成では、レルムはuserPatternにユーザー名を代入することでユーザーの識別名(DN)を決定し、このDNとユーザーから受け取ったパスワードを使用してディレクトリにバインドすることで認証を行い、ディレクトリを検索してユーザーのロールを見つけます。

次に、ユーザーがログイン時にユーザーIDではなくメールアドレスを入力することが想定されているとします。この場合、レルムはユーザーのエントリをディレクトリで検索する必要があります。(ユーザーエントリが、おそらく異なる組織単位または会社所在地に対応する複数のサブツリーに保持されている場合にも、検索が必要です)。

さらに、グループエントリに加えて、ユーザーのエントリの属性を使用してロールを保持したいとします。ジャネット・ジョーンズのエントリは次のようになります。

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   className="org.apache.catalina.realm.JNDIRealm"
     connectionURL="ldap://localhost: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」としてログインすると、レルムはメール属性としてその値を持つ一意のエントリをディレクトリで検索し、指定されたパスワードを使用してuid=jjones,ou=people,dc=mycompany,dc=comとしてディレクトリへのバインドを試みます。認証に成功すると、ディレクトリエントリの「memberOf」属性の値である「role2」と「role3」、およびメンバーである唯一のグループエントリの「cn」属性の値である「tomcat」の3つのロールが割り当てられます。

最後に、ディレクトリからパスワードを取得し、レルム内でローカル比較を行うことでユーザーを認証するには、次のようなレルム構成を使用できます。

<Realm   className="org.apache.catalina.realm.JNDIRealm"
    connectionName="cn=Manager,dc=mycompany,dc=com"
connectionPassword="secret"
     connectionURL="ldap://localhost: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はこのRealmauthenticate()メソッドを呼び出します。したがって、ディレクトリに行った変更(新しいユーザー、パスワードまたはロールの変更など)はすぐに反映されます。
  • ユーザーが認証されると、ユーザー(および関連付けられたロール)は、ユーザーのログイン期間中、Tomcat内にキャッシュされます。(フォームベースの認証の場合、セッションがタイムアウトするか無効になるまで。基本認証の場合、ユーザーがブラウザーを閉じるまで)。キャッシュされたユーザーはセッションのシリアル化に保存および復元されません。既に認証されているユーザーのディレクトリ情報に対する変更は、そのユーザーが次回ログインするまで反映されません。
  • ディレクトリサーバーの情報管理は、独自のアプリケーションの責任です。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のデフォルトインストールは、すべての仮想ホストとWebアプリケーションに適用されるように、<Engine>要素内にネストされたUserDatabaseRealmで構成されています。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はこの目的のためにアクセスできるMBeanを提供します。
  • ユーザーが初めて保護されたリソースにアクセスしようとすると、TomcatはこのRealmauthenticate()メソッドを呼び出します。
  • ユーザーが認証されると、ユーザーはユーザーのログイン期間中、Tomcat内に関連付けられます。(フォームベースの認証の場合、セッションがタイムアウトするか無効になるまで。基本認証の場合、ユーザーがブラウザーを閉じるまで)。ただし、他のレルムとは異なり、ユーザーロールは引き続きUserDatabaseの内容を反映します。ユーザーがデータベースから削除されると、ロールがないと見なされます。UserDatabaseRealmuseStaticPrincipal属性を使用して、代わりにユーザーとそのすべてのロールをキャッシュすることもできます。キャッシュされたユーザーはセッションのシリアル化に保存および復元されません。ユーザーのプリンシパルオブジェクトが何らかの理由でシリアル化されると、データベースの内容を反映しなくなるロールを持つ静的な同等のオブジェクトに置き換えられます。

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はこのRealmauthenticate()メソッドを呼び出します。
  • ユーザーが認証されると、ユーザー(および関連付けられたロール)は、ユーザーのログイン期間中、Tomcat内にキャッシュされます。(フォームベースの認証の場合、セッションがタイムアウトするか無効になるまで。基本認証の場合、ユーザーがブラウザーを閉じるまで)。キャッシュされたユーザーはセッションのシリアル化に保存および復元されません。
  • ユーザーファイルの情報管理は、アプリケーションの責任です。Tomcatは、ユーザーとロールを維持するための組み込み機能を提供していません。

JAASRealm

はじめに

JAASRealmは、標準Java SE APIの一部として提供されるようになったJava Authentication & Authorization Service(JAAS)フレームワークを介してユーザーを認証するTomcatのRealmインターフェースの実装です。

JAASRealmを使用すると、開発者は事実上考えられるあらゆるセキュリティレルムをTomcatのCMAと組み合わせることができます。

JAASRealmは、コンテナー管理セキュリティを強化し、実装がコンテナーに依存しない「プラグ可能な」認証メカニズムを促進するためのJCP仕様要求196に基づいて、J2EE v1.4のJAASベースのJ2EE認証フレームワークのTomcatのプロトタイプです。

JAASログインモジュールとプリンシパル(javax.security.auth.spi.LoginModuleおよびjavax.security.Principalを参照)に基づいて、独自のセキュリティメカニズムを開発したり、Tomcatによって実装されるCMAとの統合のためにサードパーティのメカニズムをラップしたりできます。

クイックスタート

独自のJAASログインモジュールを使用してJAASRealmを使用するようにTomcatを設定するには、次の手順に従う必要があります。

  1. JAASに基づいて独自のLoginModule、User、Roleクラスを作成します(JAAS認証チュートリアルおよびJAASログインモジュール開発者ガイドを参照)。LoginModuleを開発する際には、JAASRealmの組み込みCallbackHandlerは現在、NameCallbackPasswordCallbackのみを認識することに注意してください。
  2. JAASでは指定されていませんが、ユーザーとロールを区別するために個別のクラスを作成し、javax.security.Principalを拡張して、Tomcatがログインモジュールから返されたプリンシパルがユーザーかロールかを判断できるようにする必要があります(org.apache.catalina.realm.JAASRealmを参照)。いずれにせよ、最初に返されるプリンシパルは常にユーザープリンシパルとして扱われます。
  3. コンパイルされたクラスをTomcatのクラスパスに配置します。
  4. Javaのlogin.configファイルを設定し(JAAS LoginConfigファイルを参照)、環境変数を設定してJVMにその場所を指定します。例:JAVA_OPTS=$JAVA_OPTS -Djava.security.auth.login.config==$CATALINA_BASE/conf/jaas.config
  5. 保護するリソースについて、web.xmlでセキュリティ制約を設定します。
  6. server.xmlでJAASRealmモジュールを構成します。
  7. 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は中断し、http://localhost:8080/myapp/j_security_check URIまたはその他の未指定の場所に移動します。

JAASアプローチの柔軟性は2つの側面があります。

  • 独自のログインモジュールで、必要な処理をバックグラウンドで実行できます。
  • 構成を変更してサーバーを再起動することで、アプリケーションのコードを変更せずに、完全に異なるLoginModuleをプラグインできます。
追加の注意事項
  • ユーザーが初めて保護されたリソースにアクセスしようとすると、TomcatはこのRealmauthenticate()メソッドを呼び出します。したがって、セキュリティメカニズムに対して直接行った変更(新しいユーザー、パスワードまたはロールの変更など)はすぐに反映されます。
  • ユーザーが認証されると、ユーザー(および関連付けられたロール)は、ユーザーのログイン期間中、Tomcat にキャッシュされます。フォームベース認証の場合、セッションのタイムアウトまたは無効化されるまでです。ベーシック認証の場合、ユーザーがブラウザーを閉じるまでです。既に認証されているユーザーのセキュリティ情報の変更は、ユーザーが次回ログインするまで**反映されません**。
  • 他のRealm実装と同様に、server.xml<Realm>要素にdigest属性が含まれている場合、ダイジェストパスワードがサポートされます。JAASRealmのCallbackHandlerは、パスワードをLoginModuleに返す前にダイジェストします。

CombinedRealm

はじめに

CombinedRealmは、1つ以上のサブRealmを通じてユーザーを認証するTomcat Realmインターフェースの実装です。

CombinedRealmを使用すると、開発者は、同じタイプまたは異なるタイプの複数のRealmを組み合わせることができます。これは、異なるソースに対して認証を行う場合、1つの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は、Tomcat Realmインターフェースの実装であり、CombinedRealmを拡張してロックアウト機能を提供します。これは、一定期間内に認証失敗の試行が多すぎる場合に、ユーザーロックアウトメカニズムを提供します。

正しい動作を確保するために、この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>