概要
Webアプリケーションのユーザーの入力領域に
攻撃者が不正なSQL文を注入し、データベースに意図しない命令を実行させる攻撃手法。
- ログインフォームや検索機能などを持つWebサイトは特に注意する。
発生しうる脅威
重要情報の漏洩
データベースに格納されている重要な情報(パスワードやカード情報など)が盗まれる。
企業の信頼を大きく損なうことにもつながる。サイトの改ざん、削除
偽の情報を表示したり、情報を消去することができる。データベースの全消去・破壊
DELETE、DROP文などを使い、データベースごと削除されるシステムの乗っ取りや、攻撃の踏み台にされる
管理者権限を奪われ、サーバーを他の攻撃に使われる。
--脆弱性のあるコード SELECT * FROM users WHERE id='$id' AND pass='$pass'; ↓ --正常な入力 SELECT * FROM users WHERE id='John' AND pass='123pass'; --攻撃者の入力 SELECT * FROM users WHERE id='' or 1=1; -- 'AND pass='123pass';
☀︎攻撃者が$idに「' or 1=1; --」と入力されると、
- $idは空文字または1は1に等しいという条件になり常に真となる。
- --以降はコメント扱いになるためパスワードの条件は無効になる。
→そのため本来は通らないユーザー認証が成功してしまう。
原因
ユーザーからの入力値をそのままSQL文に埋め込むこと
対策
根本的解決
1.プレースホルダーとプリペアドステートメントで実装する
PDOでのプレースホルダー使用例
//プレースホルダー付きSQL文を準備 $stmt = $PDO->prepare("SELECT * FROM users WHERE id = :id AND pass = :pass"); //値をバインド $stmt->bindValue(':id', $id); $stmt->bindValue(':pass', $pass); //実行 $stmt->execute();
2.エスケープ処理
データベースに値を渡す前に、特殊文字を安全な形に変換する。
「’」→「’’」、「¥」→「¥¥」など
PHPでは $mysqli->real_escape_string($input) を使うことができる。
3.hiddenパラメータで直接指定しない。(禁忌)
ユーザーからの入力を直接SQLに組み込むのは避ける
保険的対策
4.エラーメッセージ表示しない
攻撃者に内部構造(テーブル名など)の情報を与えないようにする。
エラーメッセージは表示せず、ログファイルに記録する。
5.データベースの権限を最小限に設定する
DBユーザーには必要最小限の操作だけ許可する。
SELECT操作で十分であれは、INSERTやDELETE文の権限までは付与しない。
Laravelでの実装例
1.Eloquent ORMを使う
Laravelには、「Eloquent ORM」というPDOのプリペアドステートメントを利用した機能が搭載されている。
モデルクラスとしてデータベースのテーブルと連携し、SQL文を直接書かずにデータベースを操作できる。
さらに詳細なクエリや条件を指定したいときは、「クエリビルダー」を使う。
- 生SQLを使うときは、プリペアドステートメントを利用したり、入力値のバリデーションやエスケープ処理を徹底する。
実装の流れ
①あらかじめモデルでデータベーステーブルと連携しておく
Post.phpファイルに
//Postクラスの作成
class Post extends Model
{
//テーブル名を指定
protected $table = 'posts';
}
②コントローラーでPostクラスを使う
PostController.phpファイルに
class PostController extends Controller
{
public function index() {
//postsテーブルのデータを全件取得
$posts=Post::all();
return view('posts.index', compact('posts'));
}
}
③ビューで$postsを使い表示させる
posts/index.blade.phpファイルに
@foreach($posts as $post) <div> <h2>{{ $post->title }}</h2> <p>{{ $post->body }}</p> </div> @endforeach