Slide 1

Slide 1 text

Web API連携で CSRF対策が どう実装されてるか調べた Web API LT会 - vol.4 #webapilt | 2022/6/23 | @omokawa_yasu stateの実装を読んでみよう 1

Slide 2

Slide 2 text

自己紹介 2

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

話すこと 4 1. CSRF攻撃を防ぐ実装 2. Web API連携でCSRF攻撃を防ぐには 3. state 話さないこと 1. CSRFとは何か 2. OAuthやOIDCなど、Web API連携の仕様 3. nonceなど、state以外の対策方法

Slide 5

Slide 5 text

CSRF攻撃がどこで起こるか? 5

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

7 攻撃者 被害者 アプリ 認可サービス ① 攻撃者が 認可サービスへ ログイン。 アプリにログインで きるURLを取得

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

11 CSRF攻撃を防ぐには?

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

13 stateによる対策

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

15 実装を追っていく

Slide 16

Slide 16 text

16 ① stateの生成

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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を生成して セッションに格納

Slide 19

Slide 19 text

認可画面が表示されたときには既に、stateがセッションに格納されてる状態 19

Slide 20

Slide 20 text

20 セッションの実装

Slide 21

Slide 21 text

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だとこれを使ってた

Slide 22

Slide 22 text

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 アプリケーションの環境情報に、セッ ション管理オブジェクトを 設定する

Slide 23

Slide 23 text

ここまでの流れ 23

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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が 不一致ならエラー

Slide 28

Slide 28 text

28 実際の動きを確認

Slide 29

Slide 29 text

29 攻撃者 被害者 アプリ 認可サービス ② 取得したURLを被害者に送 りつけ、クリックさせる ① 攻撃者が 認可サービスへ ログイン。 アプリにログインで きるURLを取得 送りつけたURLにstateが付与され ている場合 例) localhost:3000/auth/freee/callback?cod e=2604159252ff006330b8cb632817e537ebcd 567b408611d08bfab87c65b5e1c8&state=33d 2ab070a9ee75bc4fb3169f9bb0c32

Slide 30

Slide 30 text

30 被害者がURLを別ブラウザで開くと ...

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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が存在 しないため、不一致と判定される ここで例外が実行される

Slide 33

Slide 33 text

33 まとめ

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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 わかりやすくておススメです

Slide 37

Slide 37 text

ご清聴ありがとうございました! 37