リライトバルブ

はじめに

リライトバルブは、Apache HTTP Serverのmod_rewriteと非常によく似た方法でURLリライト機能を実現します。

設定

リライトバルブは、org.apache.catalina.valves.rewrite.RewriteValveというクラス名を使用してバルブとして設定されます。

リライトバルブは、Hostに追加されるバルブとして設定できます。設定方法については、仮想サーバーのドキュメントを参照してください。リライトディレクティブを含むrewrite.configファイルを使用し、そのファイルはHostの設定フォルダに配置する必要があります。

また、ウェブアプリケーションのcontext.xmlにも設定できます。その場合、バルブはリライトディレクティブを含むrewrite.configファイルを使用し、そのファイルはウェブアプリケーションのWEB-INFフォルダに配置する必要があります。

特殊文字を含むリライトルールを使用する

リライトバルブに提示されるURLは、リクエストマッピングに使用されるURLと同じであり、リテラルな'%'';'、および/または'?'文字は%nn形式でエンコードされます。

リテラルな'%'';''?''&'、または'='文字を挿入したいリライトルールは、%nn形式で挿入する必要があります。その他の文字は、リテラル形式または%nn形式のいずれかで挿入できます。

これにより、リライトルールで以下のことが可能になります

  • リテラルな'?'文字を含むURLを処理すること;
  • クエリ文字列を追加すること;
  • %nnエンコーディングと混同されることなく、リテラルな'%'文字を挿入すること。

ディレクティブ

rewrite.configファイルには、mod_rewriteで使用されるディレクティブ、特に中心となるRewriteRuleとRewriteCondディレクティブによく似たディレクティブのリストが含まれています。#文字で始まる行はコメントとして扱われ、無視されます。

注: このセクションは、mod_rewriteドキュメントの改訂版であり、著作権は1995-2006 The Apache Software Foundationが所有し、Apache License, Version 2.0に基づいてライセンスされています。

RewriteCond

構文: RewriteCond TestString CondPattern

RewriteCondディレクティブは、ルール条件を定義します。RewriteRuleディレクティブの前に1つ以上のRewriteCondを置くことができます。以下のルールは、URIの現在の状態がそのパターンに一致し、かつこれらの条件が満たされた場合にのみ使用されます。

TestString は、プレーンテキストに加えて、以下の展開された構造を含むことができる文字列です。

  • RewriteRule バックリファレンス: これらは、現在のRewriteCond条件のセットが適用されるRewriteRuleのパターンから、グループ化された部分(括弧内)へのアクセスを提供する$N (0 <= N <= 9) の形式のバックリファレンスです。
  • RewriteCond バックリファレンス: これらは、現在の条件セットで最後にマッチしたRewriteCondのパターンから、グループ化された部分(やはり括弧内)へのアクセスを提供する%N (1 <= N <= 9) の形式のバックリファレンスです。
  • RewriteMap 展開: これらは${mapname:key|default}の形式の展開です。RewriteMapのドキュメントで詳細を参照してください。
  • サーバー変数: これらは%{ NAME_OF_VARIABLE }の形式の変数であり、NAME_OF_VARIABLEは以下のリストから取得された文字列になります
    • HTTPヘッダー

      HTTP_USER_AGENT
      HTTP_REFERER
      HTTP_COOKIE
      HTTP_FORWARDED
      HTTP_HOST
      HTTP_PROXY_CONNECTION
      HTTP_ACCEPT

    • 接続とリクエスト

      REMOTE_ADDR
      REMOTE_HOST
      REMOTE_PORT
      REMOTE_USER
      REMOTE_IDENT
      REQUEST_METHOD
      SCRIPT_FILENAME
      REQUEST_PATH
      CONTEXT_PATH
      SERVLET_PATH
      PATH_INFO
      QUERY_STRING
      AUTH_TYPE

    • サーバー内部

      DOCUMENT_ROOT
      SERVER_NAME
      SERVER_ADDR
      SERVER_PORT
      SERVER_PROTOCOL
      SERVER_SOFTWARE

    • 日付と時刻

      TIME_YEAR
      TIME_MON
      TIME_DAY
      TIME_HOUR
      TIME_MIN
      TIME_SEC
      TIME_WDAY
      TIME

    • 特殊

      THE_REQUEST
      REQUEST_URI
      REQUEST_FILENAME
      HTTPS

    これらの変数はすべて、同様の名前のHTTP MIMEヘッダーとServlet APIメソッドに対応しています。ほとんどはマニュアルまたはCGI仕様の他の場所で文書化されています。リライトバルブに特有のものは以下の通りです。

    REQUEST_PATH
    マッピングに使用されるフルパスに対応します。
    CONTEXT_PATH
    マップされたコンテキストのパスに対応します。
    SERVLET_PATH
    サーブレットパスに対応します。
    THE_REQUEST
    ブラウザからサーバーに送信された完全なHTTPリクエスト行(例: 「GET /index.html HTTP/1.1」)。これには、ブラウザから送信された追加のヘッダーは含まれません。
    REQUEST_URI
    HTTPリクエスト行で要求されたリソース。(上記の例では、「/index.html」になります。)
    REQUEST_FILENAME
    リクエストに一致するファイルまたはスクリプトへの完全なローカルファイルシステムパス。
    HTTPS
    接続がSSL/TLSを使用している場合は「on」を、そうでない場合は「off」を含みます。

その他注意すべき点

  1. 変数 SCRIPT_FILENAME と REQUEST_FILENAME は同じ値を含みます。これはApacheサーバーの内部request_rec構造のfilenameフィールドの値です。最初の名前は一般的に知られているCGI変数名であり、2番目はREQUEST_URI(request_recuriフィールドの値を含む)の適切な対応物です。
  2. variable が任意のJavaシステムプロパティである%{ENV:variable}も利用可能です。
  3. variable がSSL環境変数の名前である%{SSL:variable}は、SSL_SESSION_RESUMEDSSL_SECURE_RENEGSSL_COMPRESS_METHODSSL_TLS_SNISSL_SRP_USERSSL_SRP_USERINFOSSL_CLIENT_VERIFYSSL_CLIENT_SAN_OTHER_msUPN_nSSL_CLIENT_CERT_RFC4523_CEASSL_SERVER_SAN_OTHER_dnsSRV_nを除いて実装されています。OpenSSLが使用されている場合、SSL_SERVER_で始まるサーバー証明書に関連する変数は利用できません。例: %{SSL:SSL_CIPHER_USEKEYSIZE}128に展開される場合があります。
  4. header が任意のHTTP MIMEヘッダー名である%{HTTP:header}は、HTTPリクエストで送信されたヘッダーの値を取得するために常に使用できます。例: %{HTTP:Proxy-Connection}はHTTPヘッダー「Proxy-Connection:」の値です。

CondPattern は条件パターンであり、TestString の現在のインスタンスに適用される正規表現です。TestString は、CondPattern と照合される前にまず評価されます。

注意: CondPattern は、いくつかの追加機能を持つPerl互換の正規表現です。

  1. パターン文字列の前に「!」文字(感嘆符)を付けることで、一致パターンを指定できます。
  2. CondPattern にはいくつかの特殊なバリアントがあります。実際の正規表現文字列の代わりに、以下のいずれかを使用することもできます
    • <CondPattern」(辞書順で前にある)
      CondPattern をプレーンな文字列として扱い、TestString と辞書順で比較します。TestStringCondPattern より辞書順で前にある場合にtrueとなります。
    • >CondPattern」(辞書順で後にある)
      CondPattern をプレーンな文字列として扱い、TestString と辞書順で比較します。TestStringCondPattern より辞書順で後にある場合にtrueとなります。
    • =CondPattern」(辞書順で等しい)
      CondPattern をプレーンな文字列として扱い、TestString と辞書順で比較します。TestStringCondPattern と辞書順で等しい場合(両方の文字列が文字単位で完全に等しい場合)にtrueとなります。CondPattern""(二重引用符2つ)の場合、TestString を空文字列と比較します。
    • -d」(directory である)
      TestString をパス名として扱い、それが存在し、かつディレクトリであるかどうかをテストします。
    • -f」(通常のfile である)
      TestString をパス名として扱い、それが存在し、かつ通常のファイルであるかどうかをテストします。
    • -s」(size を持つ通常のファイルである)
      TestString をパス名として扱い、それが存在し、かつサイズがゼロより大きい通常のファイルであるかどうかをテストします。
    注: これらのすべてのテストは、感嘆符「!」を前に付けることでその意味を否定することもできます。
  3. RewriteCond ディレクティブの3番目の引数として[flags]を追加することで、CondPattern に特殊なフラグを設定することもできます。flags は、以下のいずれかのフラグをコンマで区切ったリストです。
    • nocase|NC」(大文字小文字の区別し)
      これによりテストが大文字小文字を区別しなくなります。つまり、展開されたTestStringCondPatternの両方で「A-Z」と「a-z」の違いが無視されます。このフラグは、TestStringCondPattern間の比較にのみ有効です。ファイルシステムやサブリクエストのチェックには影響しません。
    • ornext|OR」(次の条件とOR結合)
      これを、暗黙のANDの代わりにローカルなORでルール条件を結合するために使用します。典型的な例
      RewriteCond %{REMOTE_HOST}  ^host1.*  [OR]
      RewriteCond %{REMOTE_HOST}  ^host2.*  [OR]
      RewriteCond %{REMOTE_HOST}  ^host3.*
      RewriteRule ...some special stuff for any of these hosts...
      このフラグがない場合、条件/ルールペアを3回書く必要があります。

リクエストの「User-Agent:」ヘッダーに基づいてサイトのホームページをリライトするには、次のようにします

RewriteCond  %{HTTP_USER_AGENT}  ^Mozilla.*
RewriteRule  ^/$                 /homepage.max.html  [L]

RewriteCond  %{HTTP_USER_AGENT}  ^Lynx.*
RewriteRule  ^/$                 /homepage.min.html  [L]

RewriteRule  ^/$                 /homepage.std.html  [L]

説明: 「Mozilla」と自称するブラウザ(Netscape Navigator、Mozillaなどを含む)を使用した場合、maxホームページ(フレームやその他の特殊機能を含む可能性がある)が表示されます。Lynxブラウザ(ターミナルベース)を使用した場合、minホームページ(簡単でテキストのみのブラウジング用に設計されたバージョンである可能性がある)が表示されます。これらの条件のいずれも適用されない場合(他のブラウザを使用しているか、ブラウザが非標準の何かとして自己識別している場合)、std(標準)ホームページが表示されます。

RewriteMap

構文: RewriteMap name rewriteMapClassName optionalParameters

rewriteMapClassNameの値は、特殊な値も許可します

  • int:toupper: 渡された値を大文字に変換する特殊マップ
  • int:tolower: 渡された値を小文字に変換する特殊マップ
  • int:escape: 渡された値をURLエスケープする
  • int:unescape: 渡された値をURLアンエスケープする

マップは、ユーザーが実装する必要があるインターフェースを使用して実装されます。そのクラス名はorg.apache.catalina.valves.rewrite.RewriteMapで、そのコードは次のとおりです

package org.apache.catalina.valves.rewrite;

public interface RewriteMap {
    default String setParameters(String params...); // calls setParameters(String) with the first parameter if there is only one
    public String setParameters(String params);
    public String lookup(String key);
}

そのようなクラス(例ではrewriteMapClassName)の参照された実装は、setParameters(String)を呼び出すことにより、オプションのパラメータ(上記からのoptionalParameters、空白に注意)でインスタンス化および初期化されます。そのインスタンスは、RewriteMapルールの最初のパラメータとして与えられた名前で登録されます。

注: 複数のパラメータを使用できます。これらはスペースで区切る必要があります。パラメータは「"」で囲むことができます。これにより、パラメータ内にスペース文字を含めることができます。

そのマップインスタンスには、対応するRewriteRuleで設定されたルックアップ値が、lookup(String)の呼び出しによって与えられます。実装は、与えられたデフォルトを使用すべきであることを示すためにnullを返すか、置換値を返すことができます。

例えば、すべてのルックアップキーを大文字に変換するリライトマップ関数を実装したいとします。まず、RewriteMapインターフェースを実装するクラスを実装することから始めます。

package example.maps;

import org.apache.catalina.valves.rewrite.RewriteMap;

public class UpperCaseMap implements RewriteMap {

  @Override
  public String setParameters(String params) {
    // nothing to be done here
    return null;
  }

  @Override
  public String lookup(String key) {
    if (key == null) {
      return null;
    }
    return key.toUpperCase(Locale.ENGLISH);
  }

}

このクラスをコンパイルし、jarファイルにまとめて${CATALINA_BASE}/libに配置します。

それが終わったら、RewriteMapディレクティブでマップを定義し、さらにそのマップをRewriteRuleで使用することができます。

RewriteMap uc example.maps.UpperCaseMap

RewriteRule ^/(.*)$ ${uc:$1}

この設定により、URLパス/index.htmlへのリクエストは/INDEX.HTMLにルーティングされます。

RewriteRule

構文: RewriteRule Pattern Substitution

RewriteRuleディレクティブは、真のリライトの主力です。このディレクティブは複数回記述でき、各インスタンスが単一のリライトルールを定義します。これらのルールが定義される順序は重要です。これが実行時に適用される順序になります。

パターンはPerl互換の正規表現であり、現在のURLに適用されます。「現在」とは、このルールが適用される時点でのURLの値を意味します。これは元のリクエストされたURLとは異なる場合があります。元のURLはすでに前のルールに一致し、変更されている可能性があるためです。

セキュリティ警告: Javaの正規表現マッチングの性質上、不適切に形成された正規表現パターンは「壊滅的なバックトラック」、別名「正規表現サービス拒否 (ReDoS)」の脆弱性を持っています。したがって、RewriteRuleパターンには細心の注意を払う必要があります。一般的に、このような脆弱な正規表現を自動的に検出することは困難であるため、良い防御策としては、壊滅的なバックトラックに関する情報を少し読むことです。良い参照元は、OWASP ReDoS ガイドです。

正規表現の構文に関するヒント

Text:
  .           Any single character
  [chars]     Character class: Any character of the class 'chars'
  [^chars]    Character class: Not a character of the class 'chars'
  text1|text2 Alternative: text1 or text2

Quantifiers:
  ?           0 or 1 occurrences of the preceding text
  *           0 or N occurrences of the preceding text (N > 0)
  +           1 or N occurrences of the preceding text (N > 1)

Grouping:
  (text)      Grouping of text
              (used either to set the borders of an alternative as above, or
              to make backreferences, where the Nth group can
              be referred to on the RHS of a RewriteRule as $N)

Anchors:
  ^           Start-of-line anchor
  $           End-of-line anchor

Escaping:
  \char       escape the given char
              (for instance, to specify the chars ".[]()" etc.)

正規表現に関する詳細情報については、Perl正規表現のmanページ(「perldoc perlre」)を参照してください。正規表現とそのバリアント(POSIX正規表現など)に関するより詳細な情報に興味がある場合は、以下の書籍がこのトピックに特化しています

Mastering Regular Expressions, 第2版
Jeffrey E.F. Friedl
O'Reilly & Associates, Inc. 2002
ISBN 978-0-596-00289-3

ルールでは、NOT文字(「!」)も可能なパターン接頭辞として利用できます。これにより、パターンを否定することができます。たとえば、「現在のURLがこのパターンに一致しない場合」と指定できます。これは、否定パターンに一致させる方が簡単な例外的なケースや、最後のデフォルトルールとして使用できます。

注: NOT文字を使用してパターンを否定する場合、そのパターンにグループ化されたワイルドカード部分を含めることはできません。これは、パターンが一致しない場合(つまり、否定が一致する場合)、グループに内容がないためです。したがって、否定されたパターンを使用する場合、置換文字列で$Nを使用することはできません!

リライトルールの置換は、Patternに一致した元のURLと置換される(または置き換える)文字列です。プレーンテキストに加えて、以下を含むことができます

  1. RewriteRuleパターンへのバックリファレンス ($N)
  2. 最後に一致したRewriteCondパターンへのバックリファレンス (%N)
  3. ルール条件テスト文字列と同様のサーバー変数 (%{VARNAME})
  4. マッピング関数呼び出し (${mapname:key|default})

バックリファレンスは、一致したPatternN番目のグループの内容に置換される、$N (N=0..9) の形式の識別子です。サーバー変数は、RewriteCondディレクティブのTestStringと同じです。マッピング関数はRewriteMapディレクティブに由来し、そこで説明されています。これら3種類の変数は上記の順序で展開されます。

すでに述べたように、すべてのリライトルールはSubstitutionに適用されます(設定ファイルで定義された順序で)。URLはSubstitutionによって完全に置き換えられ、すべてのルールが適用されるまで、またはLフラグによって明示的に終了されるまで、リライト処理が続行されます。

特殊文字$%は、バックスラッシュ文字\を前に付けることでエスケープできます。

-」という特殊な置換文字列があり、これは置換なしを意味します!これは、URLにのみ一致するが何も置換しないリライトルールを提供する場合に役立ちます。これは通常、置換が発生する前に複数のパターンを適用するために、C(チェーン)フラグと組み合わせて使用されます。

新しいmod_rewriteバージョンとは異なり、Tomcatのリライトバルブは絶対URL(絶対URLを指定するには特定のredirectフラグを使用する必要がある、後述参照)や直接ファイルサービングを自動的にサポートしていません。

さらに、RewriteRuleディレクティブの3番目の引数として[flags]を追加することで、Substitutionに特殊なフラグを設定できます。Flagsは、以下のいずれかのフラグをコンマで区切ったリストです。

  • chain|C」(次のルールとェイン)
    このフラグは現在のルールを次のルールとチェーンします(次のルールもさらにその次のルールとチェーンできます)。これにより、次のような効果があります。ルールが一致した場合、処理は通常通り続行され、このフラグは影響を与えません。ルールが一致しない場合、後続のチェーンされたすべてのルールはスキップされます。たとえば、外部リダイレクトが発生した際に(「.www」の部分が発生すべきではない場合)、ディレクトリごとのルールセット内で「.www」部分を削除するために使用できます。
  • cookie|CO=NAME:VAL:domain[:lifetime[:path]]」(ッキーの設定)
    これはクライアントのブラウザにクッキーを設定します。クッキーの名前はNAMEで、値はVALです。domainフィールドはクッキーのドメイン(例: 「.apache.org」)、オプションのlifetimeはクッキーの有効期間(分単位)、オプションのpathはクッキーのパスです。
  • env|E=VAR:VAL」(境変数の設定)
    これにより、VARという名前のリクエスト属性が値VALに設定されることを強制します。VALは展開される正規表現バックリファレンス($N%N)を含むことができます。このフラグは複数回使用でき、複数の変数を設定できます。
  • forbidden|F」(URLを止する)
    これにより、現在のURLが禁止されることを強制します。直ちにHTTP 403 (FORBIDDEN) レスポンスを返します。このフラグを適切なRewriteCondと組み合わせて使用し、一部のURLを条件付きでブロックします。
  • gone|G」(URLを止する)
    これにより、現在のURLが廃止されることを強制します。直ちにHTTP 410 (GONE) レスポンスを返します。このフラグを使用して、存在しなくなったページを廃止としてマークします。
  • host|H=Host」(ストにリライトを適用)
    URLをリライトするのではなく、仮想ホストがリライトされます。
  • last|L」(後​​のルール)
    ここでリライト処理を停止し、それ以上リライトルールを適用しません。これはPerlのlastコマンドまたはC言語のbreakコマンドに対応します。このフラグを使用して、現在リライトされたURLが後続のルールによってさらにリライトされるのを防ぎます。例えば、ルートパスURL(「/」)を実際のURL、例:/e/www/」にリライトするために使用します。
  • next|N」(のラウンド)
    リライト処理を再実行します(最初のルールから再度開始)。このとき、マッチングされるURLは元のURLではなく、最後のルールによって返されたURLとなります。これはPerlのnextコマンドまたはC言語のcontinueコマンドに対応します。このフラグを使用してリライト処理を再開し、直ちにループの先頭に戻ります。
    無限ループを作成しないように注意してください!
  • nocase|NC」(大文字小文字の区別し)
    これにより、Pattern が現在のURLと照合される際に、Pattern が大文字小文字を区別せず、「A-Z」と「a-z」の違いを無視します。
  • noescape|NE」(出力のURIスケープなし)
    このフラグは、リライトバルブがリライト結果に通常のURIエスケープルールを適用するのを防ぎます。通常、特殊文字(「%」、「$」、「;」など)はそれぞれの16進コード(それぞれ「%25」、「%24」、「%3B」)にエスケープされますが、このフラグはこれを防ぎます。これにより、次のように出力にパーセント記号が表示されるようになります
    RewriteRule /foo/(.*) /bar?arg=P1\%3d$1 [R,NE]
    これにより、「/foo/zed」が「/bar?arg=P1=zed」に対する安全なリクエストに変わります。
  • qsappend|QSA」(エリトリングの加)
    このフラグは、リライトエンジンに、置換文字列のクエリ文字列部分を既存の文字列に置き換えるのではなく、追加することを強制します。リライトルールを介してクエリ文字列にさらにデータを追加したい場合に使用します。
  • redirect|R [=code]」(強制ダイレクト
    Substitutionhttp://thishost[:thisport]/でプレフィックスし(これにより新しいURLがURIになる)、外部リダイレクトを強制します。codeが指定されていない場合、HTTP 302 (FOUND、以前はMOVED TEMPORARILY) レスポンスが返されます。300-399の範囲で他のレスポンスコードを使用したい場合は、適切な数値を指定するか、以下のシンボリック名のいずれかを使用してください: temp(デフォルト)、permanentseeother。URLを正規化してクライアントに返すルール(例: 「/~」を「/u/」に変換したり、/u/userに常にスラッシュを追加したりする場合など)にこれを使用します。
    注: このフラグを使用する場合、置換フィールドが有効なURLであることを確認してください!そうでない場合、無効な場所にリダイレクトされます。このフラグ単独では、URLの前にhttp://thishost[:thisport]/を付加するだけで、リライトは続行されることを覚えておいてください。通常、この時点でリライトを停止し、すぐにリダイレクトしたいと思うでしょう。リライトを停止するには、「L」フラグを追加する必要があります。
  • skip|S=num」(次のルールをキップ)
    現在のルールが一致した場合、このフラグはリライトエンジンに、次に続くnum個のルールをスキップするよう強制します。これは擬似的なif-then-else構造を作成するために使用します。then節の最後のルールはskip=Nとなり、Nはelse節のルール数です。(これは「chain|C」フラグとは異なります!)
  • type|T=MIME-type」(MIMEイプを強制)
    ターゲットファイルのMIMEタイプをMIME-typeに強制します。これは、いくつかの条件に基づいてコンテンツタイプを設定するために使用できます。例えば、以下のスニペットは、.phpファイルが.phps拡張子で呼び出された場合、mod_phpによって表示されることを許可します。
    RewriteRule ^(.+\.php)s$ $1 [T=application/x-httpd-php-source]
  • valveSkip|VS」(バルブをスキップ)
    このフラグは、バルブの条件付き実行を設定するために使用できます。フラグが設定され、ルールが一致した場合、リライトバルブはCatalinaパイプライン内の次のバルブをスキップします。リライトバルブがパイプラインの最後のバルブである場合、このフラグは無視され、コンテナの基本バルブが呼び出されます。リライトが発生した場合、このフラグは効果がありません。