午前I問題

サーバサイドで動的にコンテンツを生成する処理の脆弱性に関する記述として、適切なものはどれか。

ア)OSコマンドインジェクションは、アプリケーションがユーザ入力をOSのシェルコマンドの一部として実行する際、入力にシェルのメタ文字(; | && 等)を含めることで、意図しないコマンドを実行させる攻撃である。

イ)OSコマンドインジェクションは、ユーザ入力をSQL文にそのまま連結することで、データベースへの不正な問い合わせを実行させる攻撃である。

ウ)テンプレートインジェクションは、通信経路上でパケットを改ざんし、正規のテンプレートファイルを別のファイルに差し替える攻撃である。

エ)テンプレートインジェクションは、ブラウザのキャッシュに保存されたテンプレートを盗み見ることで、機密情報を窃取する攻撃である。

午前I の解答・解説を見る

正解: ア

解説:

  • ア)正解。OSコマンドインジェクションは、シェル呼び出しを伴う処理(system(), exec()等)にユーザ入力が渡り、; や | 等のメタ文字によってコマンドが連結・追加実行されてしまう攻撃です。
  • イ)不正解。これはSQLインジェクションの説明です。
  • ウ)不正解。テンプレートインジェクションは通信経路上の改ざんではなく、アプリケーションがユーザ入力をテンプレートエンジンの構文として評価してしまうことで発生します。
  • エ)不正解。テンプレートインジェクションはキャッシュの盗み見とは無関係です。

午前II問題

次のPythonコード(Jinja2テンプレートエンジンを使用)は、ユーザ名を含む挨拶文をレンダリングする処理である。

from jinja2 import Template

name = request.args.get('name')
template = Template("こんにちは、" + name + "さん")
output = template.render()

このコードに対し、name パラメータに {{ 7*7 }} を送信すると出力に 49 が含まれてしまう。この脆弱性の名称と、根本的な対策の組み合わせとして最も適切なものはどれか。

ア)名称:反射型XSS 対策:出力をHTMLエスケープする。

イ)名称:サーバサイドテンプレートインジェクション(SSTI) 対策:ユーザ入力をテンプレート文字列の構築(テンプレート自体の動的生成)に含めず、テンプレート内の変数(プレースホルダ)として渡す設計に変更する。

ウ)名称:サーバサイドテンプレートインジェクション(SSTI) 対策:{{ と }} という文字列をブラックリストとして除去する。

エ)名称:OSコマンドインジェクション 対策:シェル呼び出しを行わないAPIに置き換える。

午前II の解答・解説を見る

正解: イ

解説:

  • ア)不正解。出力に 49 が計算結果として現れている時点で、サーバ側でテンプレート構文として評価(式の実行)されていることを意味し、単なるHTML出力のエスケープ漏れ(XSS)ではありません。
  • イ)正解。この脆弱性はSSTI(Server-Side Template Injection)です。原因はユーザ入力を「テンプレート文字列そのもの」に文字列連結してしまっている点にあります。根本対策は、ユーザ入力をテンプレートの構造(構文)に混入させず、Template("こんにちは、{{ name }}さん").render(name=name) のように、常にデータとして変数バインディングで渡す設計に変更することです。
  • ウ)不正解。ブラックリスト方式は別の構文表現(Jinja2では {%...%} 等)や、フィルタ関数を経由した迂回など多様な回避手段があり、根本対策になりません。
  • エ)不正解。このコードはシェルコマンドを呼び出しておらず、OSコマンドインジェクションではありません。

SSTIはテンプレートエンジンの機能次第では、単なる式評価にとどまらずPythonオブジェクトの階層をたどってサーバ上で任意コード実行(RCE)に発展する危険性があります。

午後問題

L社が運営する社内向けの帳票作成システムでは、ユーザが指定した氏名や住所を含む挨拶文テンプレートを生成する機能と、指定したホスト名に対して疎通確認(ping)を行う機能を提供している。セキュリティ診断の結果、以下の指摘を受けた。

指摘①(テンプレート生成機能)

  • 入力欄に {{ config.items() }} を入力すると、アプリケーションの設定情報(データベース接続文字列を含む)がレスポンスに出力された。
  • さらに調査を進めたところ、Pythonのオブジェクト階層をたどる特殊な入力により、サーバ上で任意のOSコマンドが実行可能であることが確認された。

指摘②(疎通確認機能)

  • 実装は以下の通りであった。
import os
host = request.form.get('host')
os.system("ping -c 3 " + host)
  • host パラメータに 8.8.8.8; cat /etc/passwd を入力すると、ping結果に加えて /etc/passwd の内容が出力されることが確認された。

設問1

指摘①について、{{ config.items() }} のような入力から最終的に任意のOSコマンド実行(RCE)にまで発展し得る理由を、テンプレートエンジンの評価対象という観点から40字以内で述べよ。

設問1の解答・解説を見る

正解例: テンプレートエンジンがユーザ入力をPythonの式として評価するため、内部オブジェクトを辿り危険な関数を呼び出せるから。

解説・採点基準(計15点):

採点項目キーワード配点
「入力をコードとして評価する」旨の説明式として評価/プログラムとして実行8点
オブジェクト階層をたどる点への言及内部オブジェクト/親クラス等へのアクセス7点

解説: Jinja2等のテンプレートエンジンは {{ }} 内をPythonの式として評価します。攻撃者はこの性質を利用し、公開されているオブジェクトから __class__.__mro__ のようなPythonの特殊属性を辿って os モジュールなど危険な機能へ到達し、最終的に os.popen() 等を呼び出してOSコマンドを実行させます。SSTIが「単なる情報漏えい」で終わらず「RCE」に発展し得る典型例です。

設問2

指摘②のコードにおける脆弱性の名称を答え、根本的な対策を実装レベルで具体的に述べよ。また、指摘①・②に共通する設計上の問題点を20字以内で述べよ。

設問2の解答・解説を見る

正解例:

  • 脆弱性の名称:OSコマンドインジェクション
  • 根本的な対策:os.system() のようなシェル経由の呼び出しをやめ、subprocess.run(["ping", "-c", "3", host], shell=False) のようにシェルを介さず引数をリストで渡すAPIを使用する。加えて host の値をIPアドレス・ホスト名の正規表現でホワイトリスト検証する。
  • 共通する設計上の問題点:ユーザ入力を実行対象のコード・コマンドの一部として直接組み込んでいる点。

解説・採点基準(計15点):

採点項目キーワード配点
脆弱性名称OSコマンドインジェクション3点
対策shell=False/シェル非経由API/引数リスト化6点
共通問題点ユーザ入力をコード/コマンドの一部として組み込んでいる6点

解説: SSTIとOSコマンドインジェクションは表面上異なる脆弱性ですが、根本原因は共通しています。「ユーザから見ればただのデータ(氏名やホスト名)のはずの入力を、サーバ側で“実行可能な構文の一部”として組み込んでしまう」という設計ミスです。対策の方向性も共通しており、(1)入力をコード・コマンドの構築に混ぜずデータとして扱う、(2)どうしても混ぜる必要がある場合は許可された値のみを通すホワイトリスト検証を行う、という2点に集約されます。

重要キーワード

用語説明
SSTI(サーバサイドテンプレートインジェクション)ユーザ入力がテンプレートエンジンの構文として評価され情報漏えいやRCEに至る脆弱性
OSコマンドインジェクションユーザ入力がシェルコマンドの一部として実行され、意図しないコマンドが実行される脆弱性
RCE(Remote Code Execution)攻撃者がリモートから対象サーバ上で任意のコードを実行できる状態
シェルメタ文字; `
変数バインディングテンプレートやクエリの構造とデータを分離し、データを安全にプレースホルダへ渡す設計
ホワイトリスト検証許可された形式・値のみを受け入れ、それ以外を拒否する入力検証方式

まとめ

  • 午前I視点: OSコマンドインジェクションとテンプレートインジェクションは「何として評価されるか(シェルコマンド/テンプレート構文)」で区別し、SQLi・XSSとの選択肢の混同に注意
  • 午前II視点: SSTIの根本対策は「ユーザ入力をテンプレート構造に混ぜない(変数バインディングで渡す)」ことであり、ブラックリスト方式の限界を理解する
  • 午後視点: SSTIがRCEに発展する仕組み(オブジェクト階層をたどる)と、OSコマンドインジェクション対策(shell=False・引数リスト化)を説明できるようにし、両者に共通する「入力とコードの分離」という設計原則を理解する