【パストラバーサル編】保存場所の外へ出さないファイル処理|CTF思考フレームワーク #31
こんにちは、アンペンです!
前回は、シェルに繋がるOSコマンドインジェクションを扱いました。
今回は、ファイル名やパスを受け取る機能で起きるパストラバーサルを見ていきます。 ../ でディレクトリを越える攻撃と、その守り方を学びましょう。

../って、ただの相対パス記号でしょ?何が危ないの?

サーバが受け取ったパスを正規化せずに連結すると、想定の上位ディレクトリの設定ファイルや秘密鍵にアクセスされることがあるんだ。
../——ファイルを扱ったことがある人なら見慣れた“ひとつ上の階層へ”の記号ですよね。こんな日常的な記号が、設定ファイルや秘密鍵の流出につながる。それが今日のパストラバーサルです。仕組みはとてもシンプルで、『サーバが、利用者の言うままにファイルを取りに行ってしまう』だけ。今日は倉庫の係員のたとえで、その素朴だけど見落としやすい穴をほどいていきます。
パストラバーサルは、利用者入力のファイル名/パスをサーバが正規化せずに使うことで、想定外のファイルにアクセスされる脆弱性です。守りは『絶対パスへの正規化』『許可ディレクトリ強制(canonicalize+startswith)』『そもそも入力からファイル名を組み立てない設計』の組み合わせ。
この記事で分かること
- パストラバーサルが成立する条件
- 絶対パス化と許可ディレクトリ強制の考え方
- ID参照経由でパス受け取り自体を避ける設計
📖 はじめてのWebセキュリティ #31|パストラバーサル編
『相対パスの解釈』が攻撃面になる理由と、安全なパス処理を学びます。 シリーズ一覧を見る →
⚠️ 大事なお約束
この記事の確認は、CTF・自分の検証環境のみで行ってください。本物のサービスへの ../ 含むパス試行は不正アクセスに該当します。
ファイルパスは『相対』も解釈される
多くのOSやプログラミング言語のファイル操作は、パスを相対的に解釈します。 files/ + ../etc/passwd のような連結が許されると、想定外の場所にアクセスされてしまいます。
サーバ側でこの解釈を意識せず、文字列連結だけでパスを組み立てている処理が、パストラバーサルの典型的な発生源です。
ここが発生源。多くのプログラムは、『保存フォルダ』+『利用者が指定したファイル名』を、ただ文字としてつなげてパスを作ります。"files/" + 入力 のように。普段はこれで問題ありません。でも入力に ../ が混ざると、つなげた結果が枠の外を指してしまう。“足し算しただけ”のつもりが、想定外の住所を作ってしまう——この素朴な文字列連結こそが、落とし穴の正体です。
まず ../ の意味をおさらい。これは“ひとつ上のフォルダに戻る”という指示です。files/report.pdf なら files フォルダの中の report.pdf。でも files/../../etc/passwd だと、files から二つ上に戻って、まったく別の場所のファイルにたどり着いてしまう。コンピュータは“戻れ”と言われれば素直に戻ります。つまり、利用者がファイル名を指定できる場所では、この“戻る”を使って想定の枠の外へ出られてしまうわけです。
図解:通常パス vs ../入りパス

同じダウンロード処理でも、入力に ../ が含まれるかで、最終的に読み出されるファイルが大きく変わります。

倉庫の係員に『お客様用棚の3番目を取って来てください』と頼むと普通は安全です。でも、伝票の番号欄に『戻って、奥の機密エリアの棚を取って』と書かれていたら、係員がそれに従ってしまえば機密物まで持ってきてしまいます。サーバのファイル参照も、入力を信用すると同じ事態が起きます。
ここで覚える用語:正規化(canonicalize)
パスから .. . // を解決して、唯一の絶対パスに変換することです。Pythonの os.path.realpath、Javaの File.getCanonicalPath 等で行えます。正規化後に許可ディレクトリ配下かを確認することで、パストラバーサルを構造的に防げます。
『正規化(canonicalize)』は、今日のいちばんの武器です。むずかしい言葉ですが、やることは“住所を最後まで確定させる”だけ。files/../../etc/passwd のような“戻る指示入り”の住所を、いったん「これは結局 /etc/passwd のことですね」と最終的な一本道の住所に直すんです。住所が確定すれば、「それ、許可した部屋の中ですか?」と確実に判定できる。“戻る前”の見た目で判断せず、“たどり着いた先”で判断する——これがコツです。
パストラバーサルの3パターン
パストラバーサルが顔を出すのは、主に3つの場面。『ファイルをダウンロード/プレビューさせる機能』『アップロードの保存先を決める処理』『動的にファイルを読み込むテンプレート処理』。共通点は、どれも“利用者が指定した名前を、そのままパスに使っている”こと。ファイル名を受け取る場所を見たら、まず疑ってかかるのが正解です。
代表的な発生経路

- ダウンロード/プレビュー機能:
?file=report.pdfのような入力に../../etc/passwdが混ぜられる - アップロード保存先:利用者の入力ファイル名にディレクトリ区切りが入り、想定外の場所に保存される
- テンプレート/インクルード経由(LFI):動的にファイルを読み込んで処理する箇所で、
../から設定ファイルが読み出される
多くは、入力を正規化せずパスに連結している共通点があります。エンコード(%2e%2e等)や、Windows特有の区切り(\\)、シンボリックリンク経由のすり抜けにも注意が必要です。
ここで一つ注意。「じゃあ .. という文字列を見つけて弾けばいいのでは?」——この“ブラックリスト方式”は、実は穴だらけなんです。攻撃者は %2e%2e のようにURLエンコードしたり、Windows用の \ 区切りを使ったり、シンボリックリンクを経由したり、と次々に書き換えてきます。「危ない形を一個ずつ禁止する」のはキリがない。だから守りは“禁止リスト”ではなく、さきほどの正規化のように“最終的な行き先で判断する”方式に寄せるのが鉄則です。
練習は、コードの中で『利用者の入力からファイルパスを作っている場所』を探すことです。攻撃ではなく棚卸し。見つけたら、「正規化してる?」「許可フォルダの中か確認してる?」とチェックしていきます。本物のサービスに ../ 入りのURLを送るのは厳禁。自分のラボの中だけで、自分のコードを点検しましょう。
CTFでやってみよう:パス受け取り処理を棚卸し
自分のラボで、ファイル名/パスを受け取る処理を整理しよう
目的は実際の攻撃ではなく、『入力からパスが組み立てられている箇所』を可視化することです。
- コードベースで
open/fopen/File()/readFileSyncなど、ファイル参照APIを検索する - 引数に利用者入力(クエリ・フォーム・パス変数)が含まれる箇所をリスト化する
- 正規化(canonicalize)処理が入っているか、許可ディレクトリ判定があるか確認する
- そもそも利用者からパスを受け取らず、IDで参照する設計に置き換えられないか検討する
- 監査ログでパス例外や想定外アクセスを検知できる仕組みを整える
../ 入りURLを送る試行は絶対にやめてください。確認は自分のラボの範囲だけです。守りの考え方は、さきほどの“行き先で判断”の続きです。会話で固めましょう。

結局、入力から .. を消したり弾いたりすればいいんじゃないの?

その“引き算”の発想だと、さっき言ったエンコードや別記号で必ずすり抜けられるんだ。だから発想を逆にする。①まず入力でパスを組み立てて、②それを正規化して“最終的な住所”を確定し、③その住所が「許可したフォルダの中か」だけを確認する。中ならOK、外ならNG。危ない形を数え上げるのではなく、“許す場所”を1つに決めて、そこから出たら全部はじく。この“ホワイトリスト発想”が、パス系では一番強いんだよ。
守る側なら、「正規化+許可ディレクトリ強制+ID参照化」
パストラバーサルの守りは、「パスを必ず正規化」「許可ディレクトリ配下を確認」「そもそも利用者入力をパスにしない」の3層で組みます。
- 入力されたパスを絶対パス化(canonicalize)し、想定外解決を防ぐ
- 正規化後のパスが許可ディレクトリ配下かを
startswith等で必ず確認 - 可能なら、利用者からパスを受け取らずID参照経由でファイルを取得
- アップロード保存名はサーバ側でUUID等に再生成し、利用者入力を保存名に使わない
- シンボリックリンク・特殊エンコード(
%2e等)・Windows区切り(\\)も考慮する - 監査ログで
..を含むアクセスやファイルアクセス例外を検知する

『入力をパスにしない』が、結局一番安全なんだね。

そう。ID→DB→絶対パス、のようにサーバ側で解決すれば、入力に ../ が混ざる余地が消えるよ。
ここまでをひと言で言うと、パストラバーサルの守りは『最終的な行き先で判断する』。見た目の文字を禁止するのではなく、正規化して住所を確定し、許可フォルダの中かを確認する。さらに言えば、そもそも利用者にパスを書かせず、IDで指定させてサーバ側が住所に変換する——これができれば、../ が混ざる余地そのものが消えます。
まとめ:『入力をパスに連結しない』が王道
- パストラバーサルはパスの相対解釈を悪用する攻撃
- 守りは正規化+許可ディレクトリ強制+ID参照化
- エンコード・Windows区切り・シンボリックリンクも考慮する
- 監査ログで
..含むアクセスを継続検知する
今日の持ち帰りは『ファイル名は“住所”ではなく“番号札”で受け取る』です。利用者から生のパスを受け取ると、どうしても ../ の悪用がついて回ります。でも「3番のファイルをください」のようにID(番号札)で受け取り、本当の住所はサーバ側の台帳で引く——この形にすれば、根っこから断てます。受け取り方を変えるだけで、ぐっと安全になります。
次回は、応答が見えない状況下でも1ビットずつ情報を引き出すブラインドSQLiを扱います。
