Web制作、Web開発の歩き方

初心者のためのDjango入門

■第10話:SQLインジェクション対策をして素のSQLを実行しよう

(最終更新日:2023.07.29)

Djangoフレームワークのイメージ
この記事は5分で読めます!
(絵が小さい場合はスマホを横に)

「検索速度を上げるために素のSQLを実行したい!」

DjangoのORMは様々な書き方に対応できるため、基本的なデータベース操作はORMで充分である。 しかしながら、検索速度を上げたいときなど、どうしても素のSQLを使わざる得ないこともある。 そんなときに重要なのが、SQLインジェクション対策だ。 今回は素のSQLを使う上で、セキュリティ的に重要なSQLインジェクション対策の手法を紹介する。


1.SQLインジェクションとは

SQLインジェクションとは、ユーザーが入力した情報に基づきSQLを実行する際に、 その情報がSQLの命令として認識され、悪意のあるSQL文を実行させる方法である。 下記はその例で、黄色のマーカー部分が悪意のあるユーザーがパラメータとして入力した例である。 本来ある検索の命令(SELECT * FROM 商品データ WHERE id = 1;)を;を入れることで一旦終われせ、 その後のSQL文(SELECT * FROM 顧客データ;)で本来公開してはいけない顧客データを表示させている。

SELECT * FROM 商品データ WHERE id = 1;SELECT * FROM 顧客データ;

実際、SQLインジェクションにより、数千万円の損害賠償が発生したという事例はある。このような悲劇を招かないためにも、 ユーザーが入力した情報(黄色の部分)はSQL文を実行せず、ただの値としてのみ入力させる必要がある。これがSQLインジェクションの根本的な対策になる。

2.PythonのSQLインジェクション対策

PythonのSQLインジェクション対策と書いたが、PHPでもPerlでも若干書き方が変わるだけでやることは変わらない。 SQL部分とパラメータ部分(ユーザー入力部分)に分けて、パラメータ部分を値(SQLとして読ませないように)して、悪意のあるコードを実行させないということである。 まずは、SQL対策をしていない記述を見てみよう。下記はパラメータとしてユーザーid番号を入力して、その個人データを取得している。 この場合、本人のidが1番で本人のデータだけが取得できる。

UniqueConstraintの使用例

SQLインジェクション対策をしていないコード

次に、このparameter部分に悪意のあるコード(1 OR 1 = 1)を入力する。すると、本人以外の全ての人のデータが表示される。 もちろん、今回のようにidを直接指定してデータを取得するということは無いのだが、 1=1というような、いつでも成立する検索条件を書かれるとデータを盗られてしまう。

UniqueConstraintの使用例

対策していないコードに対して、SQLインジェクションが成功した例

ここで、上記のような操作をさせないためにも、SQLインジェクション対策を行う。 同じ入力値に対して、SQLインジェクション対策したコードに変える。 するとエラーを吐き出し、SQLは実行されない。書き方自体は難しくないので、素のSQLを扱う際は覚えておこう。 下記の書き方はPythonで対策を行った一例だが、他にもDjangoには、execute関数を用いた対処法、raw関数を用いた対処法がある。 %sは連続した文字列を意味する。 つまり、入力されたデータ(1 OR 1 = 1)が一つの文字列(タプルや配列の先頭の文字列)として認識され、プレースホルダとして機能している。

UniqueConstraintの使用例

SQLインジェクション対策した例

SQLインジェクション対策に関しての 詳しい情報は、安全SQLの呼び出し方を見てほしい。 各言語(Java, PHP, Perl)の書き方、具体的な保護のメカニズムを説明している。 アプリケーション側の言語問わず、最も安全な方法は、prepared文で事前にSQLの骨組み部分を記述し、 SQLを実行する直前にbindするパラメータを値にして、SQLを実行する静的プレースホルダが良いとされている。 SQLの実行効率もこの方法が良い。 また、今回用いたMySQLdbではなく、mysql-connector-pythonを用いる場合は、prepared=Trueにして、その機能を有効にできる。 調べて試してみよう。

3.Djangoにおける生SQLの実行

最後にDjangoにおける生SQLの実行とSQLインジェクション対策について説明する。 SQLインジェクション対策については、フレームワークを用いないPythonで行うときと殆ど変わらない。 下記はDjangoで生SQLを実行する際に、SQLインジェクション対策を行った例である。 Django公式ドキュメントに書かれたコードそのままであるが、ここでやるべきは、 プレースホルダを用いて、実際、悪意のあるコードが弾かれることを確認するということだ。 そこだけ頭の片隅に覚えておこう。

ExclusionConstraintの使用例


4.まとめ

今回、主にPythonのSQLインジェクション対策について説明した。 DjangoのORM機能は充実しており、SQLを書かずともデータベース操作することができる。 生SQLを書くことはほとんどないだろう。 しかしながら「検索スピードを上げたい」「複雑な命令が必要」という場合は使う必要が出てくる。 そんなとき、頭の片隅に今回の対策を思い出して頂ければ幸いである。


▼参考図書、サイト

安全なウェブサイトの作り方 - 1.1 SQLインジェクション  IPA 情報処理推進機構
Python MySQL Connector エスケープについて  fukuの犬小屋
DjangoにおけるSQLインジェクション  Qiita
Performing raw SQL queries  Django公式ドキュメント