サイバーセキュリティ

 

セキュリティ勉強会などの打ち上げで、SQLインジェクションが怖いどうやってやるのか教えて欲しいという話をされることがあります。

そんなわけで、自分の勉強の為にも普段行うPHP/MySQLの記述でSQLインジェクションが発生しないか検証して記事にしてみました。もし尋ねられてもこの記事を見せれば良いわけだと画策ですよ。

 

SQLインジェクションとは

結論から書くとSQLインジェクションの基本はアプリケーションのDBに不正なクエリを流し込むことで、開発者が意図しない結果を成立させてしまう攻撃です。

 

例えば・・・

 

上記のクエリが実行されるフォームのid項目に【999 OR 1 = 1; — 以降はコメントになります】を入力する。

すると — の後はコメントになるので、パスワードに何も入力せずともクエリが成立します。実際に、自分の環境でSQLインジェクションさせてみると感覚的にわかりやすい。

 

 

SQLインジェクション攻撃されるとどうなっちゃうの?

  • 顧客データの漏洩
  • データの改ざん、削除
  • 会員専用画面、管理画面へのログイン
  • DoS攻撃

00021, 12234などの連番、admin, user1, appleなど、ユーザIDが予想できるもの、ユーザ名の重複を通知してくれるアプリケーション。これに関してパスワードの部分にSQLインジェクションを利用することで狙ったユーザに対してログインが出来てしまいます…!

 

また、データ量が多いDBに対して、負荷のかかるクエリも打てるので、DoS攻撃に使われるとやっかいです。

 

改ざんはもちろん、SQLインジェクションにより、途中でクエリを終了させ、DELETE文を混入させることで、テーブル丸ごとを削除するといったことも可能です。

 

※SQLインジェクションの検証が出来なくなるので、一般的なフォームのバリデーションは行っていません。Nullも許容します…!

 

予防するには

SQLインジェクションはDBMSの仕様*です。

だからシステムの要件を考えながら、意図した動作のみをただ行うように、セキュリティに配慮をした適切なプログラミングによる実装を行うことが大切。

具体的には、DBに入ってくるフォームやパラメータのバリデーションチェック、プログラムの書き方、フレームワークが用意してくれている関数の利用で予防できるはずです。

 

*バグと表現するのは実装側。

 

IPAが教えてくれるSQLインジェクションの対策

  1. SQL文の組み立ては全てプレースホルダで実装する。
  2. SQL文の構成を文字列連結により行う場合は、アプリケーションの変数をSQL文のリテラルとして正しく構成する。
  3. ウェブアプリケーションに渡されるパラメータにSQL文を直接指定しない。

@see セキュリティ実装チェックリスト

 

つまりどういうことなんでしょう?ソースコードがないとイメージしにくいですよね。今回はPHPを使って実際のコードを掲載します。

 

流れとして、

  1. SQLインジェクション攻撃が出来ることを体験してみる。
  2. 更に攻撃が通る記述と、対策済みの記述を確認を行います。

 

残念ながら私は毎日業務でプログラムを書いているわけではないので、色々やっているうちに忘れてしまう危険があります・・・!!そんなわけで、プログラム系もぼちぼち記事にしていこうと思います。

 

 

さっそく実践してみます。

では早速実践、簡単なデータベース構造と簡単なスクリプトを用意して検証してみます。

 

テーブルの作成

今回はサクっと簡単に以下を用意しました。

 

 

検証用プログラムの設置

次にスクリプトの用意です。今回はPHPで簡単なクラスを用意してみました。

 

 

 

999 OR 1 = 1

SQLインジェクション

 

SQLインジェクション

 

適当なIDとなる数字にOR 1 = 1をつけて、条件文を成立させてしまう。

 

 

999 OR 1 = 1; —

 

今回はプログラム側でidのフォームにint型に型指定をしてしまっているので、検証用スクリプトそのままでは攻撃用のクエリが通りませんが、型指定をしないか、string型を指定すると上記のSQLインジェクションが通ります。

↓変更

 

これでコメントを使用したインジェクションが通るようになります。

 

 

 

SQLインジェクション対策済み関数を利用してみる

 

SQLインジェクション

フォームにチェックを入れることで、SQLインジェクション対策を行った関数を選択出来るので、チェックを入れて【送信】をクリックします。

 

SQLインジェクション

存在しない無効な値なので、何も表示されません。

理想的な結果が返りました。

 

 

何が違うのか

 

 

ここが大事

データベース接続時の処理にて、静的プレースホルダにしているのは共通です。

 

 

IPAが教えてくれるSQLインジェクションの対策

  1. SQL文の組み立ては全てプレースホルダで実装する。
  2. SQL文の構成を文字列連結により行う場合は、アプリケーションの変数をSQL文のリテラルとして正しく構成する。
  3. ウェブアプリケーションに渡されるパラメータにSQL文を直接指定しない。

@see セキュリティ実装チェックリスト

 

ここを踏まえて比べてみます。

 

SQLインジェクション対策なし

 

 

SQLインジェクション対策あり

  • prepare()でSQL文をセット
  • 入力される変数に対してバインド機構を利用し、更に型を指定

 

 

バインド機構とは、

あらかじめSQL文のひな型を用意し、後から変動個所(プレースホルダ)に実際の値(バインド値)を割り当ててSQL文を生成するデータベースの機能だ。SQL文のひな型とバインド値は個別にデータベースに送られ、構文解析されるので、バインド値に悪意あるSQL文が挿入されても、その実行を阻止することができる(図1-2)。

SQLステートメントを書く時に必ずバインド機構を使用すれば、バインド機構に不具合がない限り、SQLインジェクションは不可能だと考えられる。

@see SQLインジェクションの対策

 

対策まとめ

  • 静的プレースホルダを指定する
  • prepare()でSQL文をセット
  • 入力される変数に対してバインド機構を利用し、更に型を指定

上記を忘れずに行うことが重要。

 

今回はDBへのクエリ対策が主でしたが、バリデーションや出力時のエスケープ処理などきちんと対応を行う必要があります。セキュリティ意識したプログラミングは行数が増えますが、きちんと記述したいところですね!

お疲れ様です。

 

 

 

セキュリティ企業と連携して、アプリケーション診断やセキュリティサーバホスティングサービスなど行っています。ご相談下さい。

金広 優(エンジニア)
この記事を書いた人:金広 優(エンジニア)

システムガーディアン爆弾処理班。アクセス負荷対策やNginxへの移行案件が多いこの頃。IBM SoftLayerやAWSなどクラウド案件も多くなってきました。