$30 off During Our Annual Pro Sale. View Details »

Build and Learn Rails Authentication

sylph01
October 22, 2021

Build and Learn Rails Authentication

@ Kaigi on Rails 2021, 2021/10/22

sylph01

October 22, 2021
Tweet

More Decks by sylph01

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

  4. Rails(
    鉄道)
    にはよく乗ります

    View Slide

  5. 若干真面目な自己紹介
    やせいのプログラマ(
    要するにフリーランス)
    W3C, IETF
    などでセキュリティ寄りのプロトコル
    の標準化のお手伝いをしていました
    HTTPS in Local Network, Messaging Layer
    Security
    など
    次世代OAuth
    の薄い本を書きました
    現バージョンは今は頒布中止していますが新
    バージョン出したい
    Kaigi on Rails 1
    週間前に松山に引っ越しました

    View Slide

  6. 現場の宣伝
    株式会社コードタクトにて「まなびポケット」の認証認可基盤の開発をしていま

    認証認可チームにて新規メンバーを募集中です
    既存の認証基盤を置き換える新規開発を行います
    モダンな認証認可技術で日本の公教育にイノベーションをもたらしましょう

    View Slide

  7. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  12. Disclaimer
    Do try this at home, but
    Do not try this in production
    できる限り脆弱性を埋め込まないように気をつけて作ることをしますが、通常の場合
    production
    では多数の人によって検証されているライブラリを使用することをおすすめ
    します。

    View Slide

  13. Disclaimer (2)
    なんなら 認証機能は自分で持たないほうがいい。
    自分でID
    を管理するのは飲水を確保するのに自分で井戸を掘ること
    ID
    連携技術を使ってIdP
    を利用することは近代的な水道インフラに乗っかること
    OAuth/OpenID Connect
    の話もできるにはできますが今回はその話はしません。

    View Slide

  14. View Slide

  15. 第1
    部:
    認証を作ってみる

    View Slide

  16. 認証/
    認可についての概念
    人は対象をどのように認知するか?
    人/
    システムは 対象(Entity)
    を直接認知しない。
    人/
    システムは Identity(
    属性の集合)
    を通して対象(Entity)
    を認知する。
    1
    つの対象(Entity)
    は複数の属性(Identity)
    を持ち、文脈に応じて使い分ける。

    View Slide

  17. 認証/
    認可についての概念
    Authentication(
    認証)
    Entity
    がサービスの認知するIdentity
    に紐付いているという確証を得ること
    一般にいう「ログイン機能」は、識別子(ID)
    とパスワードの組を提示できるこ
    とによって、 利用するEntity
    がサービス上のEntity
    に対応しているという確
    証を得られることによって成立する
    Authorization(
    認可)
    リソースにアクセスするための条件を定めること

    View Slide

  18. 忙しい人のための暗号・ハッシュアルゴリズム
    この後の説明に用いる道具を超高速で説明します。実際の中身はググって

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  25. (https://twitter.com/TerahashCorp/status/1155128018156892160
    より)
    bcrypt
    なら8
    桁でもクラックするのにGPU
    クラスタでも18
    年かかる!

    View Slide

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

    View Slide

  27. has_secure_password
    Rails
    では has_secure_password
    というモデルのメソッドがbcrypt
    を利用してパスワ
    ードハッシュ化の面倒を見てくれる。パスワード入力の確認も面倒を見てくれる。便
    利。
    ソースは activemodel/lib/active_model/secure_password.rb

    View Slide

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

    API
    を通してハッシュ計算をする
    secret
    はHSM
    上にしかないので、ハッシュ値の計算がHSM
    にしかできなくな

    このような手法をpassword pepper
    と呼ぶらしい
    ハッシュ値そのもののアクセスを可能な限りさせないためにデータベース関数を
    利用する
    後で紹介する rodauth
    がこの方式を使っている

    View Slide

  29. クッキーからの自動再ログイン、パスワードリセッ
    ト、E
    メール認証
    基本的に原理は同じで、
    有効期限つきの乱数列を払い出しユーザーモデルに関連づける
    この際、データベースに保存するのはハッシュ化された値
    乱数列が利用される際には有効期限を確認、利用されたら乱数列を破棄
    E
    メール認証とパスワードリセットではこの乱数列を登録しているメールアドレス
    に対して送信する
    すべて同様に 「生成した乱数列を知っていて」「有効期限内で提示できる」 という性
    質をもってEntity
    が登録ユーザーに対応することを確認している。

    View Slide

  30. クッキーからの自動再ログイン?セッションじゃダメ
    なの?
    セッション:
    ブラウザウィンドウが開いている間のみ有効な Cookie
    のこと
    セッションはCookie
    のサブセット
    Cookie:
    ここでは「 ブラウザウィンドウよりも長いライフタイムを持つ Cookie

    のことを指す
    明示的に有効期限を指定したものを指す
    なのでブラウザウィンドウを閉じた後の再ログインに使える
    Rails
    ではセッションは自動で( secret_key_base
    を使って)
    暗号化される
    Cookie
    は明示的にsigned
    かencrypted
    を指定する必要がある

    View Slide

  31. ワンタイムパスワード
    HOTP: An HMAC-Based One-Time Password Algorithm (RFC 4226)
    TOTP: Time-Based One-Time Password Algorithm (RFC 6238)
    を用いるものが一般的。
    サーバーと共通の秘密を知っていて、共通の秘密から時刻などに基づいて特定の値を
    導出できる という性質をもってEntity
    が登録ユーザーに対応することを確認してい
    る。

    View Slide

  32. ロックアウト(
    ブルートフォース攻撃対策)
    以下の性質を持つカウンタを用意
    不正なログイン試行でカウントが1
    増えて
    正常なログインで0
    に戻る
    一定以上のカウントを持っている場合、最終ログイン時間から一定時間が経過し
    ていない場合パスワードがあっていても自動的にログインに失敗する
    試行回数に対して指数でログイン不可能時間を設けるexponential backoff
    algorithm
    という方式が一般的

    View Slide

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

    View Slide

  34. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  41. Devise (1)
    Warden
    の上に作られている
    Warden
    とは:
    認証用 rack middleware
    session middleware
    の後に入って、session
    の情報を使って認証状態を確かめ
    たり認証アクションをトリガーしたりする

    View Slide

  42. 超忙しい人のためのWarden
    env['warden'].authenticated? -
    認証済みであるかを確かめる
    env['warden'].authenticate(:password) - :password strategy
    で認証を行う。実際
    の認証は各々定義するstrategy
    の中で行う
    成功したら env['warden'].user
    にuser object
    が入ってくる
    認証エラー時は throw(:warden)
    でWarden
    の例外を投げる

    View Slide

  43. Devise (2)
    Devise
    のstrategy
    は lib/devise/strategies
    以下にある。パスワード認証は
    DatabaseAuthenticatable strategy
    で実装されている。
    コントローラーで用いる signed_in? , sign_in , sign_out
    などは
    Devise::Controllers::SignInOut
    で実装されている。Warden
    の authenticate?

    set_user
    や logout
    が使われていることがわかる。

    View Slide

  44. Devise (3)
    Routes
    に devise_for :users
    を書くとそのUser
    が対応しているDevise
    のモジュール
    に応じてDevise
    の提供するcontroller
    へのroute
    が設定される。
    Controller
    のアクションをカスタマイズしたいときにはDevise
    の提供するcontroller
    をそ
    のまま使いたくない。多分これがDevise
    を嫌う一番の理由か?

    View Slide

  45. Sorcery
    code generation
    を可能な限り使わない、シンプルに切り詰めた認証ライブラリ
    Devise
    ではデフォルトから離れたことをしようと思うとコントローラーを継
    承したりoverride
    したりしないといけない
    Sorcery
    ではライブラリのメソッドを自分のMVC
    コードの中で使う
    ただし自己責任の部分が増える
    設定はInitializer
    にまとまっている
    コード中で sorcery_config
    を取る動作がよく見られるのはこれ
    暗号コードはAuthlogic
    をベースにしている
    パスワードの暗号化が可能
    at your own risk...

    View Slide

  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
    個!

    View Slide

  47. Authlogic
    Session
    オブジェクトを中心に据えた認証ライブラリ
    他のライブラリではログインセッションが明示的にオブジェクトで表されな
    いことに注意
    モデルに acts_as_authentic
    と書くと機能が有効化される
    他では対応していない外部認証プロバイダ(OpenID, LDAP, PAM, x509)
    に対応でき

    generator
    がない
    モデルのセットアップ時にREADME
    にあるmigration
    から必要な機能分を選ん
    で手書きする

    View Slide

  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
    より抜粋)

    View Slide

  49. Rodauth (1)
    "Ruby's most advanced authentication framework"
    の名に恥じない圧倒的高機能
    暗号技術ファンとして素直に感心する
    WebAuthn
    、ワンタイムパスワード、SMS
    、JWT
    のサポート
    データベース関数によるパスワードハッシュへのアクセス
    HMAC
    を使った"password pepper"
    の徹底
    Rails/ActiveRecord
    を前提としていない
    Roda
    とSequel
    で作られている
    がRails
    での利用方法はそんなに自明ではない

    View Slide

  50. Rodauth (2):
    データベース関数の利用
    普通のRails
    アプリではアプリを実行するユーザーがパスワードハッシュ値にアク
    セスできる
    通常のアプリユーザーとパスワードハッシュ値にアクセスできるユーザーを分離
    パスワードハッシュ値をアプリに見せることなく値の設定や比較を行うデー
    タベース関数を定義
    アプリユーザーはデータベース関数を利用するだけ
    データベースの権限昇格が発生しない限りパスワードハッシュが漏れることがな

    View Slide

  51. Rodauth (3): password pepper
    の徹底
    hmac_secret
    を設定することで、以下の値(p.29
    で説明した仕組み)
    の保存時にHMAC
    が適用される(→
    共通のsecret
    を使う"password pepper"
    )。
    E
    メールで送信するtoken
    remember
    で使用するtoken
    ワンタイムパスワードで使用するトークンはユーザーに提示されるキーにHMAC
    が適
    用される。
    hmac_secret
    をメモリ上にのみ存在させることで攻撃者はハッシュ(HMAC)
    値の計算
    に用いる関数を知ることができない。

    View Slide

  52. 外部認証プロバイダ利用
    Devise
    はOmniAuth
    が利用できる
    Authlogic
    はプラグインが複数ある
    レガシーな認証方式に対してもプラグインがある
    Rodauth
    でもLDAP
    は対応している
    Sorcery
    はExternal
    プラグインというのが同梱されている
    Rodauth
    は見る限りまだ?

    View Slide

  53. おまけ:
    ハッシュ済みパスワードのカラム名
    Devise
    は encrypted_password
    一般にハッシュ化した値をencrypted
    であるとは言わない
    Sorcery, Authlogic
    は crypted_password
    そもそも暗号化済みを指す語は "crypted"
    ではない
    Rodauth
    は password_digest
    ハッシュ化した値のことを "digest"
    と呼ぶのは正式な用法
    has_secure_password
    で実装した場合も password_digest

    View Slide

  54. 多分こういう使い分けになる
    Rails/ActiveRecord
    に縛られないものが欲しい: Rodauth
    とにかく普通のパスワード認証+α
    をさくっと作りたい: Devise
    認証周りにたくさんカスタムコードがあって細かく制御したい: Sorcery, Authlogic
    外部認証プロバイダへの移行がありそう:
    今のところRodauth
    以外
    レガシー外部認証方式はAuthlogic
    に一日の長がある
    ほぼ初期設定でとにかくセキュアにしたい: Rodauth
    が有利か?
    他がinsecure
    であるとは言ってないことに注意

    View Slide

  55. まとめ
    パスワード認証とその付随技術の実装の注意点を紹介しました
    主な認証ライブラリの特徴とその使い分けを紹介しました

    View Slide

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

    View Slide

  57. Questions / Comments?
    Send them to @s01
    or see you in the Q&A session!

    View Slide

  58. おまけ: Further Reading
    デジタルアイデンティティの考え方そのものについて
    『デジタルアイデンティティー 経営者が知らないサイバービジネスの核心』
    崎村夏彦
    暗号技術と認証技術
    『図解即戦力 暗号と認証のしくみと理論がこれ1
    冊でしっかりわかる教科
    書』光成滋生
    『暗号技術のすべて』IPUSIRON

    View Slide

  59. おまけ:
    時間が足りなくて話せなかったことのメモ
    ハッシュの衝突耐性について:
    過去に記事書きました
    secure string comparison (timing-safe comparison)
    「前から順に一致判定して途中で打ち切る」方法では時間差を測定すること
    で情報量の漏れが発生する
    セッションハイジャックの対策
    HTTPS
    を使おう、 Secure
    かつ HttpOnly
    のCookie
    を使おう

    View Slide