Save 37% off PRO during our Black Friday Sale! »

Build and Learn Rails Authentication

404139d782ec666acea93dffc86e089f?s=47 sylph01
October 22, 2021

Build and Learn Rails Authentication

@ Kaigi on Rails 2021, 2021/10/22

404139d782ec666acea93dffc86e089f?s=128

sylph01

October 22, 2021
Tweet

Transcript

  1. Build and Learn Rails Authentication Ryo Kajiwara (sylph01) 2021/10/21 @

    Kaigi on Rails 2021
  2. 本発表のレポジトリは以下です スライドはslides フォルダ以下にあります

  3. 誰? sylph01 / 梶原 龍 Twitter: @s01 暗号とかできます Elixir とかできます

    Rails まるでわからん
  4. Rails( 鉄道) にはよく乗ります

  5. 若干真面目な自己紹介 やせいのプログラマ( 要するにフリーランス) W3C, IETF などでセキュリティ寄りのプロトコル の標準化のお手伝いをしていました HTTPS in Local

    Network, Messaging Layer Security など 次世代OAuth の薄い本を書きました 現バージョンは今は頒布中止していますが新 バージョン出したい Kaigi on Rails 1 週間前に松山に引っ越しました
  6. 現場の宣伝 株式会社コードタクトにて「まなびポケット」の認証認可基盤の開発をしていま す 認証認可チームにて新規メンバーを募集中です 既存の認証基盤を置き換える新規開発を行います モダンな認証認可技術で日本の公教育にイノベーションをもたらしましょう

  7. None
  8. Rails の認証? 要するにDevise のことでしょ?

  9. なんか強い人から Devise はイケてないって聞いたんだけど?

  10. Rails sucks? It's most likely that you suck Devise sucks?

    It's most likely that you suck
  11. 本発表の目的 認証機能の自作と解説、また主要な認証ライブラリのアプローチの比較を通して、 認証技術への理解を深めること、また認証ライブラリの選択を手助けすること を目指します

  12. Disclaimer Do try this at home, but Do not try

    this in production できる限り脆弱性を埋め込まないように気をつけて作ることをしますが、通常の場合 production では多数の人によって検証されているライブラリを使用することをおすすめ します。
  13. Disclaimer (2) なんなら 認証機能は自分で持たないほうがいい。 自分でID を管理するのは飲水を確保するのに自分で井戸を掘ること ID 連携技術を使ってIdP を利用することは近代的な水道インフラに乗っかること OAuth/OpenID

    Connect の話もできるにはできますが今回はその話はしません。
  14. None
  15. 第1 部: 認証を作ってみる

  16. 認証/ 認可についての概念 人は対象をどのように認知するか? 人/ システムは 対象(Entity) を直接認知しない。 人/ システムは Identity(

    属性の集合) を通して対象(Entity) を認知する。 1 つの対象(Entity) は複数の属性(Identity) を持ち、文脈に応じて使い分ける。
  17. 認証/ 認可についての概念 Authentication( 認証) Entity がサービスの認知するIdentity に紐付いているという確証を得ること 一般にいう「ログイン機能」は、識別子(ID) とパスワードの組を提示できるこ とによって、

    利用するEntity がサービス上のEntity に対応しているという確 証を得られることによって成立する Authorization( 認可) リソースにアクセスするための条件を定めること
  18. 忙しい人のための暗号・ハッシュアルゴリズム この後の説明に用いる道具を超高速で説明します。実際の中身はググって

  19. 暗号化(ここでは共通鍵暗号のこと) 一般に128bit 以上のAES を使う(256bit は実はoverkill 気味)。3-DES って言われたら 「臭い」を感じ取って欲しい。 RSA は公開鍵暗号。ここでは説明しない。

  20. ハッシュ化 現代的にはSHA-2(SHA256, SHA512) が一般的。SHA-3 などもあるにはある。あえて新 規にSHA-1 やMD5 を使う理由はない。 元の値に戻すのが困難という性質がセキュアなパスワード認証においてとても重要。

  21. HMAC (超訳: 鍵付きハッシュ) ハッシュ関数を使って秘密の値( 鍵) で特徴づけられる関数を得る方式。秘密の値を知ら ないと関数の形がわからないのでMAC 値が計算できない。「HMAC-SHA256 」という ように「方式名」+「利用するハッシュ関数」の組で呼ばれる。

  22. Web サービスの認証で必要な機能 必ず欲しい ログイン・ログアウト クッキーからの自動再ログイン(remember) パスワードリセット できれば欲しい ロックアウト E メール認証

    ワンタイムパスワード ( 機能はDevise やRodauth のfeatures から抜粋)
  23. パスワード認証に対する攻撃 パスワード認証に対する攻撃は一般に ブルートフォース攻撃 辞書攻撃もこれの亜種 データベース流出を利用したハッシュクラック ローカルで大量にハッシュ計算して衝突させる なお、「暗号化して保存」では復元できてしまうのでダメです の2 つの形を取る。

  24. ハッシュアルゴリズムの選択 パスワードを保存する場合のハッシュアルゴリズムは遅いものほどよい。この理由は 正規のハッシュは1 回しか計算されないが攻撃の際は複数のハッシュを計算しなくては いけないため。 (詳細は省略、キーワードで検索してください) MD5: 伸長攻撃が可能、そもそもハッシュ長が短いので衝突させやすい SHA-1: 強衝突耐性が突破されている

    SHA-2: 衝突耐性の面では現時点では十分だが、計算が十分に遅くない 複数回適用して遅くするPBKDF2 という方式がある パスワードハッシュ用に開発されたbcrypt はわざと計算を遅くしている
  25. (https://twitter.com/TerahashCorp/status/1155128018156892160 より) bcrypt なら8 桁でもクラックするのにGPU クラスタでも18 年かかる!

  26. salt ユーザーごとにランダムな文字列s を生成し、ハッシュ値の と平文の s を保存 することによって、事前にハッシュを計算しておいてテーブルをルックアップするレ インボーテーブルアタックを回避できる。 bcrypt では出力される文字列がsalt

    とハッシュ値の組を結合したものになっている。 h(p∣∣s)
  27. has_secure_password Rails では has_secure_password というモデルのメソッドがbcrypt を利用してパスワ ードハッシュ化の面倒を見てくれる。パスワード入力の確認も面倒を見てくれる。便 利。 ソースは activemodel/lib/active_model/secure_password.rb

  28. 応用 攻撃者が正解のハッシュ値を得られないようにすればよいので、もっとがんばるなら 以下のような方法が取れる: HMAC-SHA256 のsecret を外部のHardware Security Module に保存して、HSM の

    API を通してハッシュ計算をする secret はHSM 上にしかないので、ハッシュ値の計算がHSM にしかできなくな る このような手法をpassword pepper と呼ぶらしい ハッシュ値そのもののアクセスを可能な限りさせないためにデータベース関数を 利用する 後で紹介する rodauth がこの方式を使っている
  29. クッキーからの自動再ログイン、パスワードリセッ ト、E メール認証 基本的に原理は同じで、 有効期限つきの乱数列を払い出しユーザーモデルに関連づける この際、データベースに保存するのはハッシュ化された値 乱数列が利用される際には有効期限を確認、利用されたら乱数列を破棄 E メール認証とパスワードリセットではこの乱数列を登録しているメールアドレス に対して送信する

    すべて同様に 「生成した乱数列を知っていて」「有効期限内で提示できる」 という性 質をもってEntity が登録ユーザーに対応することを確認している。
  30. クッキーからの自動再ログイン?セッションじゃダメ なの? セッション: ブラウザウィンドウが開いている間のみ有効な Cookie のこと セッションはCookie のサブセット Cookie: ここでは「

    ブラウザウィンドウよりも長いライフタイムを持つ Cookie 」 のことを指す 明示的に有効期限を指定したものを指す なのでブラウザウィンドウを閉じた後の再ログインに使える Rails ではセッションは自動で( secret_key_base を使って) 暗号化される Cookie は明示的にsigned かencrypted を指定する必要がある
  31. ワンタイムパスワード HOTP: An HMAC-Based One-Time Password Algorithm (RFC 4226) TOTP:

    Time-Based One-Time Password Algorithm (RFC 6238) を用いるものが一般的。 サーバーと共通の秘密を知っていて、共通の秘密から時刻などに基づいて特定の値を 導出できる という性質をもってEntity が登録ユーザーに対応することを確認してい る。
  32. ロックアウト( ブルートフォース攻撃対策) 以下の性質を持つカウンタを用意 不正なログイン試行でカウントが1 増えて 正常なログインで0 に戻る 一定以上のカウントを持っている場合、最終ログイン時間から一定時間が経過し ていない場合パスワードがあっていても自動的にログインに失敗する 試行回数に対して指数でログイン不可能時間を設けるexponential

    backoff algorithm という方式が一般的
  33. 実際に作ってみた https://github.com/sylph01/touch-and-learn-authentication/ 以下のRails アプリにこれらの欲しい機能をできるだけプリミティブに実装したサンプ ルを置いています。

  34. None
  35. 第2 部: 認証ライブラリの話

  36. ( 再掲) Rails の認証? 要するにDevise のことでしょ?

  37. 何でライブラリが欲しいか 楽をしたい 読みやすいイディオム/DSL の形で認証機能を使いたい いろんな人の目が入ってるのでセキュリティバグを埋め込んでいる可能性が少な い

  38. ライブラリ化する場合に行うこと 初期設定手段を用意 rails generate のジェネレータを用意するのが一般的 モデル、コントローラーなどのクラスを拡張しDSL を追加する

  39. 何でライブラリがあるのに自作をしたか 今回は学習目的 production でも一度自作している ユーザーインターフェースを伴わないJSON API で、アクセストークンを払い 出す機構だけが欲しかった 一般にproduction でライブラリを使わない理由があるとすれば目的に合致しな

    いから
  40. ライブラリの比較 Devise Sorcery Authlogic Rodauth を対象に比較をしていきます。 発表者の経験としては「Devise はproduction で使ったことがある」「あとは今回調べ た」程度です。

  41. Devise (1) Warden の上に作られている Warden とは: 認証用 rack middleware session

    middleware の後に入って、session の情報を使って認証状態を確かめ たり認証アクションをトリガーしたりする
  42. 超忙しい人のためのWarden env['warden'].authenticated? - 認証済みであるかを確かめる env['warden'].authenticate(:password) - :password strategy で認証を行う。実際 の認証は各々定義するstrategy

    の中で行う 成功したら env['warden'].user にuser object が入ってくる 認証エラー時は throw(:warden) でWarden の例外を投げる
  43. Devise (2) Devise のstrategy は lib/devise/strategies 以下にある。パスワード認証は DatabaseAuthenticatable strategy で実装されている。

    コントローラーで用いる signed_in? , sign_in , sign_out などは Devise::Controllers::SignInOut で実装されている。Warden の authenticate? や set_user や logout が使われていることがわかる。
  44. Devise (3) Routes に devise_for :users を書くとそのUser が対応しているDevise のモジュール に応じてDevise

    の提供するcontroller へのroute が設定される。 Controller のアクションをカスタマイズしたいときにはDevise の提供するcontroller をそ のまま使いたくない。多分これがDevise を嫌う一番の理由か?
  45. Sorcery code generation を可能な限り使わない、シンプルに切り詰めた認証ライブラリ Devise ではデフォルトから離れたことをしようと思うとコントローラーを継 承したりoverride したりしないといけない Sorcery ではライブラリのメソッドを自分のMVC

    コードの中で使う ただし自己責任の部分が増える 設定はInitializer にまとまっている コード中で sorcery_config を取る動作がよく見られるのはこれ 暗号コードはAuthlogic をベースにしている パスワードの暗号化が可能 at your own risk...
  46. require_login login(email, password, remember_me = false) auto_login(user) logout logged_in? current_user

    redirect_back_or_to @user.external? @user.active_for_authentication? @user.valid_password?('secret') User.authenticates_with_sorcery! (GitHub のreadme より) パスワード認証だけならメソッドは11 個!
  47. Authlogic Session オブジェクトを中心に据えた認証ライブラリ 他のライブラリではログインセッションが明示的にオブジェクトで表されな いことに注意 モデルに acts_as_authentic と書くと機能が有効化される 他では対応していない外部認証プロバイダ(OpenID, LDAP,

    PAM, x509) に対応でき る generator がない モデルのセットアップ時にREADME にあるmigration から必要な機能分を選ん で手書きする
  48. UserSession.create(:login => "bjohnson", :password => "my password", :remember_me => true)

    session = UserSession.new(:login => "bjohnson", :password => "my password", :remember_me => true) session.save session = UserSession.find session.destroy (GitHub のreadme より抜粋)
  49. Rodauth (1) "Ruby's most advanced authentication framework" の名に恥じない圧倒的高機能 暗号技術ファンとして素直に感心する WebAuthn

    、ワンタイムパスワード、SMS 、JWT のサポート データベース関数によるパスワードハッシュへのアクセス HMAC を使った"password pepper" の徹底 Rails/ActiveRecord を前提としていない Roda とSequel で作られている がRails での利用方法はそんなに自明ではない
  50. Rodauth (2): データベース関数の利用 普通のRails アプリではアプリを実行するユーザーがパスワードハッシュ値にアク セスできる 通常のアプリユーザーとパスワードハッシュ値にアクセスできるユーザーを分離 パスワードハッシュ値をアプリに見せることなく値の設定や比較を行うデー タベース関数を定義 アプリユーザーはデータベース関数を利用するだけ

    データベースの権限昇格が発生しない限りパスワードハッシュが漏れることがな い
  51. Rodauth (3): password pepper の徹底 hmac_secret を設定することで、以下の値(p.29 で説明した仕組み) の保存時にHMAC が適用される(→

    共通のsecret を使う"password pepper" )。 E メールで送信するtoken remember で使用するtoken ワンタイムパスワードで使用するトークンはユーザーに提示されるキーにHMAC が適 用される。 hmac_secret をメモリ上にのみ存在させることで攻撃者はハッシュ(HMAC) 値の計算 に用いる関数を知ることができない。
  52. 外部認証プロバイダ利用 Devise はOmniAuth が利用できる Authlogic はプラグインが複数ある レガシーな認証方式に対してもプラグインがある Rodauth でもLDAP は対応している

    Sorcery はExternal プラグインというのが同梱されている Rodauth は見る限りまだ?
  53. おまけ: ハッシュ済みパスワードのカラム名 Devise は encrypted_password 一般にハッシュ化した値をencrypted であるとは言わない Sorcery, Authlogic は

    crypted_password そもそも暗号化済みを指す語は "crypted" ではない Rodauth は password_digest ハッシュ化した値のことを "digest" と呼ぶのは正式な用法 has_secure_password で実装した場合も password_digest
  54. 多分こういう使い分けになる Rails/ActiveRecord に縛られないものが欲しい: Rodauth とにかく普通のパスワード認証+α をさくっと作りたい: Devise 認証周りにたくさんカスタムコードがあって細かく制御したい: Sorcery, Authlogic

    外部認証プロバイダへの移行がありそう: 今のところRodauth 以外 レガシー外部認証方式はAuthlogic に一日の長がある ほぼ初期設定でとにかくセキュアにしたい: Rodauth が有利か? 他がinsecure であるとは言ってないことに注意
  55. まとめ パスワード認証とその付随技術の実装の注意点を紹介しました 主な認証ライブラリの特徴とその使い分けを紹介しました

  56. Welcome to Authentication Hell ( また今年も沼に人を誘ってしまった…)

  57. Questions / Comments? Send them to @s01 or see you

    in the Q&A session!
  58. おまけ: Further Reading デジタルアイデンティティの考え方そのものについて 『デジタルアイデンティティー 経営者が知らないサイバービジネスの核心』 崎村夏彦 暗号技術と認証技術 『図解即戦力 暗号と認証のしくみと理論がこれ1

    冊でしっかりわかる教科 書』光成滋生 『暗号技術のすべて』IPUSIRON
  59. おまけ: 時間が足りなくて話せなかったことのメモ ハッシュの衝突耐性について: 過去に記事書きました secure string comparison (timing-safe comparison) 「前から順に一致判定して途中で打ち切る」方法では時間差を測定すること

    で情報量の漏れが発生する セッションハイジャックの対策 HTTPS を使おう、 Secure かつ HttpOnly のCookie を使おう