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

Web API連携でCSRF対策がどう実装されてるか調べた / how to implements csrf-detection on Web API

Web API連携でCSRF対策がどう実装されてるか調べた / how to implements csrf-detection on Web API

OAuth, OIDCを対象に、CSRF対策をどうやって実装しているのか興味がありましたので調査しました。CSRFは昔からあるセキュリティリスクで、普段の開発では意識しなくてもフレームワークやライブラリがよしなにやってくれてますが、「どうやって実装してるの?」と疑問に思う人には有益な内容だと思います。

ぜひ読んでみてください。

79fc33e55cacc42beef647541de24d58?s=128

面川泰明

June 23, 2022
Tweet

More Decks by 面川泰明

Other Decks in Programming

Transcript

  1. Web API連携で CSRF対策が どう実装されてるか調べた Web API LT会 - vol.4 #webapilt

    | 2022/6/23 | @omokawa_yasu stateの実装を読んでみよう 1
  2. 自己紹介 2

  3. 3 副業:占い師 おもかわ やすあき

  4. 話すこと 4 1. CSRF攻撃を防ぐ実装 2. Web API連携でCSRF攻撃を防ぐには 3. state 話さないこと

    1. CSRFとは何か 2. OAuthやOIDCなど、Web API連携の仕様 3. nonceなど、state以外の対策方法
  5. CSRF攻撃がどこで起こるか? 5

  6. 6 Auth屋 『OAuth・OIDC への攻撃と対策を整理して理解できる本(リダイレクトへの攻撃編)』 より

  7. 7 攻撃者 被害者 アプリ 認可サービス ① 攻撃者が 認可サービスへ ログイン。 アプリにログインで

    きるURLを取得
  8. 8 攻撃者 被害者 アプリ 認可サービス ② 取得したURLを被害者に送 りつけ、クリックさせる ① 攻撃者が

    認可サービスへ ログイン。 アプリにログインで きるURLを取得
  9. 9 攻撃者 被害者 アプリ 認可サービス ② 取得したURLを被害者に送 りつけ、クリックさせる ① 攻撃者が

    認可サービスへ ログイン。 アプリにログインで きるURLを取得 ③ 被害者は意図せず攻撃者の マイページにログインしてしまう。
  10. 10 攻撃者 被害者 アプリ 認可サービス ② 取得したURLを被害者に送 りつけ、クリックさせる ① 攻撃者が

    認可サービスへ ログイン。 アプリにログインで きるURLを取得 ③ 被害者は意図せず攻撃者の マイページにログインしてしまう。 GAME OVER
  11. 11 CSRF攻撃を防ぐには?

  12. 12 攻撃者 被害者 アプリ 認可サービス ② 取得したURLを被害者に送 りつけ、クリックさせる ① 攻撃者が

    認可サービスへ ログイン。 アプリにログインで きるURLを取得 送りつけたURLが 攻撃者のセッションで 発行されたことを 検知できればよい
  13. 13 stateによる対策

  14. 14 Auth屋 『OAuth・OIDC への攻撃と対策を整理して理解できる本(リダイレクトへの攻撃編)』 より

  15. 15 実装を追っていく

  16. 16 ① stateの生成

  17. https://github.com/omniauth/omniauth_openid_connect/blob/master/lib/omniauth/strategies/openid_con nect.rb#L241-L250 17 def new_state state = if options.state.respond_to?(:call) if

    options.state.arity == 1 options.state.call(env) else options.state.call end end session['omniauth.state'] = state || SecureRandom.hex(16) end
  18. https://github.com/omniauth/omniauth_openid_connect/blob/master/lib/omniauth/strategies/openid_con nect.rb#L241-L250 18 def new_state state = if options.state.respond_to?(:call) if

    options.state.arity == 1 options.state.call(env) else options.state.call end end session['omniauth.state'] = state || SecureRandom.hex(16) end stateを生成して セッションに格納
  19. 認可画面が表示されたときには既に、stateがセッションに格納されてる状態 19

  20. 20 セッションの実装

  21. https://github.com/rack/rack-session/blob/main/lib/rack/session/abstract/id.rb#L306-L312 21 def prepare_session(req) session_was = req.get_header RACK_SESSION session =

    session_class.new(self, req) req.set_header RACK_SESSION, session req.set_header RACK_SESSION_OPTIONS, @default_options.dup session.merge! session_was if session_was end ※ Sinatraだとこれを使ってた
  22. https://github.com/rack/rack-session/blob/main/lib/rack/session/abstract/id.rb#L306-L312 22 def prepare_session(req) session_was = req.get_header RACK_SESSION session =

    session_class.new(self, req) req.set_header RACK_SESSION, session req.set_header RACK_SESSION_OPTIONS, @default_options.dup session.merge! session_was if session_was end アプリケーションの環境情報に、セッ ション管理オブジェクトを 設定する
  23. ここまでの流れ 23

  24. 24 セッション情報を設定したあとで、 stateを生成してセッションに格納 ※ 詳しい仕組みを知りたい人は (Rack Middlewareで検索)

  25. 25 ② sessionとstateの対 応を確認 ① stateの生成

  26. https://github.com/omniauth/omniauth_openid_connect/blob/master/lib/omniauth/strategies/openid_con nect.rb#L107-L136 26 def callback_phase error = params['error_reason'] || params['error']

    error_description = params['error_description'] || params['error_reason'] invalid_state = params['state'].to_s.empty? || params['state'] != stored_state raise CallbackError, error: params['error'], reason: error_description, uri: params['error_uri'] if error raise CallbackError, error: :csrf_detected, reason: "Invalid 'state' parameter" if invalid_state ..(省略).. end
  27. https://github.com/omniauth/omniauth_openid_connect/blob/master/lib/omniauth/strategies/openid_con nect.rb#L107-L136 27 def callback_phase error = params['error_reason'] || params['error']

    error_description = params['error_description'] || params['error_reason'] invalid_state = params['state'].to_s.empty? || params['state'] != stored_state raise CallbackError, error: params['error'], reason: error_description, uri: params['error_uri'] if error raise CallbackError, error: :csrf_detected, reason: "Invalid 'state' parameter" if invalid_state ..(省略).. end クエリパラメータのstateと セッションに保存した stateが 不一致ならエラー
  28. 28 実際の動きを確認

  29. 29 攻撃者 被害者 アプリ 認可サービス ② 取得したURLを被害者に送 りつけ、クリックさせる ① 攻撃者が

    認可サービスへ ログイン。 アプリにログインで きるURLを取得 送りつけたURLにstateが付与され ている場合 例) localhost:3000/auth/freee/callback?cod e=2604159252ff006330b8cb632817e537ebcd 567b408611d08bfab87c65b5e1c8&state=33d 2ab070a9ee75bc4fb3169f9bb0c32
  30. 30 被害者がURLを別ブラウザで開くと ...

  31. 31 CSRF攻撃だと 検知され、ログインエラーとなる

  32. https://github.com/omniauth/omniauth_openid_connect/blob/master/lib/omniauth/strategies/openid_con nect.rb#L107-L136 32 def callback_phase error = params['error_reason'] || params['error']

    error_description = params['error_description'] || params['error_reason'] invalid_state = params['state'].to_s.empty? || params['state'] != stored_state raise CallbackError, error: params['error'], reason: error_description, uri: params['error_uri'] if error raise CallbackError, error: :csrf_detected, reason: "Invalid 'state' parameter" if invalid_state ..(省略).. end 被害者のセッションには stateが存在 しないため、不一致と判定される ここで例外が実行される
  33. 33 まとめ

  34. 34 攻撃者 被害者 アプリ 認可サービス ② 取得したURLを被害者に送 りつけ、クリックさせる ① 攻撃者が

    認可サービスへ ログイン。 アプリにログインで きるURLを取得 送りつけたURLが 攻撃者のセッションで 発行されたことを 検知できればよい
  35. 35 攻撃者 被害者 アプリ 認可サービス ② 取得したURLを被害者に送 りつけ、クリックさせる ① 攻撃者が

    認可サービスへ ログイン。 アプリにログインで きるURLを取得 送りつけたURLが 攻撃者のセッションで 発行されたことを 検知できればよい stateで セッションを 識別すると CSRF対策が 実現できる
  36. 36 参考資料 - CSRFの説明 - Auth屋 『OAuth・OIDC への攻撃と対策を整理して理解できる本(リダイレクトへの攻撃編)』 - CSRF対策の実装

    - https://github.com/omniauth/omniauth_openid_connect - Rackの概念 - https://qiita.com/nishio-dens/items/e293f15856d849d3862b - Rackの動き - https://github.com/rack/rack - https://github.com/rack/rack-session - そもそもセッションとは何か? - https://speakerdeck.com/sylph01/build-and-learn-rails-authentication?slide=30 - 動作確認に使ったWebサービス - https://www.ninja-sign.com わかりやすくておススメです
  37. ご清聴ありがとうございました! 37