はじめに
Webアプリケーションの診断中、本来アクセスできないはずのファイル(.bak や .json など)が、拡張子チェックをすり抜けてダウンロードできてしまうケースがあります。
今回は、その代表的な手法であるヌルバイトインジェクション(Null Byte Injection)と、それを支える二重URLエンコードの仕組みについて解説します。
拡張子の制限
https://example.com/ftp/package-lock.json.bak
直接アクセスしてダウンロードしようとすると、以下のエラーが返ってきます。
403 Error: Only .md and .pdf files are allowed!

サーバー側で「末尾が .md または .pdf であるか」というチェックが行われており、それ以外の拡張子は拒否される設定です。
ヌルバイト・インジェクションの攻撃手法
この制限を回避するために、以下のURLでアクセスを試みます。
https://example.com/ftp/package-lock.json.bak%2500.md
末尾に %2500.md を付加するのがポイントです。
なぜこれで突破できるのか?その仕組みの解剖
この攻撃が成立する背景には、
URLデコードが複数回行われること と 言語による文字列の扱いの差 があります。
① URLエンコードの基本ルール
URLエンコード(パーセントエンコード)は、URL内で特別な意味を持つ文字を % + 16進数2桁 で表す仕組みです。
| 文字 | エンコード後 | 意味 |
|---|---|---|
% |
%25 |
パーセント記号自体を指す |
\0 |
%00 |
ヌルバイト(終端文字) |
② 攻撃のステップ
攻撃者が送る値: /ftp/package-lock.json.bak%2500.md
【ステップ1:ミドルウェアによるデコード】
ExpressなどのWebフレームワークがリクエストを受け取った際、最初のURLデコードが行われます。
-
%25が%に変換され、後ろの00と結合 - 結果:
package-lock.json.bak%00.mdとなる
【ステップ2:アプリケーションの拡張子チェック】
サーバー側のプログラムが拡張子をチェックします。
- プログラムは「末尾が
.mdか?」を確認 - 末尾は確かに
.mdなので、「許可されたファイルだ」と判定され、チェックを通過します
【ステップ3:システム層でのファイル読み込み】
チェックを通過した後、サーバーは実際にファイルを探しに行きます。ここで2回目のデコード(あるいはOSレベルでの解釈)が発生します。
-
%00が ヌルバイト(\0) として認識される - ヌルバイトの特性: C言語などの低レイヤー言語において、「ここで文字列は終了」 を意味する
- OSが認識するパス:
/ftp/package-lock.json.bak(\0以降の.mdが無視される)
結果
サーバーは「.md ファイルを確認したはず」なのに、実際に読み込んだのは .bak ファイルになり、ダウンロードが成功してしまいます。
役割まとめ
| 役割 | 担当 | 動作 |
|---|---|---|
| 偽装(二重エンコード) | 攻撃者 | ヌルバイトを %2500 として送信 |
| デコード1回目 | Webサーバー |
%2500 を %00 という「文字列」に戻す |
| 拡張子判定 | アプリ |
.md で終わっていることを確認(合格) |
| デコード2回目 | OS/システム |
%00 を「終端」と見なし、本来のファイルへアクセス |