概要
本日はPythonセキュリティシリーズということでPythonを用いて構築したWebサイトに対してSQLインジェクションを行います
なお、SQLインジェクションは私自身のPC内に構築したサーバに対して行っております
他者が運営しているWebサイトに対してSQLインジェクションを行うことは犯罪ですので、決して行わないでください
では解説を開始します
SQLインジェクションの概要
まずはSQLインジェクションについて簡単に解説します
SQLとはデータベースを操作する言語のことです。そしてインジェクションとは注入を意味します
具体的にはWEB画面における入力フォームに不正なSQL文を注入することで、
データベースの削除、改ざん、情報の不正取得を図る攻撃手法のことです
前回の動画では通信を暗号化する手法を解説しましたが
SQLインジェクションは通信が暗号化されていても防ぐことができませんので、別途対策が必要です
では、具体的にSQLインジェクションを試してみましょう。
SQLインジェクションの実践1
今回はあらかじめ脆弱性が存在するWebサイトを用意しました
こちらはGithubに登録しておきますのでよろしければご参考にしてください

まず、こちらのWebサイトをWindows PowerShellから起動します

そして、タスクを検索のフォーム部分に注目します。
これは、単語を入力すると、タスク内容に部分的に一致するTodoのみを表示するようになります
試しにPythonと入力し、検索ボタンをクリックしてみましょう
Pythonという文字に部分的に一致するタスクは「Pythonの勉強をする」と
「Pythonの本を読む」なので、この二つが表示されます
この時、データベースに格納されているTodoリストから「Python」という文字列を含むものだけをSQLで抽出しております

ではSQL文はどのようになっているのでしょうか
select * from todos where todo like ‘%{param}%’となっております。
このparamの部分は「タスクを検索」のフォームに入力した文字列が入ります

先程の例では「Python」を入力したのでselect * from todos where todo like ‘%Python%’というようになります
このSQL文はtodosテーブルからtodoカラムがPythonを含むデータを取得するという意味になります
では早速SQLインジェクションを試してみましょう

まずは試しにサーバ内のユーザを全て削除してみます
先程は入力フォームに「Python」と入力しましたが、今度は「’;delete from user where name like ‘」と入力してみましょう

検索をクリックするとtodoタスクが表示されなくなってしまいました
ブラウザの更新ボタンをクリックするとログイン画面に戻ってしまいました
再度、ログインをしようとしてもログインができなくなっています

ツールでデータベースのuserテーブルをのぞいてみましょう。
すると、userテーブルが空になっていることがわかります
SQLインジェクションが成功し、意図通り、ユーザを全て削除できたようですね
画面からの操作で簡単にデータベースの情報を削除できるなんて恐ろしいですが、なぜこのようになったのでしょうか
先程と同様にSQL文がどのようになっているのか見てみましょう
SQLインジェクションの仕組み
SQL文は先ほど見た通りselect * from todos where todo like '%{param}%'
となっております。
そして、今回の例では「';delete from user where name like '
」と入力したので
SQL文は「select * from todos where todo like '%';delete from user where name like '%'
」というようになります
これはどういう意味でしょうか?
まず、今回注目すべき部分は;が含まれていることです

SQL文において;はSQL文の終わりを意味します。
つまり、上記の文は「select * from todos where todo like ‘%’;」と
「delete from user where name like ‘%’」の二つの文が含まれるということです
前半の「select * from todos where todo like ‘%’;」の意味を考えてみましょう
where todo like ‘%’とはどいう言う意味でしょうか。とくに%とは何でしょうか
%はワイルドカード、つまり、任意の長さの文字列を意味します
likeはあいまい検索を行うための文法でワイルドカードが使われるときには=の代わりにlikeを使います
従って、where todo like ‘%’はtodoが任意の長さの文字列であるという条件です
todoが任意の長さの文字列という条件の場合、すべての文字列が条件に当てはまります
つまり、「select * from todos where todo like ‘%’;」はtodosテーブルから全データを取得するという意味です
このSQL文は特に害を及ぼさない文ですので、次のSQL文「delete from user where name like ‘%’」の意味を考えてみましょう
where name like ‘%’となっているので、先程と同じようにすべてのデータが条件に当てはまります
つまり、「delete from user where name like ‘%’」はuserテーブルから全データを削除するという意味になります
このSQL文のせいでユーザーが全て削除されるという被害をWebサイトが受けることとなりました
SQLインジェクションの対策1
では、これの何が悪かったのか考えてみましょう
通常実行されるSQL文は「select * from todos where todo like ‘%Python%’」
SQLインジェクションによって実行されたSQL文は
「select * from todos where todo like ‘%’;delete from user where name like ‘%’」でした
この二つの大きな違いが何かわかりますか?
正解は前者がSQL文が一つしか実行されていないのに対して、後者はSQL文が複数実行されていることです
つまり、SQL文が一つしか実行されないようにしておけば、このSQLインジェクションは防ぐことができます

SQL文はexecutescriptで実行しています。このメソッドは複数のSQLが含まれていても実行されてしまいますので
executescriptからexecuteに変更しましょう。するとどうなるでしょうか?


先程と違ってInternal Server Errorが表示されるようになりました。
禁止されている複数のSQL文実行を行ったためエラーが発生したようです
ブラウザバックをし、確認してみるとtodoタスクは閲覧可能になっています
ログアウト、ログインも依然として可能であることからユーザの削除はされていないことがわかります
これで一応、このSQLインジェクションは防げたようです
SQLインジェクションの実践2
それではこれでSQLインジェクションの心配はないのでしょうか?いやそうではありません
二つ目の例としてユーザー情報の取得を行ってみましょう

今度はフォームに「’ UNION ALL select password as todo, email as id, name as status from user where name like ‘%」
と入力してみましょう。

すると、他のユーザーのメールアドレスが取得できます
また、今度はフォームに「’ UNION ALL select email as id, password as todo, name as status from user where name like ‘%」
と入力してみましょう。
すると今度はハッシュ化されたパスワードを取得することができます
流出したメールアドレスはフィッシング詐欺に悪用される恐れがあります
また、ハッシュ化されたパスワードから元のパスワードを知ることは基本的にはできませんが、
あまり強度が強くないハッシュ化アルゴリズムが使われていた場合には元のパスワードが流出する可能性もあります
SQLインジェクションの対策2
それでは、今回はなぜSQLインジェクションが成功してしまったか考えてみましょう
今回はフォームに入力された文字列に着目してみましょう
通常時は「Python」と入力しました
SQLインジェクション時には「’ UNION ALL select email as id, password as todo, name as status from user where name like ‘%」
と入力しました。両者の違いが何かわかりますか?
正解はSQLの構文が含まれているか否かです
通常時は値しか含まれていませんが、SQLインジェクション時にはUNION ALLやSELECT、FROM、WHEREなどSQLでしようする文法が含まれていることがそもそもの原因であることがわかります
それではUNIONやSELECTの文字を入力不可にすればよいのでしょうか?
しかし、その対策ですと入力不可にすべき候補がたくさんあるのとtodoタスクにWHEREなどの文字を使いたい場合は不便でしょう
もっといいやり方があります
それはプレースホルダーを使用することです
プレースホルダー
プレースホルダーを使用すると、あらかじめSQLの構文解析が済んだ状態になり、そのあとに値を入れる形になります
つまり、UNION ALLやSELECTなどのSQLの構文にしようされていた文字列を入力しても
SQLの文法とみなされず、値とみなされるようになります
試しにやってみましょう。プレースホルダーはクエスチョンマークを使用します

この状態で先ほどと同じように
「’ UNION ALL select password as todo, email as id, name as status from user where name like ‘%」を入力します
すると、今回はメールアドレスは表示されなくなりました
プレースホルダーを使用したことによって「UNION ALL select以下略」の部分が値とみなされたからです
つまり、select * from todos where todo like 「UNION ALL select以下略」というSQLが生成されるように変わりました
これはtodoが「UNION ALL select以下略」と一致するtodoタスクを取得するという意味になります
もちろんそのようなtodoタスクは存在しないので一件も表示されない結果となります
これで無事、SQLインジェクションを防ぐことができました