5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DjangoにおけるSQLインジェクション

Last updated at Posted at 2021-10-02

はじめに

Djangoでの生のSQL文の正確な書き方の記事がほとんどないので、今回の記事の執筆に至りました。
最初にSQLインジェクションの基本事項を抑え後に、具体的にDjangoのSQLインジェクション対策について見ていきましょう。SQLインジェクションの基本事項は、IPA 安全なアプリケーションの作り方- 1.1 SQLインジェクションを多く参考にさせていただきました。

SQLインジェクションとは?

SQL文の組み立て方法に問題がある場合、攻撃によってデータベースの不正利用を行うこと。

SQLインジェクションの種類

インバウンドSQLインジェクション

webアプリケーションからのレスポンスを収集して脆弱性を分析し、その脆弱性をついたSQL文を実行し、データを盗んだり消去したりすること。さらに以下の二つに分けられる。

  • エラーベースSQLインジェクション
    攻撃者がデータベースに対してエラーメッセージを生成させるアクションを実行する。
  • UNIONインジェクション
    複数のSELECTステートメントの結果を一つの結果に結合し、HTTPレスポンスの一部に結果が含まれて返ってくる。

ブラインドSQLインジェクション

攻撃者がデータをwebサーバーに送信し、webサーバーのレスポンスと動作を観察・分析することによって、攻撃者がデータベースの管理情報を盗むことができる。

発生しうる脅威

IPA 安全なアプリケーションの作り方- 1.1 SQLインジェクションで以下のように説明されています。

  • データベースに蓄積された非公開情報の閲覧
  • データベースに蓄積された情報の改ざん、消去
  • 認証回避による不正ログイン
  • ストアドプロシージャ等を利用したOSコマンドの実行

一般的な対策

  1. SQL文の組み立ては全てプレースホルダーで実装する。
    文字列連結に対して機械的な処理でSQL文が組み立てられるので、SQLインジェクションを解消できる。プレースホルダーに実際の値を割り当てることをバインドと呼び、データベースエンジンで割り当てる静的プレースホルダーと、アプリケーション側で割り当てる動的プレースホルダーがある。より安全なのは前者。
  2. SQL文の組み立てを文字列連結によって行う場合は、エスケープ処理等を行いSQL文のリテラルを正しく構成する。
  3. webアプリケーションに渡されるパラメータにSQL文を直接指定しない。

次にDjangoについて具体的に見ていきましょう。

Djangoで既に行われているSQLインジェクションへの対策

ORMによって対策されている。ORMとは、Object-relational mapperの略。これは、オブジェクト志向言語を用いることで、プログラム内のシステム間でデータの非互換なやり取りを行う技法。つまり、オブジェクトを操作することでデータの管理を行うことを可能にした。これを行うことで、クエリのパラメータ化が行われ、それをデータベースドライバーによってエスケープするため、SQLインジェクションを防御できている。

生のSQL文を書く時の注意点

Djangoで既に対策されているとはいえ、複雑なクエリを叩きたい時やデータがモデルに対応していない時など、生のSQL文を書きたい時がある。その時は、次のような二つの記法のうちどちらかを用いる必要がある。

  • executeを用いた際の対処法
context = super().get_context_data(**kwargs)
con = sqlite3.connect("db.sqlite3")
cur = con.cursor()
sql_input = self.request.GET.get("sql", None)
# プレースホルダーを設ける
sql = "SELECT * FROM books_Book WHERE id = %s"
# 値の入れ方に注意
cur.execute(sql % (sql_input))
context["data"] = cur.fetchone()
con.close()
return context
  • Raw()を用いた際の対処法
context = super().get_context_data(**kwargs)
sql_input = self.request.GET.get("sql", None)
# プレースホルダーを設けて値の代入も行う
item = Item.objects.raw("SELECT * FROM items_Item WHERE id=%s", str(sql_input))
context["data"] = item[0].overview
return context

最後に

SQLインジェクションは非常に危険性の高い攻撃ですが、Djangoは基本的にORMによって対策できているようなので、Djangoでコードを書くときはあまり意識する必要はないでしょう。ただ生のSQL文を書く時は、上記の書き方を参考にしていただきたいです。
また初めてのqiitaへの投稿なので、至らない点もあると思いますので、その場合は是非コメントなどをお寄せください。

参考資料

IPA 安全なアプリケーションの作り方- 1.1 SQLインジェクション
油断できない SQL インジェクション。その種類と Web アプリにおける対策
DjangoのSQLインジェクション対策
Djangoの公式のSQL解説

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?