Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Secure JSON Web Token (JWT) を使用して Ruby アプリを強化する

Web アプリケーションにユーザーが関与する場合、当然のことながら、ユーザーのデータは保護され、安全に保護される必要があります。

Web アプリケーションのセキュリティを確保するには、いくつかの意味があります。この投稿では、JSON Web トークン (JWT) と Ruby on Rails Web アプリケーション フレームワークを使用した認証を含む Web セキュリティのサブセットについて説明します。

始めましょう!

JSON Web トークンとは何ですか?

JSON Web トークンは、Internet Engineering Task Force (IETF) によって「二者間で転送されるクレームを表現するコンパクトで URL セーフな手段」として定義されたインターネット標準です。

ここで、「クレーム」とは、主題に関するさまざまな情報を指します。クレームは名前と値のペアとして表されます。名前は常に文字列であり、値は任意の JSON 値にすることができます。

JSON Web トークンの基本構造

JWT の複雑さの詳細については、この投稿の範囲外です。そうは言っても、JWT の構造を知ることは価値があります。

JWT は、ヘッダー、ペイロード、署名というピリオドで区切られた 3 つの部分で構成されます。

JWT の例は次のようになります。

 

読みやすくするために、トークンの各部分は新しい行で始まります。実際には、パーツは結合されます。

このトークンはサーバーからクライアントに送信されます。クライアントはトークンをサーバーに送信して自身を識別し、リクエストを処理させます。

最初の部分であるヘッダーには、トークンの生成に使用されるアルゴリズムとトークンのタイプに関する情報が含まれています。デコードすると、次のような結果が得られます。

 

2 番目の部分であるペイロードには、ユーザーに関する一連のクレームが含まれています。ほとんどの場合、これはクライアント/サーバー設定のクライアントであり、次のようになります。

 

署名 (署名された JWT の最後の部分) によってトークンが検証されます。これは、Base64url エンコーディング - RFC 4648 を使用してヘッダーとペイロードをエンコードし、それらをピリオド区切り文字で連結することによって生成されます。

基本的に、署名ビットで何が起こるかは次のとおりです。

 

HMAC_SHA256 署名をハッシュする SHA-256 ハッシュ関数から構築された、キー付きハッシュ アルゴリズムの一種です。暗号化アルゴリズムの選択は "alg": "HS256" によって決まります。 ヘッダーにあります。トークンが署名されていない場合、署名のないヘッダーとペイロードのみが含まれます。

JWT とRuby アプリのその他の認証方法

JSON Web トークンは、名前が示すように、トークンベースです。その対極には、ユーザーを認証するより伝統的な方法であるセッションベースの認証があります。セッションベースの認証のフローは、トークンベースの認証のフローとはまったく異なります。

セッションベースの認証のフローは次のようになります。

<オル>
  • ユーザーまたはクライアントは、ユーザー認証情報を含むリクエストを送信します。
  • サーバーはユーザーを認証し、セッションを保存し、ブラウザに Cookie として保存されたセッション ID を返します。
  • クライアントは、後続のリクエストとともに Cookie をサーバーに送信します。
  • サーバーは提示されたセッション情報を検査し、有効であればユーザーを認証し、要求された情報をクライアントに返します。
  • セッションベースの認証は主にクライアントとサーバーの接続に使用されますが、トークンベースの認証はサーバーとサーバーの接続 (たとえば、2 つの API 間) でよく使用されます。

    ただし、注意すべき重要な違いの 1 つは、セッションベースの認証では、認証状態はサーバーで処理され、トークンはクライアントで管理されることです。

    認証に JWT を使用する理由

    実装が比較的簡単であること以外にも、認証に JSON Web トークンを使用することには次のような利点があります。

    • それらはステートレスです つまり、セッション ストアは必要ありません。トークン自体にはすべてのユーザー情報が含まれているため、リクエストごとにデータベースや認証サーバーに情報を問い合わせる必要はありません。
    • JWT は一般的にパフォーマンスが高い 従来のほとんどの認証方法よりも(サーバーがユーザーを認証するためにデータベースやストアに対する検索を行わない限り)、非常に効率的です。
    • 強固なセキュリティ保証も提供します 署名された JWT は、攻撃者やクライアントがトークンを変更して保護されたデータにアクセスできないように保護機能を提供するという点で

    Ruby アプリの JWT ベスト プラクティス

    JWT の署名に使用される秘密鍵は長く、ランダムで、複雑な文字の組み合わせである必要があることは言うまでもありません。これにより、キーが十分に安全であることが保証され、攻撃者によるブルート フォース攻撃が困難になります。

    Rails が生成する秘密鍵はほとんどの場合安全ですが、誤って鍵をコミットして公開した場合、安全性の保証は無効になります。

    ネットワーク上の当事者間でトークンを転送するときに、トランスポート層セキュリティ (TLS) を使用することも重要です。 TLS は中間者攻撃を軽減できます (トークンベースの認証方法とセッションベースの認証方法の両方がそのような攻撃を受けやすいです)。

    Rails アプリでの JWT 認証の実装

    トークンベースの認証フローを見てみましょう。

    Secure JSON Web Token (JWT) を使用して Ruby アプリを強化する

    セッションを使用して認証されたユーザーを追跡するステートフル認証手法であるセッションベースの認証とは異なり、JWT によるトークンベースの認証はステートレスです。ユーザーの認証状態に関する情報をサーバーに保存する必要はありません。これにより、アプリケーションの設計が簡素化されます。

    この投稿では、アプリケーションがフロントエンドとバックエンドに分割されていることを前提とします。認証はバックエンドで行われるため、認証を備えた Rails API バックエンドを構築します。

    この投稿のサンプル コードは、Rails 7.0.5 と Ruby 3.2.2 に基づいています。

    jwt の使用 と bcrypt ルビージェム

    アプリケーションには 2 つの gem が必要です:jwt および bcrypt .

    jwt は、RFC 7519 OAuth JSON Web Token 標準の Ruby 実装です。 bcrypt OpenBSD bcrypt() の Ruby バインディングです。 パスワード ハッシュ アルゴリズム。

    このコード リポジトリのサンプル コードに従うことができます。

    注: jwt JWT を操作するための唯一のソリューションではありません。もう 1 つのよく知られた gem は devise-jwt です。 、Devise と Rails に JWT 認証を提供します。ただし、jwt に焦点を当てます。 この投稿で。

    始めましょう。

    Rails API の構築

    まず必要なのは API アプリケーションです。以下を使用して作成します。

     

    --api ここのオプションは、API アプリケーション専用の小さな Rails スタックを事前構成します。

    Gemfile 内に、最初の依存関係 jwt を追加できます。 。 2 番目の gem、bcrypt は、新しく生成された Rails アプリケーションの Gemfile にすでに含まれています。コメントを解除するだけで済みます。

    bcrypt が必要です データベース内のユーザーパスワードを安全にハッシュ化します。 bcrypt は使用しないことに注意してください。 直接的に。 Active Model の has_secure_password を利用します。 bcrypt に依存するクラス メソッド .

    新しい Rails アプリケーションに付属するデフォルトの gem を無視すると、Gemfile は次のようになります。

     

    bundle install を使用して gem をインストールする良い機会です。 .

    User を生成します と Product モデル

    次に、2 つのモデルを生成します:User および ProductUser はユーザーを表すモデルとなり、Product で表される製品へのアクセスを許可するためにそれを認証します。 モデル。

     

    rails db:migrate で移行を実行した後 、モデルのセットアップが完了し、スキーマが db/schema.rb にあります。 、次のようになります。

     

    この段階で、データベースの制約、検証、一意性の確保などの潜在的な問題を意図的に無視していることを指摘することが重要です。この投稿では、特にエラーの処理を無視します。ここでの目的は、Ruby アプリケーションを保護するためのステートレス認証の形式として動作する JWT を実証することです。実稼働アプリケーションでは、 これらすべてがカバーされていることを確認する必要があります

    jwt を構築する ジェムラッパー

    次のステップは、jwt の周囲にラッパーを構築することです。 先ほどインストールした宝石。このラッパーを使用して、サーバーからクライアントへのクレームをエンコードおよびデコードします。このために、app/lib を作成します。 フォルダ。

    lib を使用しない理由 Rails に付属のフォルダーは自動ロードされないということです。 app の下のすべて はデフォルトで自動ロードされ、即時ロードされるため、この場合のセットアップがより簡単になります。

    ラッパー クラスは app/lib/json_web_token.rb にあります。 次のようになります:

     

    ここでの主なメソッドは encode です。 — ユーザー情報をエンコードする — および decode — 後でサーバー内のユーザー情報をデコードします。エンコードとデコードのタスクを jwt にどのように委任しているかに注目してください。 gem から JWT.encode まで および JWT.decode .

    この時点で、Rails コンソールでこのクラスをすでにテストできます。

     

    JsonWebToken.encode(data) の結果がどのようになるかに注目してください。 はピリオドによって 3 つの部分に分割され、ヘッダー、ペイロード、署名が生成されます。 Rails が Rails.application.secrets.secret_key_base を通じて提供する秘密鍵を使用してペイロードに署名したため、署名ビットが得られます。 .

    User に戻る モデル

    User にアクセスしてみると良いでしょう。 app/models/user.rb のモデル 。ここで行う必要があるのは、has_secure_password を追加することだけです。 クラスメソッド:

     

    has_secure_password ユーザーのパスワードをデータベース内で安全にハッシュ化します。

    Rails でサンプル ユーザーと製品を作成する

    これで、Rails コンソールに移動して、データベースにサンプル ユーザーと製品を生成し、アプリケーションのセキュリティをテストできるようになりました。

     

    同じコード部分を seeds.rb に配置できます。

    Rails コントローラーでの JWT の使用

    次のステップでは、コントローラー内で JWT を使用してセキュリティを実装します。 ApplicationController から始めるとよいでしょう。 app/controllers/application_controller.rb で :

     

    ここでは、authenticate を作成します。 ユーザーが送信した JSON Web トークンをデコードするメソッド。トークンを正常に検証できた場合は、User を返します。 リクエストを行ったユーザーを表すオブジェクト。ここでは主にハッピーパスに興味があり、多くのチェックをバイパスします。

    authenticate の定義 ApplicationController のメソッド before_action として設定します それを継承するすべてのコントローラーを保護します。他のコントローラへのリクエストには、それらのコントローラにアクセスするための有効な JWT が必要です (他のすべてのコントローラはこのメイン コントローラを継承するため)。

    次に、AuthenticationController が必要です。 ユーザーはそこにリクエストを送信し、サーバーから署名された JSON Web トークンを取得できます。このコントローラは app/controllers/authentication_controller.rb に配置する必要があります。 次のようになります:

     

    リクエストがこのコントローラーに到達したとき、それはトークンを要求しているユーザーであるため、最初にユーザーを認証する必要はありません。このコントローラーの目的は、ユーザーがサーバー上の残りのリソースにアクセスするために使用できるトークンで応答することです。したがって、skip_before_action :authenticate が必要になります。 2 行目。

    login 内 アクション (ユーザーがトークンを取得するためにヒットするアクション) では、username を取得します。 と password このコントローラーへのリクエストに付属するパラメーターから。ユーザーを認証できた場合、つまりユーザー名とパスワードがデータベースに保存したものと一致するかどうかを確認した場合、署名付きトークンとそのトークンの有効期限が切れる時期に関する情報をユーザーに提示します。

    この場合、トークンの有効期限は使用しません。ただし、実稼働アプリケーションでは、リソースへのアクセスを取り消すために使用される可能性があります。

    curl を使用してこれらすべての手順を実行します。

    保護されたリソースを使用した Ruby アプリケーションのテスト

    フローの一部は、以前の「Rails アプリへの JWT 認証の実装」セクションのフロー図で説明しました。すべてを完全にカバーし、フロー図のすべてのステップをテストするには、保護するリソースが必要です。

    Product があります すでにモデル。次に、製品モデル用のコントローラーと、製品とトークンにアクセスするためのルートが必要です。

    rails g controller Product index でコントローラーを作成しましょう したがって、次のようなものになります。

     

    もちろん、これらのコントローラーにアクセスするためのルートが必要です。私たちの config/routes.rb 次のようになります:

     

    次に、curl を使用してテストします。 すべてが期待どおりに機能するかどうかを確認します。認証するユーザーとアクセスする製品リソースがすでに存在することに注意してください。

    存在しないユーザーの JWT を取得してみましょう。

     

    次の応答が得られるはずです。

     

    次に、前に作成したユーザーで同じことを試してください。

     

    これにより、次のような署名付き JSON Web トークンが得られます。

     

    このトークンをしばらく保持して、不正なトークンを使用して製品リソースにアクセスしてみましょう (トークン内のランダムな文字を変更しました)。

     

    そして、次のものを取得する必要があります:

     

    ただし、以前にサーバーから取得した有効なトークンを使用して製品リソースにアクセスする同じリクエストを行うと、次のようになります。

     

    製品リソースへのアクセスが許可されています:

     

    それです! JSON Web トークンを使用して Ruby アプリケーションを保護することに成功しました!

    まとめ

    この投稿では、JSON Web トークンとその仕組みについて説明しました。最初に、JWT の構造やいくつかのベスト プラクティスなど、JWT の基本について説明しました。次に、jwt を使用して単純な JWT 認証を実装しました。 宝石。

    この投稿がお役に立てば幸いです。コーディングを楽しんでください!

    追記Ruby Magic の投稿を報道後すぐに読みたい場合は、Ruby Magic ニュースレターを購読して、投稿を 1 つも見逃さないようにしてください。


    1. Ruby内部:Rubyオブジェクトのメモリレイアウトの調査

      Ruby内部のクイックツアーをご希望ですか? その後、あなたは御馳走になります。 なぜなら … Rubyオブジェクトがメモリ内にどのように配置されるか、そして内部データ構造を操作していくつかのクールなことを行う方法を一緒に探求します。 シートベルトを締めて、Rubyインタープリターの奥深くへの旅の準備をしてください! アレイのメモリレイアウト 配列を作成するとき、Rubyはそれをシステムメモリと少しのメタデータでバックアップする必要があります。 メタデータに含まれるもの : 配列サイズ(アイテム数) アレイ容量 クラス オブジェクトのステータス(凍結されているかどうか) データが

    2. Ruby および Rails 用の JIT コンパイラー:YJIT、MJIT、および TenderJIT でパフォーマンスを向上

      プログラムは、実行前コンパイルとは異なる方法を使用して実行時にコンパイルされます。このプロセスは、ジャストインタイム コンパイルまたは動的変換として知られています。 この投稿では、利用可能ないくつかのオプション (YJIT、MJIT、TenderJIT) とそれらのインストール方法を説明する前に、JIT コンパイルが Ruby on Rails アプリに適した選択肢である理由を説明します。 まず最初に、JIT コンパイルはどのように機能するのでしょうか? JIT コンパイラの仕組み ジャストインタイム コンパイルは、プログラムの実行中にコンパイルが必要なコンピューター コードを実行する方