Upgrade to Pro — share decks privately, control downloads, hide ads and more …

現場で使える Django のセキュリティ対策 / Django security meas...

現場で使える Django のセキュリティ対策 / Django security measures for business (DjangoCon JP 2019)

○ 発表タイトル

現場で使える Django のセキュリティ対策

○ 発表内容

待望のバージョン2系の LTS がリリースされ、ますます現場で Django が盛り上がることが予想されますが、導入検討の際にネックになるものの一つに「セキュリティ」への懸念があります。

Django にはデフォルトでセキュリティ対策も含まれていますが、そのあたりの理解が中途半端だと、リリースした Webアプリが攻撃者の格好の餌食になってしまいかねません。そうならないためにも、Django のセキュリティ事情については開発前から(もちろん開発中でも)きちんと把握しておきたいところです。

そこでまず、Django におけるセキュリティ対策の基礎について概観します。そして CSRF などの重要な(そして重要な割にあまりよく理解されていない)ものについては、「CSRF(しーさーふ)って何?」というレベルの人にも分かるように基本から解説します。

akiyoko

May 18, 2019
Tweet

More Decks by akiyoko

Other Decks in Programming

Transcript

  1. ⽬次 1. Webアプリケーションにセキュリティ対策は必要? 2. セキュリティ対策の基準 3. 代表的な脆弱性と Django での対策 4.

    まとめ スライドに掲載した情報やコードを利⽤することで発⽣したトラブルや損失、 損害に対しては⼀切責任を負いません。 2
  2. ⾃⼰紹介 • 名 前 : 横瀬 明仁(akiyoko) • 所 属

    : 株式会社 ワングリット • Twitter : @aki_yok • ブログ : akiyoko blog(http://akiyoko.hatenablog.jp/) • Django歴 : 6年くらい • セキュリティ資格 : IPA「情報セキュリティマネジメント試験」 • 同⼈誌 : 『現場で使える Django の教科書(基礎編)』 『現場で使える Django の教科書(実践編)』 『現場で使える Django REST Framework の教科書』 3
  3. 「安全なウェブサイトの作り⽅」要約 (3/4) 脆弱性の根本対策 • 「脆弱性」とは、第三者に悪⽤されてしまう「安全上の⽋陥」 • 予測不可能な落とし⽳ではなく、よくあるものについては⼀般常識に • SQLインジェクション対策、XSS対策、CSRF対策、etc ⼆次対策(保険的対策)

    • 根本解決ではないが、攻撃されにくくする、被害を最⼩化するなどの⽬的で • 予算や運⽤要件などに応じて、安全性を強化するための積極施策も • パスワード漏洩対策、監視⽤ログ出⼒、etc 12 今⽇話すのは 主にコレ
  4. 「安全なウェブサイトの作り⽅」要約 (4/4) 13 カテゴリ 脆弱性・攻撃の例 概要 Ⅰ. パスワード系 (1) パスワード不正取得

    (2) パスワード漏洩 悪意ある攻撃からパスワードを保護。 万が⼀流出した場合の対策も必要 Ⅱ. 認証・認可系 (3) セッション管理の不備 (4) アクセス制御の不備 セッションIDを不正取得されないように 対策。アクセス制御で秘密情報を保護 Ⅲ. インジェク ション攻撃系 (5) SQLインジェクション (6) HTTPヘッダ・インジェクション (7) メールヘッダ・インジェクション OSコマンド・インジェクション 意図しないスクリプトが実⾏されないよ うに制御。本来実⾏するスクリプト⽂の 区切り⽂字や制御⽂字が悪⽤される Ⅳ. クロスサイト (罠サイト)系 (8) XSS (9) CSRF (10) クリックジャッキング 罠サイトからの攻撃を考慮 Ⅴ. その他 ディレクトリ・トラバーサル バッファオーバーフロー バッファオーバーフローは主に C や C++ が対象 ͜Ε͚ͩ͸ઈରରࡦ͠ͳ͖Όଛ֐ഛঈϞϯͷܹϠό੬ऑੑ 
  5. Ⅰ- (1) パスワード不正取得 【Django での対策】 • startprojectコマンドで⾃動作成される設定ファイル(settings.py)の 「AUTH_PASSWORD_VALIDATORS」にセットされたバリデーションが、 パスワード設定時に実⾏される(Django 1.9

    以降) • createsuperuserコマンドや UserCreationForm、SetPasswordForm で django.contrib.auth.password_validation.validate_password() を介して実⾏される • アカウントロックはデフォルトで⽤意されていないため、⾃前で導⼊する • django-axes を利⽤すれば簡単に導⼊可能 https://django-axes.readthedocs.io/ 20 ཁ ௥ Ճ ର Ԡ
  6. Ⅰ- (1) パスワード不正取得 【該当コード】 AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',

    }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] <設定ディレクトリ>/settings.py 21 デフォルトでは ①「username」「first_name」「last_name」 「email」と類似していないか ② 8⽂字以上であること ③ よくあるパスワードのリストと⼀致していないか ④ 数値のみで構成されていないか を検証してくれる ① ② ③ ④
  7. Ⅰ- (2) パスワード漏洩 【Django での対策】 • ⽣パスワードとソルトと連結したものを規定回数(Django 2.2 では 15,000

    回)ストレッチングしたハッシュ値が保存される • django.contrib.auth.base_user.AbstractBaseUser#set_password() を 使うと、内部で django.contrib.auth.hashers.make_password() が呼ばれる pass12345 pbkdf2_sha256$150000$VU5rxZy7LYHK$ zWzEkGbxey/1A5nAW7/6yn9wxfCyv+DeGmOOoN+wq6s= <アルゴリズム>$<ストレッチ回数>$<ソルト>$<ハッシュ値> 【⽣パスワード】 【変換されたハッシュ値】 26
  8. Ⅰ- (2) パスワード漏洩 【該当コード】 def make_password(password, salt=None, hasher='default'): """ Turn

    a plain-text password into a hash for database storage Same as encode() but generate a new random salt. If password is None then return a concatenation of UNUSABLE_PASSWORD_PREFIX and a random string, which disallows logins. Additional random string reduces chances of gaining access to staff or superuser accounts. See ticket #20079 for more info. """ if password is None: return UNUSABLE_PASSWORD_PREFIX + ¥ get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH) hasher = get_hasher(hasher) salt = salt or hasher.salt() return hasher.encode(password, salt) django.contrib.auth.hashers.make_password ハッシャーは設定ファイルの「PASSWORD_HASHERS」 にセットされたもの(Django 2.2 のデフォルトは django.contrib.auth.hashers.PBKDF2PasswordHasher) が使われる 27
  9. Ⅱ- (3) セッション管理の不備 【どんな脆弱性?】 • ログイン⽅式に Cookie 認証(セッション認証)を利⽤していて且つ次のよ うな場合は、盗⽤・なりすまし(セッション・ハイジャック)の危険がある •

    推測可能なセッションID • URL 埋め込みのセッションID • ログイン前後で同じセッションIDを利⽤(セッションID 固定化攻撃) • HTTPS/HTTP 間で同じセッションID を共有 31
  10. Ⅱ- (3) セッション管理の不備 【⼀般的な対策】 • セッションID を推測可能なものにしない • セッションID を

    URL に埋め込まない • ログイン成功時に新たなセッションを開始する • HTTPS/HTTP で別のセッションID を使う(Cookie に Secure属性を付与) 32
  11. Ⅱ- (3) セッション管理の不備 【Django での対策】 • Django のセッション管理は Cookie ベース。URL

    埋め込みはしていない • セッションID は推測困難なランダム⽂字列として発⾏される • ログインのビューに django.contrib.auth.views.LoginView を使った場合、 ログイン成功時に古いセッションを破棄してからセッション再開してくれる • HTTPS 環境では「SESSION_COOKIE_SECURE」 (デフォルトは False) を True に設定して、Cookie に Secure 属性を付与する(ただし、本番環境 を常時SSL にすればほぼ問題なし) 33 ཁ ௥ Ճ ର Ԡ
  12. Ⅱ- (4) アクセス制御の不備 【Django での対策】 • ログイン後は、django.contrib.auth.middleware.AuthenticationMiddleware が全てのリクエストの前処理で、セッションID とログインユーザーを紐付け てくれる(ログインユーザー権限の付与)

    • django.contrib.auth.mixins.LoginRequiredMixin を利⽤して、ログイン後のユーザーの みがアクセスできる権限をビュー単位に付与できる • パーミッション(Permission)とグループ(Group)を使ってモデルベース の権限を付与できる認可機能もデフォルトで提供されている 37 見たことあるはず ཁ ௥ Ճ ର Ԡ ཁ ௥ Ճ ର Ԡ
  13. Ⅲ. インジェクション攻撃系の脆弱性 とその対策 40 カテゴリ 脆弱性・攻撃の例 Django での対策 Ⅲ. インジェクション

    攻撃系 (5) SQLインジェクション ? (6) HTTPヘッダ・インジェクション ? (7) メールヘッダ・インジェクション ? OSコマンド・インジェクション ✕ 該当箇所が無いか探す
  14. Ⅲ- (5) SQLインジェクション 【Django での対策】 • Django のモデルやクエリセットAPI を利⽤してクエリを発⾏することで、 Django

    ORM が内部でセキュアな SQL を組み⽴ててくれる https://docs.djangoproject.com/ja/2.2/topics/security/#sql-injection- protection • 最後の⼿段としてモデルマネージャの raw() や django.db.connection.cursor オブジェクトの execute() を使えば ⽣の SQL を実⾏することもできるが、 書き⽅によっては SQLインジェクションのリスクがあるので注意 https://docs.djangoproject.com/ja/2.2/topics/db/sql/#passing- parameters-into-raw 44 ཁ ஫ ҙ
  15. Ⅲ- (6) HTTPヘッダ・インジェクション 【どんな脆弱性?】 • HTTPヘッダにおいて改⾏や空⾏に意味があることが悪⽤される • 外部からのパラメータを元に HTTPレスポンスヘッダを⽣成するプログラム では、ヘッダが任意に改変される危険性がある

    • レスポンスを分割して任意のレスポンスボディをリバースプロキシ等に キャッシュさせる攻撃は「キャッシュ汚染」と呼ばれる 罠サイト Location: example.com Location: evil.xyz 47 HTTPリクエスト HTTPレスポンス https://evil.xyz/
  16. Ⅲ- (6) HTTPヘッダ・インジェクション 【Django での対策】 • Django が適切にレスポンスヘッダを組み⽴てるので、ユーザーのリクエス トを元にしたレスポンスヘッダは追加されない。 django.http.response.HttpResponse

    を使う場合、ヘッダに改⾏が含められ ていると例外発⽣ • 「Host」ヘッダに攻撃⽤の⽂字列が注⼊されるのを防ぐために、 「ALLOWED_HOSTS」に定義したホワイトリストでホスト名を検証 https://docs.djangoproject.com/ja/2.2/topics/security/#host-header- validation • 本番⽤の設定ファイルの「ALLOWED_HOSTS」は適切に設定(” * " にしない!) 49 ཁ ஫ ҙ
  17. Ⅲ- (6) HTTPヘッダ・インジェクション 【該当コード】 def _convert_to_charset(self, value, charset, mime_encode=False): ...

    if ((isinstance(value, bytes) and (b‘¥n’ in value or b‘¥r’ in value)) or isinstance(value, str) and (‘¥n’ in value or ‘¥r’ in value)): raise BadHeaderError(“Header values can‘t contain newlines (got %r)” % value) django.http.response.HttpResponseBase#_convert_to_charset >>> from django.http.response import HttpResponse >>> res = HttpResponse() >>> res['Location¥nLocation: evil.net'] = 'example.com’ django.http.response.BadHeaderError: Header values can't contain newlines (got 'Location¥nLocation: evil.net') 実⾏結果 50
  18. Ⅲ- (7) メールヘッダ・インジェクション 【どんな脆弱性?】 • 送信メールのヘッダを改変され、迷惑メール送信などに悪⽤される 【⼀般的な対策】 • ⾃前でメールヘッダを出⼒せず、ヘッダ出⼒⽤のAPIを使う 【Django

    での対策】 • django.core.mail.send_mail() や django.core.mail.message.EmailMultiAlternativesなどのメール送信⽤APIを 利⽤することで、意図しないヘッダの改変は防御可能 53
  19. Ⅳ- (8) XSS 【どんな脆弱性?】 • XSS(クロスサイト・スクリプティング)とは、画⾯への出⼒処理にスクリ プトを埋め込める脆弱性がある場合、利⽤者のブラウザ上で不正なスクリプ トが実⾏される危険性がある • Cookie

    に保存されたセッションIDを抜き取られる可能性もあり、⼤変危険 スクリプト 実⾏! 罠サイト 57 HTTPリクエスト https://evil.xyz/ HTTPリクエスト <script>func()</script>
  20. Ⅳ- (8) XSS 【⼀般的な対策】 • 意図しない HTML や JavaScript が注⼊・実⾏されないように、画⾯に出⼒

    する項⽬は特定⽂字のエスケープ(無害化)処理を施す • 万が⼀ XSS の被害にあっても、Cookie に保存されている重要情報が JavaScript によって抜き出されないように、レスポンスの Set-Cookie ヘッ ダに HttpOnly 属性を付与する 58
  21. Ⅳ- (8) XSS 【Django での対策】 • Django Template Language(Django 標準のテンプレート構⽂)を利⽤して

    いる場合は、{{ 〜 }} を使って変数を表⽰する際に危険とみなされる特定⽂ 字を⾃動的にエスケープしてくれる • ただし、万能ではない https://docs.djangoproject.com/ja/2.2/topics/security/#cross-site- scripting-xss-protection 59 <style class={{ var }}>...</style> class1 onmouseover=javascript:func() ཁ ஫ ҙ
  22. Ⅳ- (8) XSS 【Django での対策(続き)】 • ⾃動エスケープを無効にするための「safeフィルタ」や「autoescapeタグ」 は、第三者が登録・更新する可能性がある項⽬では利⽤しないように注意 https://docs.djangoproject.com/ja/2.2/topics/security/#cross-site- scripting-xss-protection

    • XSS の被害にあった場合に Cookie に保存されている重要情報が JavaScript によって抜き出されないように、「CSRF_COOKIE_HTTPONLY」(デフォ ルトは False)および「SESSION_COOKIE_HTTPONLY」 (デフォルトは True)を True に設定して、HttpOnly 属性を付与 60 ཁ ஫ ҙ ཁ ௥ Ճ ର Ԡ
  23. Ⅳ- (9) CSRF 【どんな脆弱性?】 • CSRF(クロスサイト・リクエスト・フォージェリ)とは、「(Webブラウ ザに保存されている Cookie などの値が⾃動的にリクエストヘッダにセット されて送信されるという性質を攻撃者が利⽤して)副作⽤を伴う重要なリク

    エストを利⽤者の意図に反して強要する」攻撃を指す • Cookie 認証を利⽤している場合、Cookie に保存されているセッションID が Webブラウザによって勝⼿に送信されてしまうことが悪⽤される • 読み⽅は「しーさーふ」とも 64 CSRF 攻撃の仕組みを図解
  24. Ⅳ- (9) CSRF 正規のサイト https://example.com/ セッション ② ログアウトするまで (または⼀定時間が経過す るまで)

    セッションデータ がサーバ側で保存される ① 正規のサイトにログイン ③ Webブラウザの Cookie に セッションIDが保存される https://example.com/ ④ 正規のリクエスト リクエスト⾏ メッセージヘッダ POST /shop/buy/ Host: example.com Cookie: sessionid=xxxxxxx メッセージボディ item_no=123 購 ⼊ Cookie sessionid=xxxxxx (Domain=example.com) 65 図解:CSRF とは
  25. Ⅳ- (9) CSRF ⑤ ログインセッションが 残った状態で、 悪意のある罠サイトに アクセスしてしまい・・ https://evil.xyz/ とにかく

    クリックしてね ⑥ 罠によって、正規のサイトに 不正なリクエストを送信して しまう https://example.com/ 購 ⼊ Cookie sessionid=xxxxxx (Domain=example.com) 正規のサイト https://example.com/ セッション 不正なリクエスト リクエスト⾏ メッセージヘッダ POST /shop/buy/ Host: example.com Cookie: sessionid=xxxxxxx メッセージボディ tem_no=123 ⑦ ログインユーザーの 正規のリクエストと区別 できないので、そのまま 処理してしまうことも クリック 罠サイト 66 図解:CSRF とは
  26. Ⅳ- (9) CSRF 【⼀般的な対策】 • POST などの副作⽤のあるリクエストをおこなうフォームが正規のサイトに よって⽣成されたものかどうかを検証する • たとえば、乱数値を

    Cookie とリクエストパラメータの両⽅に持たせ、両者 が⼀致していれば正規のリクエストとみなす(「トークン埋め込み」⽅式) • Ajax でも CSRF 対策は必要 67
  27. Ⅳ- (9) CSRF 【Django での対策】 • CsrfViewMiddleware を有効にして(デフォルトで有効)、テンプレートの POST⽤の form

    要素に {% csrf_token %} を含めておくだけで、あとは⾃動 で CSRF検証をおこなってくれる • CsrfViewMiddleware によって「CSRFトークン」(Cookie に保存される「csrftoken」 と hidden項⽬として利⽤される「csrfmiddlewaretoken」の2種類の乱数値)の⽣成お よび検証がおこなわれる • テンプレートレンダリング時に、テンプレートに {% csrf_token %} が含まれていれば CSRFトークンを⽣成する • CsrfViewMiddleware の前処理時に、POST リクエストの場合のみ CSRF検証をおこな い、検証NG の場合は 403エラーを返す • csrf_exemptデコレータで CSRF検証を回避できるが、理由なく使⽤しない 68 ཁ ஫ ҙ
  28. Ⅳ- (9) CSRF Django サイト CsrfViewMiddleware リクエスト⾏ メッセージヘッダ メッセージボディ GET

    /accounts/login/ ステータス⾏ メッセージヘッダ メッセージボディ 200 OK Set-Cookie: csrftoken=111111 <body> ... <input type="hidden" name="csrfmiddlewaretoken" value="xxxxxx"> </body> ① GET リクエスト ユーザー名 パスワード ログイン ビュー ログイン画⾯ レスポンス ② POST リクエスト(次ページへ) ( csrftoken = 111111) ①ʼʼ csrfmiddlewaretoken ⽣成 ①ʼ csrftoken ⽣成 ( csrfmiddlewaretoken = xxxxxx) Cookie csrftoken=111111 (Domain=example.com) 69 Djangoでの CSRF 対策
  29. Django サイト CsrfViewMiddleware ビュー Ⅳ- (9) CSRF リクエスト⾏ メッセージヘッダ メッセージボディ

    POST /accounts/login/ Cookie: csrftoken=111111 username=admin&password=pass &csrfmiddlewaretoken=xxxxxx ステータス⾏ メッセージヘッダ メッセージボディ 302 OK Set-Cookie: csrftoken=222222 <body>...</body> リクエスト⾏ メッセージヘッダ メッセージボディ GET /dashboard/ Cookie: csrftoken=222222 ・・・ ログイン後の画⾯ ②ʼ CSRF 検証 ( csrftoken = 222222) ②ʼʼ csrftoken 更新 ログイン画⾯ ユーザー名 パスワード ログイン Cookie csrftoken=111111 (Domain=example.com) Cookie csrftoken=222222 (Domain=example.com) ② POST リクエスト ③ GET リクエスト 70 Djangoでの CSRF 対策 レスポンス
  30. Ⅳ- (10) クリックジャッキング 【⼀般的な対策】 • frame および iframe での参照を制限する「X-Frame-Options」ヘッダをレ スポンスに付与

    【Django での対策】 • 「XFrameOptionsMiddleware」を有効にして(デフォルトで有効)、 「X-Frame-Options」ヘッダを全てのレスポンスに設定することで iframe 内のページの埋め込みを制御 • デフォルト値は「SAMEORIGIN」(同⼀オリジンのみ許可) • ちなみに、Django 3.0 以降はデフォルト値が「DENY」(すべて拒否)に変更 74
  31. 代表的な脆弱性とその対策まとめ カテゴリ 脆弱性・攻撃の例 Django での対策 Ⅰ. パスワード系 (1) パスワード不正取得 ◦

    (2) パスワード漏洩 ◦ Ⅱ. 認証・認可系 (3) セッション管理の不備 ◦ (4) アクセス制御の不備 ◦ Ⅲ. インジェクション 攻撃系 (4) SQLインジェクション ◦ (5) HTTPヘッダ・インジェクション ◦ (6) メールヘッダ・インジェクション ◦ OSコマンド・インジェクション ✕ 該当箇所が無いか探す Ⅳ. クロスサイト (罠サイト)系 (8) XSS ◦ (9) CSRF ◦ (10) クリックジャッキング ◦ Ⅴ. その他 ディレクトリ・トラバーサル ✕ 該当箇所が無いか探す バッファオーバーフロー ✕ 該当箇所が無いか探す ཁ ௥ Ճ ର Ԡ ཁ ஫ ҙ ཁ ஫ ҙ ཁ ஫ ҙ ཁ ௥ Ճ ର Ԡ 76 ཁ ௥ Ճ ର Ԡ ཁ ௥ Ճ ର Ԡ ཁ ஫ ҙ
  32. まとめ • 経産省・IPA が注意喚起した Webアプリケーションの脆弱性対策は必須 • IPA の「安全なウェブサイトの作り⽅」は最低限必ず網羅 • 補助教材として「徳丸本」を活⽤

    • Django 公式ドキュメント「Django におけるセキュリティ」も必読 https://docs.djangoproject.com/ja/2.2/topics/security/ • 脆弱性診断や WAF、各種ツールの利⽤も検討 • 正しく Django を使えば、主な脆弱性は⾃動的に対策してくれるので便利 • 安⼼して現場で Django を使っていきましょう✌ 79
  33. 今回 話したこと ① Webアプリケーションのセキュリティ対策って そもそも必要なの? ⇒絶対必要(判例もある) ② 「これだけは絶対やっとけ」的な基準はある? ⇒IPA「安全なウェブサイトの作り⽅」 ③

    フルスタック(全部⼊り)の Django を使えば セキュリティ対策もまるっと万事解決? ⇒ほぼ解決。ただし、例外や追加対応が必要なものも 80
  34. 宣伝 (電⼦版・紙の本) 『現場で使えるDjango の教科書 《基礎編》』 『現場で使えるDjango の教科書 《実践編》』 (電⼦版) (紙の本)

    『現場で使える Django REST Framework の教科書』 (紙の本) (紙の本) ★ セキュリティの章あり ★ セキュリティの章あり 81 【 Amazon 】 【 BOOTH 】 (電⼦版)
  35. 参考資料 (1) • ECサイト不正アクセス損害賠償事件訴訟(平23(ワ) 32060号) • SQLインジェクション対策もれの責任を開発会社に問う判決 | 徳丸浩の⽇記 https://blog.tokumaru.org/2015/01/sql.html

    • システムベンダのセキュリティ対策義務 | 弁護⼠ 武⽥勝弘 https://www.softic.or.jp/semi/2014/5_141113/rep2.pdf • Django 公式ドキュメント • Django におけるセキュリティ https://docs.djangoproject.com/ja/2.2/topics/security/ • django-announce メーリングリスト 新バージョンのリリース情報やセキュリティアップデート速報など https://docs.djangoproject.com/en/2.2/internals/mailing-lists/#django- announce 82
  36. 参考資料 (2) • IPA 情報セキュリティ • 「安全なウェブサイトの作り⽅」「セキュリティ実装 チェックリスト」 https://www.ipa.go.jp/security/vuln/websecurity.html •

    各ツールの紹介 https://www.ipa.go.jp/security/tools/index.html • その他 • Djangoのセキュリティとその実装 https://www.slideshare.net/aki33524/django-58472909 • Djangoはパスワードをどうやって保存しているのか | CreditEngine Tech https://medium.com/creditengine-tech/64a4b38904ff • アカウントロック機能を追加するライブラリdjango-axesの紹介 | CreditEngine Tech https://medium.com/creditengine-tech/e5414cc674e0 83