Expression Language(EL)インジェクション

Expression Language(EL)インジェクション

説明

Java/Jakarta Expression Language(EL)は、文字列で書かれた式からオブジェクトのプロパティを読み書きし、メソッドを呼び出せます。信頼できない入力を式のソースとして使うと、データと実行構造の境界が失われ、ELインジェクションが発生します。

Jakarta Expression Language 6.0は、既定で java.lang.* をインポートし、公開された静的フィールド、メソッド、コンストラクターへの参照をサポートします。実際の影響は、評価コンテキストに登録したBean、関数、リゾルバー、インポートによって異なります。攻撃者はアクセス可能なデータを読み書きしたり、メソッドを呼び出したりできます。公開された機能によっては、OSコマンドの実行につながるおそれもあります。

主なAPIの動作は次のとおりです。

API 式を渡す引数 動作
ELProcessor.eval(expression) 第1引数 式を直ちに評価します。
ELProcessor.getValue(expression, type) 第1引数 式を評価し、結果を指定した型へ変換します。
ELProcessor.setValue(expression, value) 第1引数 対象の式を評価し、末尾のプロパティや変数を変更します。第2引数はデータです。
ELProcessor.setVariable(name, expression) 第2引数 後で参照する式を解析して変数に関連付けます。第1引数は変数名です。
ExpressionFactory.createValueExpression(context, expression, type) 第2引数 後で評価する値の式を解析します。
ExpressionFactory.createMethodExpression(context, expression, ...) 第2引数 後で呼び出すメソッドの式を解析します。

javax.el は従来のJava EEの名前空間、jakarta.el はJakarta EEの名前空間です。

想定される影響

  • 評価コンテキストに公開されたアプリケーションデータの不正な閲覧や変更
  • アクセス可能なオブジェクト、静的メソッド、コンストラクターの呼び出しによる機能の悪用
  • ファイル、ネットワーク、OSコマンドのAPIに到達できる場合のサーバー権限での処理
  • 計算量の大きい式やストリーム操作によるリソース消費

対処方法

  1. EL式のテキストは開発者が管理する固定文字列にしてください。リクエスト値や外部設定値を式に連結したり、そのまま解析・評価したりしないでください。
  2. 信頼できない値は、式のソースではなくデータとしてバインドしてください。ELProcessor.defineBean や VariableMapper を使い、その値を参照する固定式だけを評価してください。
  3. ユーザーが操作を選ぶ場合は、サーバー管理の有限のキーを、確認済みの定数式に対応付けてください。任意の文字列を受け入れる正規表現や一般的なエスケープ関数では、EL文法全体の安全性を保証できません。
  4. 固定式に必要なBean、関数、リゾルバー、インポートだけを公開し、最小権限で実行してください。これは多層防御であり、攻撃者が制御する式を安全にするサニタイザーではありません。
  5. ELが不要な機能では評価を取り除くか無効にしてください。Jakarta EL 6.0はJavaの SecurityManager への参照を削除しているため、標準的なサンドボックス対策として依存しないでください。

例

変更前

java
import jakarta.el.ELProcessor;
import jakarta.servlet.http.HttpServletRequest;

final class UnsafeElEvaluation {
    Object evaluate(HttpServletRequest request) {
        String expression = request.getParameter("expression");

        ELProcessor processor = new ELProcessor();
        return processor.eval(expression); // リクエスト値をELソースとして評価
    }
}

リクエスト値を ELProcessor.eval の式の引数に直接渡しています。攻撃者は、その環境でアクセスできるプロパティやメソッドを選ぶEL構文を挿入できます。

変更後

java
import jakarta.el.ELProcessor;
import jakarta.servlet.http.HttpServletRequest;

final class SafeElEvaluation {
    Object display(HttpServletRequest request) {
        String displayName = request.getParameter("displayName");

        ELProcessor processor = new ELProcessor();
        processor.defineBean("displayName", displayName); // リクエスト値はデータとしてバインド
        return processor.eval("displayName");             // 式は開発者が固定
    }
}

リクエスト値はBeanのデータとしてだけ登録し、評価する式は固定しています。複数の機能からユーザーが選ぶ場合も、選択キーをサーバー管理の定数式へ明示的に対応付けてください。

適用時の注意点

ExpressionFactory.create*Expression と ELProcessor.setVariable は、後で使う式を解析します。返された式や変数が実際に評価される箇所まで確認してください。入力は固定式のデータとして渡し、バインドした値を式のソースとして再評価しないでください。

参考資料