SinatraのDB版のメモアプリの終了条件に「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が発行されてしまいます。
例2) 全メモを削除する
ブラウザのアドレスバーに下記を入力しアクセスすると全メモを削除できてしまいます。
http://localhost:4567/memos/';DELETE FROM memos--
このアクセスにより2つのSQLが発行されます。
SELECT * FROM memos WHERE id = ''; DELETE FROM memos--'
こちらも先程と同じように;
によるSQLの連結と--
による不完全なSQLを実行できてしまうことが原因です。
まとめ
見事にSQLインジェクションできてしまいました... 原因は理解することができたので次はSQLインジェクションへの対策を行いたいと思います。