every Tech Blog

株式会社エブリーのTech Blogです。

トモニテで発生した SQL インジェクション攻撃の記録と教訓

はじめに

こんにちは、トモニテで開発を担当している吉田です。 サービスを運営する上で、セキュリティ対策は欠かせません。

本記事では、実際にトモニテが受けた攻撃の事例をもとに、 異常検知から調査の経緯、攻撃の詳細、そして発見された問題点や今後の対応についてまとめています。

セキュリティリスク

現代の Web サービスにおいて、セキュリティリスクは多岐にわたります。代表的なものとしては、クロスサイトスクリプティング(XSS)クロスサイトリクエストフォージェリ(CSRF)ブルートフォース攻撃などが挙げられます。これらのリスクは、サービスの信頼性やユーザーの安全を脅かす重大な問題となり得ます。 中でも、SQL インジェクションは古くから存在する攻撃手法の 1 つで、昨年にはある企業の会員制サイト内の過去に使用していたページのセキュリティ設定に不備があり、同サイトのデータベースから顧客情報などが漏洩したといった事案も発生しました。(参考

このように、SQL インジェクションは今なお多くのサービスで脅威となっています。ここからは、実際にトモニテで発生した SQL インジェクション攻撃の記録と、そこから得られた教訓について紹介します。

異常検知と調査の経緯

今回、調査のきっかけとなったのは、Web サービスのレスポンスタイムが長くなっているというアラート通知と、ほぼ同時にデータベースの CPU 使用率が高くなっているというアラート通知があったことでした。 実際にログを確認したところ、多くのリクエストに対しては HTTP ステータス 400 のエラーが返されていました。

しかし、「なぜ多くのリクエストが 400 エラーで弾かれているにもかかわらず、データベースの CPU 使用率が上がっているのか?」という点に疑問を持ちました。 このことから、「もしかしたら一部の攻撃的なリクエストがアプリケーションのバリデーションをすり抜けてデータベースまで到達しているのではないか」と考え、さらに詳細な調査を進めました。

その結果、一部のクエリパラメータに対してバリデーションが不十分であったことが判明し、これが攻撃リクエストに対して HTTP ステータス 200 を返してしまう原因となっていたことが分かりました。 では、実際にどのような攻撃が行われていたのか、具体的な内容を紹介します。

受けた攻撃の詳細

詳細な調査の結果、実際に受けた攻撃はクエリパラメータに SQL 文を埋め込むことで、データベースに対して不正な命令を実行させようとするものでした。 この攻撃の特徴は、パラメータに SQL インジェクションのペイロードを仕込むことで、データベースの動作を意図的に遅延させたり、条件分岐を利用して情報を引き出そうとする点です。 特に、DBMS_PIPE.RECEIVE_MESSAGE や sleep 関数など、タイムベースのブラインド SQL インジェクションが試みられていました。

実際にログに残っていたリクエスト例をいくつか挙げます。

1. tag_id にランダムな値を指定したリクエスト

/path?tag_id=dn8r1je0elkm.html&page=1&per_page=20

推測される意図: 一見普通のリクエストに見えますが、tag_id にランダムな値(dn8r1je0elkm.html)が入っています。 これは「存在しない ID」や「想定外の値」を入れることで、アプリケーションのエラーハンドリングやレスポンスの違いを観察し、脆弱性の有無や内部構造を探ろうとしている可能性があります。

2. page パラメータに OR 条件を含めたリクエスト

/path&page=-1 OR 2+138-138-1=0+0+0+1&per_page=20

推測される意図: page パラメータに「-1 OR 2+138-138-1=0+0+0+1」という SQL 文が埋め込まれています。 これは SQL インジェクションの典型的なテストで、「OR」条件を使って本来の条件式を無効化し、任意の条件でデータを取得できるかを試しています。 この場合、2+138-138-1=0+0+0+1 は常に真(1=1)になるため、全件取得や認証回避などが狙いと考えられます。 もしアプリケーションがこの注入を受け入れてクエリを実行した場合、通常はデータが返されないはずの状況(例: 存在しない page -1)でも、1=1 が真になることでデータが返されるといった挙動の変化が起こる可能性があります。 このリクエストは Web アプリケーションが SQL インジェクションに対して脆弱であるかどうかを探る、初期段階の偵察攻撃と考えられます。

3. Oracle の DBMS_PIPE.RECEIVE_MESSAGE を使ったリクエスト

/path?page=1'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'&per_page=20

推測される意図: page パラメータに「'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'」という Oracle 特有の関数が使われています。 これはDBMS_PIPE.RECEIVE_MESSAGEを使って、SQL の実行を 15 秒間遅延させる(タイムベースのブラインド SQL インジェクション)攻撃です。 レスポンスの遅延時間によって、SQL インジェクションが成立しているかどうかを判別しようとしています。

4. MySQL の sleep 関数を使ったリクエスト

/path?page=(select(0)from(select(sleep(15)))v)/'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"/&per_page=20

推測される意図: page パラメータに「(select(0)from(select(sleep(15)))v)」など、MySQL 系の sleep 関数を使った SQL インジェクションが仕込まれています。スリープが正常に(中断なしで)復帰すると、0 が返されます。 これもタイムベースのブラインド SQL インジェクションで、SQL が実行されると 15 秒間レスポンスが遅れるため、脆弱性の有無を確認できます。

いずれも「SQL インジェクションが成立するかどうか」を探るためのテストや、タイムベースの手法で脆弱性の有無を確認しようとする攻撃です。 特に 3・4 番目は、データベースの種類(Oracle か MySQL か)によって使い分けている点も特徴的です。

これらのリクエストは、アプリケーションが入力値を適切に検証・サニタイズせずに SQL クエリを組み立てている場合、データベースに対して不正な操作を行うことができてしまう危険性を示しています。

攻撃の影響とシステムの挙動

調査の結果、基本的には不正なパラメータに対しては HTTP ステータス 400 を返していました。 しかし一部の攻撃リクエストに対して HTTP ステータス 200 が返っていたことがわかりました。 幸い、クエリのビルド時に正規のパラメータのみを受け付けていたため SQL インジェクション自体は成立していなかったことが判明しています。

ただし、攻撃者が仕込んだインジェクションペイロードがそのままアプリケーションに渡っていたことは事実です。

仮に SQL インジェクションが成立していた場合、データベース内の情報漏洩や、サービスの停止、データの改ざんなど重大な被害につながる恐れがありました。 特に、タイムベースのブラインド SQL インジェクションが成立していれば、攻撃者は時間をかけてデータベースの中身を窃取することも可能です。

発見された問題点と今後の対応

今回の調査で、一部のリクエストパラメータに対してバリデーションが適切に行われていなかったことが判明しました。 特に、攻撃リクエストの中には本来不正な値であるにもかかわらず、HTTP ステータス 200 を返してしまっていたものがありました。 これは、パラメータの値に対する十分な検証が行われていなかったことが原因です。 本来、許可された文字列のみを受け付けるべきですが、現状は任意の文字列がそのまま渡ってしまう状態でした。 この問題に対し、該当するパラメータに対して正規の値のみ受け付けるようホワイトリスト(許可リスト)によるバリデーションを追加し、不正な値が渡らないように修正を行っています。

まとめ

今回の事例を通じて、たとえ SQL インジェクションが実際に成立していなかったとしても、入力値のバリデーション不足は大きなリスクとなることを改めて認識しました。 また、調査の中で一部のパラメータに対するバリデーションが不十分だったことが、攻撃リクエストに対して HTTP ステータス 200 を返してしまう原因となっていたことも判明しました。 今後は、入力値に対して適切なバリデーションを徹底し、同様のリスクが他のパラメータにも潜んでいないか定期的に見直しを行っていきます。 セキュリティ対策は一度実施すれば終わりではなく、継続的な改善と運用が不可欠です。今後も気を緩めることなく、サービスの安全性向上に取り組んでいきます。