Webの世界では、データはデータベース(DB)に格納されており、データベースのデータの取り出しや編集をするには、SQL文というものを実行します。
より具体的には、画面で入力された情報をもとに、内部でSQL文を完成させて実行し、データのピックアップや編集を行っているのです。
それでは、「入力画面に悪意のある情報が入力されると、予期していないSQLが生成・実行されてしまい、全ユーザ情報が表示されてしまうことさえある」と聞いたら、あなたは信じられますか?それがSQLインジェクションという攻撃手法です。
本記事では、予備知識がない方向けに、SQLインジェクションについて解説します。
1.SQLインジェクション、その前に
SQLインジェクションを知るには、まずはSQLとは何かを知る必要があります。SQLとは何をするものなのか、少しだけ見ておきましょう。
(1) 「SQL」という言語がある
ブログサイトであれば、ユーザ情報や投稿内容、コメント、誰がどの記事にいいねした、などといった情報を持ちます。ショッピングサイトでは、さらに商品や購入履歴の情報もあるでしょう。これらのデータはすべて「データベース」というものに格納されます。
そのデータベースに対してデータの読み出しや更新、削除などといった操作をするために「SQL」という言語を使います。これはWebサイトが何の言語を使って開発されているか(JavaやPHP、Ruby等)には関係ありません。
Ruby on Railsのように、SQLそのものがソースコードに出てこない場合もありますが、背後でしっかりとSQLの作成・実行がされています。
(2) 画面で入力された情報でSQLを生成する
SQLが生成・実行される過程をもう少し細かく見てみましょう。
画面で商品コードがaaaという商品を検索する場合を考えます。ユーザは商品コード欄にaaaと入力して、検索ボタンを押下します。
すると、システムはaaaという情報を受け取り、それをあらかじめ準備してあった半完成のSQL文に連結して一つのSQLを生成します。半完成のSQL文とは以下のイメージです。
これに対して、aaaという入力値を受け取って、以下のように実行するSQL文を完成します。
このようにしてSQLが生成・実行されるのです。
2.SQLインジェクションとは
ここからは、いよいよSQLインジェクションについて解説します。むずかしい話ではないので、気軽に読み進めてください。
(1) SQLインジェクションとは何か
先の説明のように、ユーザが普通の入力をしてくれれば普通に動作します。つまり商品コード欄に商品コードを入力してくれればよいのです。
しかし悪意のある人が、こんな入力をしたとします。
一見すると呪文のようなこの文字列、次のようなSQL文を作り出します。
お分かりでしょうか?論理式的にOR TRUEが勝ってしまい、code = 'a'は無視されます。aでもbでも一緒です。SQL文全体としては、検索条件がかかっていないのと同じ意味になり、商品マスタの全レコードが返却されます。商品マスタだからまだマシですが、これがユーザ情報となると、大変ですよね。
もっと最悪パターンとしては、こんな入力をしたとします。
こんなSQLができます。
最初のセミコロン「;」で文が分かれます。前半のSELECT文が実行されたあと、後半のTRUNCATE文が実行されて、何と商品マスタをすべて消してしまいます。Webサイトやその運営会社に強い恨みを持つ人ならやりかねませんね(笑)
上記の例はTRUNCATE文なのでテーブルが空になるだけですが、DROP DATABASE文など実行されてしまうとすれば…。考えただけでも怖いですね。
DROP DATABASEを実行しようにもDB名が分からないのでムリ?必ずしもそうとは言えないのです。あえて文法エラーとなるような入力をして、エラーメッセージが画面に出てしまうとどうでしょうか?データベースによってはDB名が出るかもしれません。
(2) SQLインジェクションの攻撃実例
本記事の執筆にあたって、ネット上で「SQLインジェクション 被害」で検索して過去の攻撃事例を調べてみました。被害を受けたSQLの実物を公表している事例はさすがにありませんでしたが、多数ヒットします。ざっと上げてみましょう。
2016年7月15日 Ubuntu
https://ubuntu.com/blog/notice-of-security-breach-on-ubuntu-forums
2020年4月16日 光言社
https://www.kogensha.jp/information/detail.php?id=1612
ここまで本記事を読んでみて、SQLインジェクションは本質的には単純なものであることがお分かりでしょう。しかし、前者はLinuxディストリビューションの中でも特に有力なUbuntuの開発元です。そんなすごい技術を保持した団体でも、被害に遭うのです。後者にいたってはさほど前の話ではありません。
SQLインジェクションの脅威は、未だに健在なのです。
3.SQLインジェクションの対策
SQLインジェクションは、前述のように基本的には単純な手法です。防御する方法を解説します。
(1)まずはバックで動いているSQLを見る!
言語・フレームワークを問わず、Web系プログラマすべてに言えることです。「実際に生成、実行されたSQL文を見る!」これはとても大事なことです。
言語・フレームワークによってはSQLをいっさい書かなくてもよいものもあります。しかし見えないところでどのようなSQLが実行されているのか、これは必ず確認するようにしてください。
Ruby on Railsを例に上げてみましょう。ユーザ名がnameというユーザを抽出するコードはこうなります。
実際に生成されるSQLは、ターミナル上に表示されています。
<図1>
Railsに限らず、この例のように背後の動きを確認する仕組みはたいていの言語・フレームワークにあるはずです。それらを利用して、いわゆる「あぶない入力」を試して、どのような挙動になるのかを確認するのが第一歩になります。
(2) 言語やフレームワークによって対策はさまざま
では、どのような対策をとることができるのでしょうか?
本格的に対策しようと思えばWAF(Web アプリケーションファイヤウォール:通信内容を確認し、悪意のある情報なら無効化する)の導入や、セキュリティ脆弱性を確認するペネトレーションテストを実行してセキュリティホールを閉じるのがよいでしょう。
しかしここでは、言語やフレームワークレベル、つまり現場レベルでできる対策を考えてみましょう。
(3)プレースホルダの使用
SQLインジェクション対応の王道とも言うべき「プレースホルダ」というものがあります。
言語やフレームワークによって対応がむずかしいこともありますが、一応解説します。
先の攻撃例は、「画面で入力された値とSQLを連結し、1つのSQLとして評価・実行する」ところに問題がありました。
先の例であるRuby on RailsのSQL文をもう一度見てみましょう。
<図1−1>
ここで、SQL文において値を指定する箇所は「?」になっています。これはプレースホルダと呼ばれます。「?」の箇所以外は完全に固定化されていて、あやしい入力が入ってきてもSQL全体へ悪影響を及ぼしません。
別の角度から説明します。仮に「a' OR TRUE」という文字が入ってきてもあくまで「a' OR TRUE」という一つの文字列としてしか解釈されないので、SQL文全体への影響はありません。試しに上記の入力をしてみます。
<図2>
nameが「a' OR TRUE」の人を検索しようとしています。あくまで検索条件としてこの文字列を見ているだけなので、SQL文全体は変化していません。
(4)その他の「当たり前」ルール:エラーメッセージの詳細を表示させない
何らかのエラーが発生して、DB名やテーブル名、カラム名などをブラウザにガッツリ表示させてしまうと、悪意のある人に攻撃の手がかりを与えてしまいます。
(5)その他の「当たり前」ルール:権限の設定
ユーザがDROPやTRUNCATEを実行するケースはないはずです。よって、アプリからDBへアクセスするときの権限を限定してしまったほうが安全です。
4.まとめ
SQLインジェクションは、セキュリティ専門家だけが対応すべき問題ではありません。現場のプログラマやインフラエンジニアレベルでも十分対応すべきことがあります。
本記事を通じて、SQLインジェクションとは何かを理解し、対策を考えるきっかけにしていただければ幸いです。
- カテゴリ:
- SQL
- キーワード:
- プログラミング
- SQL
- SQLインジェクション