Goで初めてWebアプリケーションを作ってみる part8 [JWT, Redis, Middleware]

[実装する機能]
- 登録済みユーザー情報を使ってアクセストークンを発行するログイン機能
- ログインユーザーのみにAPIの利用を許可する機能
- アクセストークンに含まれる識別情報を利用する機能
- 管理者権限のユーザーのみがアクセスできる機能
[やること]
- Redisを使ったキャッシュ
- JWT(JSON Web Token)を使ったアクセストークンの取り扱い
- 「go:embed」を使ったファイル埋め込み
- ミドルウェアパターンを利用したHTTPヘッダー情報の透過的な伝達方法
- テストの事前データ作成を効率化するためのフィクスチャ関数の活用

Redis
- Redisは、NoSQLデーターベース(SQLを使用しないデーターベース)の一つであり、キーバリュー型のインメモリデータベースである。
- 「インメモリデータベースのイン」は、「メインメモリ内にデータを格納する」という意味である。
- Redisのデータはすべてメモリ内に保存されるため、高速なデータの読み書きが可能
- メモリ内のデータの読み書きって早いっていうよね。だからRedisって早いのか。
- Redisはインメモリデータベースであるため、サーバプロセスが終了するとメモリ上のデータも消えてしまう。もしそのようなことが起こった場合でも大丈夫なように、Redisはサーバプロセス終了後もデータをファイルに保持してくれる。(おそらく)
- これはPCでも電源を消すとメモリ上のデータが消えるので、わかりやすい。

リクエストを処理するAPIサーバーがアクセストークンを払い出した同じAPIサーバーだという思わない方が良い
-
クラウドネイティブなアプリケーションだと、スケールアウトして、アプリケーションの仮想サーバーやコンテナが複数台稼働している可能性や、そもそも数分前と同じ仮想サーバーが稼働していない可能性がある
-
リクエストを処理するAPIサーバーがアクセストークンを払い出した同じAPIサーバーだと思わない方が良い。仮想サーバーやコンテナはステートレスである必要があるため、一時的なデータでもRedisなどを利用してミドルウェア上で保管して共有する必要がある
-
スケールアウトとは、システムを構成するサーバーの台数を増やすこと。システムの処理能力を高めることを言う
-
Middlewareとは、リクエストレスポンスした際に実行されるアプリケーション処理の前後で何らかの処理を行ったりするものである。例えば、セッション管理したりユーザ認証。

そうか、サーバー上のメモリで値を管理しようとすると、Webサーバーのプロセスが落ちたら、メモリ上のデータも消えるな。おそらく。コードで試してないから何とも言えないけど。

アクセストークン
- アクセストークン(Access Tokenとは、WebアプリケーションやAPIにおいて、認証と認可のために使用されるトークンである。
- ユーザーがアプリケーションやサービスに対して正当なアクセス権を持つことを示すために利用されます。
- アクセストークンは、ユーザーが認証プロセスを経て正常に認証された後にサービスから発行されます。一般的に、アクセストークンは一定期間有効であり、その期間内はアプリケーションやAPIへのアクセス許可を与えます。
- アクセストークンはセキュアな方法でクライアントに送信され、クライアントはそれを使用してサーバーに対してリクエストを行います。
WebサービスとWebアプリケーションの違い
- Webサービスとは、ネットワーク上で提供されるソフトウェアサービスである。主な目的は、クライアントとサーバー間でのデータの交換や機能の提供を行うことである。
- Webアプリケーションは、Web上で実行されるアプリケーションソフトウェアである。

opensslコマンド
- opensslコマンドとは、オープンソースの暗号化ツールキットであるOpenSSLを利用して、暗号化、復号化、証明書の生成や管理など、さまざまな暗号化関連の操作を実行するためのコマンドラインツールである。
- opensslコマンドは、コマンドラインからさまざまな暗号化タスクを実行するために使用される。一般的な操作としては、以下のようなものがある:
- 公開鍵と秘密鍵の生成や管理
- サーバー証明書やクライアント証明書の生成や署名
- 暗号化されたファイルの作成や解読
- SSL/TLS通信の設定やテスト

RSA暗号方式
- RSA暗号方式とは、公開鍵と秘密鍵のペアを使用して、データを暗号化・復号化したり、署名や検証する方法のこと。
- 非常に大きな素数の積を利用して計算が行われるので、秘密鍵を知らない人は暗号化されたデータを復号化することは難しいそう

RSA鍵ペアの生成
以下のコマンドで、RSA暗号方式の鍵のペアを生成している
openssl genrsa 4096 > secret.pem
- このコマンドは、4096ビットのRSA秘密鍵を生成し、それをsecret.pemというファイルに書き出す。
- 秘密鍵は非公開であり、暗号化や署名などの操作に使用される。
openssl rsa -pubout < secret.pem > public.pem
- このコマンドは、secret.pemに格納された秘密鍵から公開鍵を抽出し、public.pemというファイルに書き出す。公開鍵は他の人と共有され、暗号化や署名の検証などの操作に使用される。

JWT(JSON Web Token)
-
ログインが成功した時に発行するアクセストークンとして、JWTを利用する
- JWTの生成には、lestrrat-go/jwxパッケージと、google/uuidパッケージを利用する
-
JWT(ジョット)とは、JSONをベースとしたアクセストークンのためのオープン標準(RFC7519)である。
-
このJSONはBase64URLエンコードされている
-JWTはヘッダー、ペイロード、署名の3つの要素をピリオドで区切った文字列で構成される。 -
ヘッダーには、署名生成に使用するアルゴリズムを格納する
-
ペイロードには認証情報などのクレーム(トークンに含めたい情報)を格納する。ユーザーIDやロール、有効期限などが含まれることがある。つまり、JWTには、認証情報も含まれていることがわかる。
-
署名は、トークンを検証するために使う。署名はヘッダーとペイロードをBase64URLエンコードしてドットで結合したものに対して、ヘッダで指定した暗号方式の秘密鍵を用いて生成したものである。
-
JWTは自己完結型である。全ての情報がトークンに含まれており、トークンを受け取ったサーバーは別途データーベースやセッションストアにアクセスする必要がない。そのため、スケーラビリティや分散システムに適している
-
JWTは一度生成されると変更できない。ペイロードの内容や有効期限などの情報は署名されているため、他の人が改ざんすることはできない。また、署名に使用される秘密鍵を持っているサーバーだけがトークンを検証できる。秘密鍵を持っているユーザーだけが、データの改ざんを行うことができる。

JWTが有効かどうかをチェックするフロー
JWTの有効性は、以下の手順でチェックされます:
-
トークンのパース: JWTを受け取ったサーバーは、まずJWTをヘッダ、ペイロード、署名の3つの部分に分割します。
-
ヘッダの検証: ヘッダはBase64URLエンコードされたJSON形式の文字列です。サーバーは、ヘッダをデコードし、使用されている署名アルゴリズムやトークンのタイプを確認します。また、信頼できる署名アルゴリズムが指定されていることを確認します。
-
署名の検証: サーバーは、ヘッダとペイロードの内容を元に署名を検証します。署名検証には、事前に共有された秘密鍵(HMAC)または公開鍵(RSA)が使用されます。署名の検証に成功すると、トークンが改ざんされていないことが確認されます。
-
有効期限のチェック: ペイロードには、トークンの有効期限(exp)が含まれています。サーバーは、現在の日時と比較してトークンの有効性を判断します。有効期限が切れている場合、トークンは無効と見なされます。
-
その他のクレームの検証: ペイロードには、アクセス制御や認証に関するさまざまなクレーム(情報)が含まれています。サーバーは、必要なクレームが存在し、値が正しいかどうかを検証します。例えば、ユーザーIDやロールの確認などが含まれます。
-
トークンの利用: トークンが上記の検証手順をすべてパスした場合、サーバーはトークンを信頼し、リクエストを承認します。これにより、ユーザーの認証や認可が実現されます。
上記の手順に従って、JWTの有効性をチェックすることで、トークンの完全性と信頼性を確保することができます。サーバーは、トークンを受け取った時点でこれらのチェックを実施し、必要な処理を行います。これにより、セキュリティやアクセス制御の要件を満たすことができます。

おそらく、もともとあった署名と、トークンとヘッダーに対して秘密鍵を用いて生成した署名が一致していたら、トークンが改ざんされていないってこと。

アクセストークンを使った認証とセッション・クッキーを使った認証のメリット・デメリット
アクセストークンを使った認証とセッション・クッキーを使った認証のメリットとデメリットは次のようになります。
アクセストークンを使った認証のメリット:
- 独立性: アクセストークンは独立した情報であり、認証サーバーとの間でやり取りされます。サーバーアプリケーションは、トークンの検証に必要な情報を持っており、認証サーバーに対するリクエストが必要ありません。このため、スケーラビリティや分散環境での認証を容易にします。
- レスポンスの軽量化: アクセストークンを使った認証では、認証情報がトークン自体に含まれているため、リクエストごとに認証情報をサーバーに送信する必要がありません。これにより、レスポンスのサイズを小さくし、ネットワークトラフィックを軽減できます。
- クロスプラットフォーム: アクセストークンは通常、プラットフォームや技術に依存しない形式であり、Webアプリケーション、モバイルアプリケーション、APIなど、さまざまなクライアントから使用できます。
アクセストークンを使った認証のデメリット:
- トークン管理: アクセストークンは有効期限がありますが、その管理と更新を適切に行う必要があります。トークンの無効化やリフレッシュトークンの処理など、追加の機構が必要となる場合があります。
- トークンの漏洩リスク: アクセストークンはセキュリティ上の重要な情報です。万一、トークンが漏洩した場合、不正なアクセスやなりすまし攻撃のリスクが発生します。トークンを適切に保護するために、HTTPSの使用やトークンの暗号化が重要です。
セッション・クッキーを使った認証のメリット:
- シームレスなユーザーエクスペリエンス: セッション・クッキーを使った認証では、ユーザーのブラウザにクッキーが保存され、自動的に送信されるため、ユーザーは再認証の手間をかけることなくシームレスにアプリケーションを利用できます。
- セッション管理: セッション・クッキーを使った認証では、セッションの管理がサーバーサイドで行われます。サーバーはセッションIDを使用してユーザーのセッションを追跡し、セッションの状態や有効期限を管理します。
セッション・クッキーを使った認証のデメリット:
- ステートフル: セッション・クッキーを使った認証では、サーバーサイドでセッションの状態を保持する必要があります。これにより、サーバー側の負荷が増加する可能性があります。また、クラスタリングやスケーラビリティの課題が発生する場合もあります。
- プラットフォーム依存: セッション・クッキーは主にWebアプリケーションに使用され、クッキーの利用にはブラウザのサポートが必要です。モバイルアプリケーションやAPIなどの他のプラットフォームでは、セッション・クッキーを利用することが難しい場合があります。
どちらの認証方法が適切かは、アプリケーションのニーズやセキュリティ要件、クライアントの種類などによって異なります。セキ

go:embedディレクティブ
- go:embedディレクティブを使うと、実行バイナリにファイルを埋め込むことができる。
- 埋め込むことで、実行バイナリの他にコードから読み込むファイルも実行環境に用意する必要がなくなる

秘密鍵の注意点
- 当たり前だが、秘密鍵をリポジトリに含めてはダメ。
- 開発環境と本番環境で利用する鍵ファイルは異なるものにした方が良い。

フィクスチャ
フィクスチャとは、テストコードで必要となるダミーの事前データのこと。

JWTはクライアントからのHTTPリクエストのAuthroizationリクエストヘッダーに付与されているものとする

JWTの jti (JWT ID)
JWTの jti (JWT ID) は、JWTの一意の識別子を表すクレーム(Claim)の1つです。

ミドルウェアを使うことで、ログイン認証している人しかエンドポイントにアクセスできなかったり、アドミン権限の人しかエンドポイントにアクセスできないとか、やることができた。

CSRFとSQLインジェクションの脆弱性のあるサイトを実施に作ってみたい
やっぱハンズオンでやらないと身に付かん。

なんでjwtでRedisも使うねんって思って調べた結論
Redisを使う理由は、ログインしているユーザーを管理するため。jwtだけだと、ログアウトしたとしても、トークンが有効期限過ぎていなかったら、保護されているエンドポイントにアクセスできちゃう。ログアウト時にjwtは手動で無効にできない

SQLインジェクション
SQLインジェクションは、開発者が想定しないSQLが実行されてしまうこと。
SQLに変数が使われていることに問題がある。プレースホルダーを使うことで、仮にSQL文が埋め込まれたとしてもエスケープ処理してくれる