Share
Embed
Transcript
59 slides
-
Build and Learn Rails Authentication Ryo Kajiwara (sylph01) 2021/10/21 @
Kaigi on Rails 2021 -
誰? sylph01 / 梶原 龍 Twitter: @s01 暗号とかできます Elixir とかできます
Rails まるでわからん -
若干真面目な自己紹介 やせいのプログラマ( 要するにフリーランス) W3C, IETF などでセキュリティ寄りのプロトコル の標準化のお手伝いをしていました HTTPS in Local
Network, Messaging Layer Security など 次世代OAuth の薄い本を書きました 現バージョンは今は頒布中止していますが新 バージョン出したい Kaigi on Rails 1 週間前に松山に引っ越しました -
Rails sucks? It's most likely that you suck Devise sucks?
It's most likely that you suck -
Disclaimer Do try this at home, but Do not try
this in production できる限り脆弱性を埋め込まないように気をつけて作ることをしますが、通常の場合 production では多数の人によって検証されているライブラリを使用することをおすすめ します。 -
Disclaimer (2) なんなら 認証機能は自分で持たないほうがいい。 自分でID を管理するのは飲水を確保するのに自分で井戸を掘ること ID 連携技術を使ってIdP を利用することは近代的な水道インフラに乗っかること OAuth/OpenID
Connect の話もできるにはできますが今回はその話はしません。 -
認証/ 認可についての概念 人は対象をどのように認知するか? 人/ システムは 対象(Entity) を直接認知しない。 人/ システムは Identity(
属性の集合) を通して対象(Entity) を認知する。 1 つの対象(Entity) は複数の属性(Identity) を持ち、文脈に応じて使い分ける。 -
認証/ 認可についての概念 Authentication( 認証) Entity がサービスの認知するIdentity に紐付いているという確証を得ること 一般にいう「ログイン機能」は、識別子(ID) とパスワードの組を提示できるこ とによって、
利用するEntity がサービス上のEntity に対応しているという確 証を得られることによって成立する Authorization( 認可) リソースにアクセスするための条件を定めること -
Web サービスの認証で必要な機能 必ず欲しい ログイン・ログアウト クッキーからの自動再ログイン(remember) パスワードリセット できれば欲しい ロックアウト E メール認証
ワンタイムパスワード ( 機能はDevise やRodauth のfeatures から抜粋) -
ハッシュアルゴリズムの選択 パスワードを保存する場合のハッシュアルゴリズムは遅いものほどよい。この理由は 正規のハッシュは1 回しか計算されないが攻撃の際は複数のハッシュを計算しなくては いけないため。 (詳細は省略、キーワードで検索してください) MD5: 伸長攻撃が可能、そもそもハッシュ長が短いので衝突させやすい SHA-1: 強衝突耐性が突破されている
SHA-2: 衝突耐性の面では現時点では十分だが、計算が十分に遅くない 複数回適用して遅くするPBKDF2 という方式がある パスワードハッシュ用に開発されたbcrypt はわざと計算を遅くしている -
応用 攻撃者が正解のハッシュ値を得られないようにすればよいので、もっとがんばるなら 以下のような方法が取れる: HMAC-SHA256 のsecret を外部のHardware Security Module に保存して、HSM の
API を通してハッシュ計算をする secret はHSM 上にしかないので、ハッシュ値の計算がHSM にしかできなくな る このような手法をpassword pepper と呼ぶらしい ハッシュ値そのもののアクセスを可能な限りさせないためにデータベース関数を 利用する 後で紹介する rodauth がこの方式を使っている -
クッキーからの自動再ログイン、パスワードリセッ ト、E メール認証 基本的に原理は同じで、 有効期限つきの乱数列を払い出しユーザーモデルに関連づける この際、データベースに保存するのはハッシュ化された値 乱数列が利用される際には有効期限を確認、利用されたら乱数列を破棄 E メール認証とパスワードリセットではこの乱数列を登録しているメールアドレス に対して送信する
すべて同様に 「生成した乱数列を知っていて」「有効期限内で提示できる」 という性 質をもってEntity が登録ユーザーに対応することを確認している。 -
クッキーからの自動再ログイン?セッションじゃダメ なの? セッション: ブラウザウィンドウが開いている間のみ有効な Cookie のこと セッションはCookie のサブセット Cookie: ここでは「
ブラウザウィンドウよりも長いライフタイムを持つ Cookie 」 のことを指す 明示的に有効期限を指定したものを指す なのでブラウザウィンドウを閉じた後の再ログインに使える Rails ではセッションは自動で( secret_key_base を使って) 暗号化される Cookie は明示的にsigned かencrypted を指定する必要がある -
ワンタイムパスワード HOTP: An HMAC-Based One-Time Password Algorithm (RFC 4226) TOTP:
Time-Based One-Time Password Algorithm (RFC 6238) を用いるものが一般的。 サーバーと共通の秘密を知っていて、共通の秘密から時刻などに基づいて特定の値を 導出できる という性質をもってEntity が登録ユーザーに対応することを確認してい る。 -
Devise (1) Warden の上に作られている Warden とは: 認証用 rack middleware session
middleware の後に入って、session の情報を使って認証状態を確かめ たり認証アクションをトリガーしたりする -
超忙しい人のためのWarden env['warden'].authenticated? - 認証済みであるかを確かめる env['warden'].authenticate(:password) - :password strategy で認証を行う。実際 の認証は各々定義するstrategy
の中で行う 成功したら env['warden'].user にuser object が入ってくる 認証エラー時は throw(:warden) でWarden の例外を投げる -
Devise (2) Devise のstrategy は lib/devise/strategies 以下にある。パスワード認証は DatabaseAuthenticatable strategy で実装されている。
コントローラーで用いる signed_in? , sign_in , sign_out などは Devise::Controllers::SignInOut で実装されている。Warden の authenticate? や set_user や logout が使われていることがわかる。 -
Devise (3) Routes に devise_for :users を書くとそのUser が対応しているDevise のモジュール に応じてDevise
の提供するcontroller へのroute が設定される。 Controller のアクションをカスタマイズしたいときにはDevise の提供するcontroller をそ のまま使いたくない。多分これがDevise を嫌う一番の理由か? -
Sorcery code generation を可能な限り使わない、シンプルに切り詰めた認証ライブラリ Devise ではデフォルトから離れたことをしようと思うとコントローラーを継 承したりoverride したりしないといけない Sorcery ではライブラリのメソッドを自分のMVC
コードの中で使う ただし自己責任の部分が増える 設定はInitializer にまとまっている コード中で sorcery_config を取る動作がよく見られるのはこれ 暗号コードはAuthlogic をベースにしている パスワードの暗号化が可能 at your own risk... -
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 個! -
Authlogic Session オブジェクトを中心に据えた認証ライブラリ 他のライブラリではログインセッションが明示的にオブジェクトで表されな いことに注意 モデルに acts_as_authentic と書くと機能が有効化される 他では対応していない外部認証プロバイダ(OpenID, LDAP,
PAM, x509) に対応でき る generator がない モデルのセットアップ時にREADME にあるmigration から必要な機能分を選ん で手書きする -
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 より抜粋) -
Rodauth (1) "Ruby's most advanced authentication framework" の名に恥じない圧倒的高機能 暗号技術ファンとして素直に感心する WebAuthn
、ワンタイムパスワード、SMS 、JWT のサポート データベース関数によるパスワードハッシュへのアクセス HMAC を使った"password pepper" の徹底 Rails/ActiveRecord を前提としていない Roda とSequel で作られている がRails での利用方法はそんなに自明ではない -
Rodauth (3): password pepper の徹底 hmac_secret を設定することで、以下の値(p.29 で説明した仕組み) の保存時にHMAC が適用される(→
共通のsecret を使う"password pepper" )。 E メールで送信するtoken remember で使用するtoken ワンタイムパスワードで使用するトークンはユーザーに提示されるキーにHMAC が適 用される。 hmac_secret をメモリ上にのみ存在させることで攻撃者はハッシュ(HMAC) 値の計算 に用いる関数を知ることができない。 -
外部認証プロバイダ利用 Devise はOmniAuth が利用できる Authlogic はプラグインが複数ある レガシーな認証方式に対してもプラグインがある Rodauth でもLDAP は対応している
Sorcery はExternal プラグインというのが同梱されている Rodauth は見る限りまだ? -
おまけ: ハッシュ済みパスワードのカラム名 Devise は encrypted_password 一般にハッシュ化した値をencrypted であるとは言わない Sorcery, Authlogic は
crypted_password そもそも暗号化済みを指す語は "crypted" ではない Rodauth は password_digest ハッシュ化した値のことを "digest" と呼ぶのは正式な用法 has_secure_password で実装した場合も password_digest -
多分こういう使い分けになる Rails/ActiveRecord に縛られないものが欲しい: Rodauth とにかく普通のパスワード認証+α をさくっと作りたい: Devise 認証周りにたくさんカスタムコードがあって細かく制御したい: Sorcery, Authlogic
外部認証プロバイダへの移行がありそう: 今のところRodauth 以外 レガシー外部認証方式はAuthlogic に一日の長がある ほぼ初期設定でとにかくセキュアにしたい: Rodauth が有利か? 他がinsecure であるとは言ってないことに注意 -
Questions / Comments? Send them to @s01 or see you
in the Q&A session! -
おまけ: Further Reading デジタルアイデンティティの考え方そのものについて 『デジタルアイデンティティー 経営者が知らないサイバービジネスの核心』 崎村夏彦 暗号技術と認証技術 『図解即戦力 暗号と認証のしくみと理論がこれ1
冊でしっかりわかる教科 書』光成滋生 『暗号技術のすべて』IPUSIRON -
おまけ: 時間が足りなくて話せなかったことのメモ ハッシュの衝突耐性について: 過去に記事書きました secure string comparison (timing-safe comparison) 「前から順に一致判定して途中で打ち切る」方法では時間差を測定すること
で情報量の漏れが発生する セッションハイジャックの対策 HTTPS を使おう、 Secure かつ HttpOnly のCookie を使おう