SQLインジェクションを試してみる

SinatraのDB版のメモアプリの終了条件に「SQLインジェクションへの対策」が含まれていたのでSQLインジェクションできるのか実際に試してみました。

SQLインジェクションとは?

引数などのパラメタにSQL文を混ぜ込んでおき(インジェクション),プログラム内部でそのSQL文を実行させてしまう攻撃手法。 ログインを突破したり、データ・データベースの削除やコンテンツの改ざん(ウイルス・ワームの埋め込み等)、情報の詐取など、被害はさまざま。管理者権限を奪取されることもある。

参照: SQLインジェクションとは ウェブの人気・最新記事を集めました - はてな

原因

SQL内で展開するパラメータからSQLを発行できてしまうことが原因。

  • パラメータに渡す値を文字列ではなくSQLとして認識してしまう。
  • 文字列連結でSQLを組み立てる仕様になっている。

試してみる

試すのはSinatraで作成したメモアプリです。 DBはPostgreSQLを使っておりpg gemでデータ取得するような仕様になっています。 メモ詳細ページ(show)の表示処理を下記のようなコードで実装していました。

get '/memos/:id' do |id|
  @memo = @connection.exec("SELECT * FROM memos WHERE id = '#{id}'")
  erb :show
end

@connection.exec でDB接続の情報を元にSQLを発行します。 SQLを生成する際にブラウザから送信されたid をSELECT文の中で文字列展開しています。 この文字列展開する#{id} にはURLのmemos/ 以降の文字列が展開されるのでブラウザのアドレスバーに直接SQLを挿入できてしまう脆弱性があります。

例1) 投稿された全メモのタイトルを変更する

ブラウザのアドレスバーに下記を入力しアクセスすると全メモのタイトルがSQL injection! に変更されてしまいます。

http://localhost:4567/memos/';UPDATE memos SET title = 'SQL injection!'--

このアクセスにより2つのSQLが発行されます。

SELECT * FROM memos WHERE id = '';
UPDATE memos SET title = 'SQL injection!'--'

次のような理由でこのようなSQLが発行されてしまいます。

  • SQLの終端を表す;を挿入することででSQLを連結できる。
  • SQLのコメントを表す-- を末尾に追加することで不完全なSQLでも実行できる。

例2) 全メモを削除する

ブラウザのアドレスバーに下記を入力しアクセスすると全メモを削除できてしまいます。

http://localhost:4567/memos/';DELETE FROM memos--

このアクセスにより2つのSQLが発行されます。

SELECT * FROM memos WHERE id = '';
DELETE FROM memos--'

こちらも先程と同じように; によるSQLの連結と-- による不完全なSQLを実行できてしまうことが原因です。

まとめ

見事にSQLインジェクションできてしまいました... 原因は理解することができたので次はSQLインジェクションへの対策を行いたいと思います。

参照

安全なウェブサイトの作り方

安全なSQLの呼び出し方

SQLインジェクションとクエリの書き方について考える - Qiita

SQLインジェクション攻撃とは - goruchan’s blog