Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

現場の宣伝 株式会社コードタクトにて「まなびポケット」の認証認可基盤の開発をしていま す 認証認可チームにて新規メンバーを募集中です 既存の認証基盤を置き換える新規開発を行います モダンな認証認可技術で日本の公教育にイノベーションをもたらしましょう

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

応用 攻撃者が正解のハッシュ値を得られないようにすればよいので、もっとがんばるなら 以下のような方法が取れる: HMAC-SHA256 のsecret を外部のHardware Security Module に保存して、HSM の API を通してハッシュ計算をする secret はHSM 上にしかないので、ハッシュ値の計算がHSM にしかできなくな る このような手法をpassword pepper と呼ぶらしい ハッシュ値そのもののアクセスを可能な限りさせないためにデータベース関数を 利用する 後で紹介する rodauth がこの方式を使っている

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

クッキーからの自動再ログイン?セッションじゃダメ なの? セッション: ブラウザウィンドウが開いている間のみ有効な Cookie のこと セッションはCookie のサブセット Cookie: ここでは「 ブラウザウィンドウよりも長いライフタイムを持つ Cookie 」 のことを指す 明示的に有効期限を指定したものを指す なのでブラウザウィンドウを閉じた後の再ログインに使える Rails ではセッションは自動で( secret_key_base を使って) 暗号化される Cookie は明示的にsigned かencrypted を指定する必要がある

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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