イメージ !! これでわかればもうOK!
違った感じに書いた記事の紹介
はじめに
『SQLインジェクション』について記事を書こうと前から思っていたのですが、
まだ材料がな〜ってなってて最近やっとこさ材料が揃ってきたので今回記事にしました!
パチパチパチパチ
どうも!拍手ありがとうございます!(誰もしてねえよ!<=今日も自分でツッコミシステムです!)
ちなみにちゃんとした記事とかないの?ってなる方もいるので以下オススメします!ヽ(´▽`)/どうぞ!
https://www.ipa.go.jp/security/vuln/websecurity/sql.html
https://www.ipa.go.jp/security/vuln/websecurity/ug65p900000196e2-att/000017316.pdf
そもそも『SQLインジェクション』って何よ!
『SQLインジェクション』の『インジェクション』は『入れるやつ』です。
『SQL』を、、『入れて』、、、『攻撃』、、、する攻撃方法を『SQLインジェクション』と言います!(◍•ᴗ•◍)
以下はゆる〜〜いイメージ。
『SQL』を、、SELECT * FROM users WHERE id = 1
『SQL』を、、SELECT * FROM users WHERE id = 1
『入れて』、、本来1
が入るところに1 OR 1 = 1
入れて
『攻撃』、、、(全ユーザーの情報にアクセス成功させてしまう)
ちなみに、
攻撃も色々あって、
アクセスして欲しくないところにアクセスもあるし、
データ丸ごと削除もあるし、
色々だからね!!(◍•ᴗ•◍)
先に攻撃パターンを見たい方は以下リンクに飛んでください。
実際の攻撃を見ていく1
ちなみに他の攻撃方法もあるのかな?
じゃあどうすればいいよの説明は以下の最後の方ですが言語ごとのお作法にのっとた書き方
にが結論です。
どうして攻撃される羽目になってしまったのでしょう? どのようにしとけばよかったのでしょうか?
何が起きたのか!?テーブル作成から攻撃まで紐解いていく!
上記をもう少し詳しく説明していくね! ( ・ὢ・ )
あ!全部理解しないとってならなくてもいいからね!
なんとなく分かってがどんどん重なって行った時にそうか!ってなるから!
あと流れで説明する方がしっくりくる気がしたので頑張ってみてみて欲しいです!
例えばデータベースという、
データ管理する箇所に、
必要なテーブル(ユーザーテーブル)を以下のように作るとします。
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(191) NOT NULL,
password VARCHAR(255) NOT NULL,
name VARCHAR(191) NOT NULL
);
次にそこにユーザーの情報を入れていきます。
INSERT INTO `users` (`id`, `email`, `password`, `name`) VALUES
(1, 'sachiko-abc1@example.com', 'sachikoXXX123', 'momo');
INSERT INTO `users` (`id`, `email`, `password`, `name`) VALUES
(2, 'suzuki-bbb1@example.com', 'suzukiXXX3423', 'yay');
INSERT INTO `users` (`id`, `email`, `password`, `name`) VALUES
(3, 'taro-ss@example.com', 'taroXXX3423', 'ZZZ');
そうすると先ほどの(ユーザーテーブル)に情報が3つ追加されます。
(ちなみにパスワードをそのまま登録する方法は本当はいけないです。)
ログインの時に入れて欲しい情報で特定のユーザーの情報を取得を考えます。
SELECT
*
FROM
users
WHERE
email = 'sachiko-abc1@example.com'
AND password = 'sachikoXXX123';
実際にデータを取得してきた時のイメージです。
でここでどういうSQLを組み立てればいいか分かった開発者は今度はコードで以下のようにアクセスするようなものを書きました。
emailもpasswordもユーザーしか知り得ないから問題なさそうですね。これでユーザーは自分のIDとか分かるようですね。
※以下はイメージです。
※取得したものを画面で表示するといったシステムとしてください。
async find(email: string, password:string): Promise<users[]> {
let selet = `SELECT * FROM users
WHERE email = '${email}' AND password = '${password}';`;
return await this.usersRepository.query(selet);
}
やった〜いい感じに開発できたぞ〜〜!
本当にそうでしょうか?
実際の攻撃を見ていく1
このシステムこんな感じでSQL打てば全ユーザー取得できるのでは????。
なんと悪いやつがやってきて、
emailとpasswordに以下のような入力をしました。
email : ' OR '1' = '1
password : ' OR '1' = '1
どうなったのでしょうか?
実際に正常な値を当てはめられた時と上記のように当てはめられた時を見てみましょう。。
[正常]
SELECT * FROM users
WHERE email = '${email}' AND password = '${password}';
SELECT * FROM users
WHERE email = 'sachiko-abc1@example.com' AND password = 'sachikoXXX123'
[異常]
SELECT * FROM users
WHERE email = '${email}' AND password = '${password}';
SELECT * FROM users
WHERE email = '' OR '1' = '1' AND password = '' OR '1' = '1'
以下画像からもわかるようにユーザー情報が全て抜き取られてしまいました、、、。
ちょろいな!全部取得達成したぜ!このメールアドレスを売って一儲けするかな!
この攻撃こそが『SQLインジェクション』で、
このような攻撃ができるシステムが『SQLインジェクション』が出来てしまうシステムです。
どうして攻撃される羽目になってしまったのでしょう? どのようにしとけばよかったのでしょうか?
まずユーザーが入力出来るテキストでSQLを組み立てられてはいけません。
ん!?どういうこと?
なので上記のような入力をしてもSQLを組み立てられてはいけません。
ん!?どういうこと?
例えばSQLで認識される文字を認識されないような書き方に
システムで変換できればいいのではないでしょうか?
[異常]
SELECT * FROM users
WHERE email = '${email}' AND password = '${password}';
-- システムで以下のように変換してから入れる
-- email : ' OR '1' = '1
-- password : ' OR '1' = '1
-- ↓
-- email : \' OR \'1\' = \'1
-- password : \' OR \'1\' = \'1
SELECT * FROM users
WHERE email = '\' OR \'1\' = \'1' AND password = '\' OR \'1\' = \'1';
お〜〜〜なんということでしょう!SQLと認識されなく無事ユーザー情報が抜き取られなくなりました!
問題のSQLと比較してみましょう。
OR
や=
がただの文字列として認識されています。
このように『ユーザーから入力されたもの』はただの文字列として認識させるようなコードを書けばいいのです!
ちなみにですが、これは自分でそのようにというより、言語とかにそういう機能が携わっているので
そのお作法に則った書き方にすれば良いとのだけです!(◍•ᴗ•◍)
今回の実装では以下の//OK
の方の実装にすべきだったということなのです!
なのでユーザーの入力をSQLに入れる時はそれぞれ書く言語のお作法が必ずあるのでそれを検索してそのように実装していきましょうということです!
//NG
async find(email: string, password:string): Promise<users[]> {
let selet = `SELECT * FROM users
WHERE email = '${email}' AND password = '${password}';`;
return await this.usersRepository.query(selet);
}
//OK
async find(email: string, password:string): Promise<users[]> {
let selet = `SELECT * FROM users
WHERE email = ? AND password = ?;`;
//『''』で囲まれ代入されるため『''』で囲むという記述はありません。
let params = [email, password];
return await this.usersRepository.query(selet, params);
}
ちなみに他の攻撃方法もあるのかな?
それでもやっぱりお作法がめんどくさくてお作法に乗っ取らないという方もいるかと思うし、
最悪このパターンなら大丈夫というのもあるっちゃありますが色々な攻撃方法を知ることでお作法に乗っ取ろうという方が増えて欲しいなということで今回ここに色々な攻撃パターン書いてみます!ヽ(´▽`)/
へへへっ。このシステムの信頼無くすためにユーザー情報全削除できそうなSQL試してやろう!
email : a@example.com
password : '; TRUNCATE users; --
[SQLインジェクション成功]
SELECT * FROM users
WHERE email = '${email}' AND password = '${password}';
SELECT * FROM users
WHERE email = 'a@example.com' AND password = ''; TRUNCATE users; -- ';
攻撃成功してしまいましたね。
最初でレコード取得できないが後でうまくSQL閉じて空にするSQL実行に成功してしまいました。
果たして文字数制限等だけでSQLの攻撃は避けられたでしょうか?ちなみに最後の--
がなくてもSQLエラーになれど、削除は成功してしまうかと、、、
へへへっ。このシステムの信頼無くすためテーブル削除してやろ!
email : a@example.com
password : '; DROP TABLE users; --
[SQLインジェクション成功]
SELECT * FROM users
WHERE email = '${email}' AND password = '${password}';
SELECT * FROM users
WHERE email = 'a@example.com' AND password = ''; DROP TABLE users; -- ';
攻撃成功してしまいましたね。
usersテーブルがなくなってしまいました。
ちなみにこれまではテーブル名を予測されたていで攻撃してみました。
だから攻撃するテーブル名がわからなければと思う方もいるかと思います。
確かに一理あります。
ですが、
実際の全SQLを把握していているわけではないので攻撃方法があるかもしれません。
あと、MYSQLのバージョン上がってそういった取得もできるようになってしまうこともなきにしもあらず。
なので私は基本的にお作法に乗っ取った書き方にすることをオススメします。
最後に
今回の記事を書いたのは、
より『SQLインジェクション』について知って、
より安全なシステムを作成していただく世界を望んだからです。
『SQLインジェクション』だけに関わらず全部を完璧に理解は難しいと思っています。
この記事を誰かの批判のために使わないようにしていただければと思います!
知っていたら教えてあげる。
知らなかったら教えてもらう。
そんな感じでいい感じのシステムがどんどん増えて行ったら嬉しいなと思います!
よろしくお願いいたします!
おまけ1
今回SQLのお作法で文字をエスケープする感じの説明しかしませんでしたが、
データベースエンジン側で値を割り当てる方式、すなわちSQL側でここにくるのは文字としてしか認識しないよもあるようです。
っと言いましたがそこよりも使う言語にお作法があるのでそれを使うようにすれば問題なしの認識です。あとはその言語がいい感じにその処理をしてくれるかと。
おまけ2
いうてもそんなSQLインジェクションで攻撃された実例なんてないんでしょう?と思うかと思います。
しかしニュースとか調べたら出てくるのでそうでもないようです、、
おまけ3
今回の説明をみていただけたらわかるかと思いますが基本的にこの言語なら起きない的なことはないの認識です。
最後の最後に
ここまで記事を読んでいただきありがとうございました!
色々わからないこと多いけどお互い頑張っていきましょう!
鬱にならないようにお互い頑張りましょう!