メインコンテンツまでスキップ

Cookie と認証(発展)

HTTP ヘッダ

HTTP におけるリクエストとレスポンスには、ヘッダと呼ばれる Key-Value 型のデータ構造が毎回付加されています。Chrome の開発者ツールでの Network タブで確認できるので、確認してみましょう。

HTTP ヘッダ

レスポンスヘッダに set-cookie ヘッダを含めることにより、次回以降のリクエストで、クライアントはそのデータをリクエストヘッダの cookie ヘッダに入れて毎回送信します。この性質を利用することで、HTTP サーバーはクライアント毎に異なるサービスを提供できるようになります。

Cookie 自体も Key-Value のデータ構造となっているので、set-cookie ヘッダを複数回送信することにより、複数の cookie を 1 つのレスポンスで送信することができます。例として、Yahoo! Japan のウェブサイト にアクセスした際に、Yahoo! Japan が送信する Cookie の中身を覗いてみましょう。

Yahoo! Japan の Cookie

このレスポンスヘッダを受けて、ブラウザは次のような Cookie を保存します。

保存された Cookie

ブラウザを更新することで、設定された Cookie が確かにリクエストヘッダの中に含まれて送信されていることがわかります。

リクエストに付加された Cookie

Express を用いてレスポンスヘッダに Cookie を付加するには、express.Response#cookie メソッドを利用します。また、クライアントからのリクエストの cookie ヘッダを解析するためには、cookie-parser パッケージを利用します。

npm install cookie-parser

を実行して、パッケージをインストールしましょう。Web サーバーのプログラムは次のようになります。

main.mjs
import express from "express";
import cookieParser from "cookie-parser";

const app = express();
app.use(cookieParser());

app.get("/", (request, response) => {
// Cookie の値は文字列なので数値に変換が必要
const count = parseInt(request.cookies.count) || 0;
const newCount = count + 1;
// 変更後の値をレスポンスヘッダに乗せる
response.cookie("count", newCount.toString());
response.send(`${newCount}回目のアクセスですね。`);
});

app.listen(3000);

express.Request#cookies プロパティ には、ブラウザから送信されていた Cookie がオブジェクト形式で保存されています。ブラウザで表示させると、更新ボタンが押されるたびに数値が増えていることがわかります。

プログラムの流れを整理すると、次の図のようになります。

プログラムの流れ

課題

  • Chrome の開発者ツールを用いて、リクエストヘッダとレスポンスヘッダの内容を確認してみましょう。
  • シークレットモードでページを開くと値はどうなるでしょうか。

ユーザー名とパスワードを用いて認証するタイプのアプリケーションを考えてみましょう。ユーザー名とパスワードを、そのまま Cookie に入れてしまうと、データが悪意のある第三者に奪われてしまうリスクが高まります。

このため、ログインが成功したタイミングで、クライアントに対してランダムな ID を発行し、Cookie に保存させておくことが一般的です(セッション ID と呼ばれる)。次回以降のアクセスでは、このセッション ID を用いて認証を行います。このフローを図にすると、次のようになります。

セッション

課題

ユーザーが自分のユーザー名とパスワードでログインし、プロフィールを表示できるウェブアプリケーションを作成してみましょう。

schema.prisma は次の通りとします。

schema.prisma
model User {
id Int @id @default(autoincrement())
username String @unique
password String
}

model Session {
id String @id // 一意でランダムなID
userId Int // User の ID
}