こんにちは!セキュリティの世界へようこそ。
新人のIT担当者や、これからセキュリティの勉強を始める開発者の皆さん、日々の開発やインフラの管理お疲れ様です。
「セキュリティ」と聞くと、なんだか難解な暗号や、ハッカーが映画のように猛スピードで黒い画面を叩いている姿を想像して身構えてしまいませんか?
でも、安心してください。セキュリティの基本は、私たちが普段の生活で何気なくやっている「戸締まり」や「来客の確認」とまったく同じなんです。
今回は、最近のWebアプリやAPIサーバーでひそかに狙われやすい「XXE(XML外部エンティティ)攻撃」という手法について、身近な例えを交えながら、一歩ずつ優しく紐解いていきたいと思います。
—
1. 家の鍵に例える「XML」と「外部エンティティ」の仕組み
まずは、今回の主役である「XML」という言葉と、攻撃のターゲットになる仕組みを、私たちの身近な「一軒家」に例えて考えてみましょう。
XMLってなに?
XMLは、データを綺麗に整理してやり取りするための「箱(封筒のようなもの)」だと思ってください。
例えば、APIサーバーに対して「この商品を買いたいです」と伝えるとき、以下のような箱に入れて送ります。
<?xml version="1.0" encoding="UTF-8"?>
<order>
<item>りんご</item>
<quantity>3</quantity>
</order>
このように、タグ(< と > で囲まれた文字)を使って、データの意味をわかりやすく整理できるのがXMLの便利なところです。
便利な機能「外部エンティティ」が持つリスク
さて、このXMLには、実はちょっと厄介な「便利な機能」が備わっています。それが「外部エンティティ」です。
これは、XMLの中に「別の場所にあるファイルや、インターネットの住所(URL)を読み込んで、その中身をここにペタッと貼り付けてね」と指示できる機能です。
これを私たちの生活に例えてみましょう。
あなたは家にいて、郵便受けに「ご近所さんからの伝言:リビングの机の上にある合鍵を取ってきて」と書かれたメモが入っていたとします。
もし、あなたがそのメモの指示通りに、わざわざ家の中(しかも普段は入ってほしくない秘密の部屋)から合鍵を取ってきて、郵便受けの前に晒してしまったとしたら……どうでしょう?ゾッとしますよね。
XXE攻撃とは、まさにこれと同じことをサーバーに対して行わせる悪巧みなんです。
—
2. 泥棒はこうやって入ってくる!XXE攻撃のメカニズム
攻撃者は、APIサーバーが受け取るXMLの中に、先ほどの「合鍵を取ってこい」という危険な指示(外部エンティティの定義)をこっそり混ぜ込みます。
例えば、サーバーの内部にある重要な設定ファイル(パスワードなどが書かれている /etc/passwd など)を盗み見ようとする場合、攻撃者は以下のようなリクエストをAPIに送りつけます。
<?xml version="1.0" encoding="UTF-8"?>
<!-- 宣言部分で「file」という名前の外部エンティティを作り、サーバー内の秘密のファイルを指定する -->
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<order>
<item>&xxe;</item> <!-- ここで先ほどの秘密のファイルを呼び出して表示させる -->
</order>
もし、サーバー側のプログラムがこの指示を素直に聞いてしまうとどうなるでしょうか?
APIサーバーは、自分の足元にある大切な機密ファイルを読み出し、それを返事(レスポンス)の中に混ぜて攻撃者に送り返してしまうのです。
玄関の鍵を開けっぱなしにしていたために、泥棒に入られて大切な通帳を勝手に持ち出されてしまったような状態ですね。これがXXE攻撃の恐ろしい仕組みです。
—
3. 開発現場でできる!泥棒をシャットアウトする具体的な対策
「うわ、怖い!どうすればいいの?」と思いますよね。
でも大丈夫です。しっかりとした「戸締まり(対策)」をすれば、この攻撃は完璧に防ぐことができます。
基本の対策は非常にシンプルです。「外部からの指示で、勝手にサーバー内のファイルや外部のURLを読みに行かないで!」と、XMLを読み込むプログラム(XMLパーサー)に命令することです。
それでは、実際のプログラム(PHPを例にします)でどのように設定するのか、コードを見てみましょう。
対策済みの安全なPHPコード例
PHPでXMLを安全に処理するための設定サンプルです。
<?php
// 1. 外部エンティティの読み込みを無効化する(これが一番重要!)
// これにより、勝手にサーバー内のファイルを見に行くのを禁止します。
libxml_disable_entity_loader(true);
// 2. 外部XMLの読み込みをブロックする安全な設定でXMLを読み込む
$xmlInput = file_get_contents('php://input');
// LIBXML_NOENTなどを外し、安全なフラグを使用する
$dom = new DOMDocument();
$loaded = $dom->loadXML($xmlInput, LIBXML_DTDLOAD | LIBXML_NOENT);
if ($loaded === false) {
// XMLの解析に失敗した場合のエラー処理
http_response_code(400);
echo "不正なXMLリクエストです。";
exit;
}
// 安全に処理を継続する
$data = simplexml_import_dom($dom);
echo "正常に処理されました: " . $data->item;
?>
他の言語やフレームワークでのポイント
PHPに限らず、JavaやPython、Node.jsなどのモダンな環境でも、XMLを処理するライブラリには必ず「外部エンティティ(DTDや外部Dtd、外部参照)をオフにする設定」が存在します。
- Java (DocumentBuilderFactoryの場合):
// 外部DTDの読み込みを禁止する設定
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
- Python (defusedxmlなどの利用):
標準の xml ライブラリの代わりに、安全性が考慮された defusedxml パッケージを使用するのが業界のベストプラクティスです。
—
4. まとめ:今日からできる一歩を踏み出そう
いかがでしたでしょうか?
XXE攻撃と聞くと難しく感じられたかもしれませんが、要するに「外部から送られてきた怪しい指示に従って、サーバーの中身をペタッと貼り付けて見せてはいけない」というお話でした。
実務でAPIサーバーやXMLを扱う機会があれば、以下のチェックポイントを思い出してみてくださいね。
1. XMLを処理する際、外部エンティティ(外部参照)が有効になっていないか確認する
2. 可能であれば、JSONなどXML以外の安全なデータ形式への移行を検討する
3. 使用しているフレームワークやライブラリのセキュリティ設定を最新の状態に保つ
セキュリティ対策は、一度にすべてを完璧にする必要はありません。日々の開発の中で「あ、ここはちゃんと戸締まりできているかな?」と気にする癖をつけることが、何よりも強力な盾になります。
一歩ずつ、確実に安全なシステムを作れるエンジニアを目指して一緒に頑張っていきましょう!
コメント