Cookieを学んでみよう!
はじめに
実務ではあまりCookieを意識したことがなく個人勉強がてら、Cookieについて学習した内容を記載していこうと思います。
そもそもCookieとは?
Cookieとは、クライアント側のブラウザに保存されるデータのことで、以下の特徴があることがわかりました。
-
CookieはサーバーからのHTTPレスポンスのSet-Cookieヘッダーを使用してWebブラウザに送られ、保存される
-
保存されたCookieはクライアントからサーバーにリクエストするたびに自動送信される
-
有効期限が設定されていないCookieは、Webブラウザが閉じられると一緒に削除される
-
有効期限が設定されたCookieは、期限が過ぎるまではWebブラウザを閉じても削除されない
→ 上記4つの特徴は実際に操作してみて動作確認を行ってみたいですね。 -
CookieはWebブラウザの識別にも使われるので、他人に盗まれてなりすましをされる可能性がある
→ ここがよく聞くセキュリティのよくないところですかね。
Cookieの種類
どうやらCookieには2種類の種類が存在する模様
-
Session Cookie(ブラウザを閉じるまで有効な一時的なクッキー)
→これは、上記特徴にも記載した、有効期限が設定されていないCookieのことですね。
セキュリティの観点ではこちらの方がいいかも。。
ログインしているかしていないかの識別も行うことができそう! -
Persistant Cookie(有効期限日まで永続するクッキー)
→これは、上記特徴の"有効期限が設定されたCookieは、期限が過ぎるまではWebブラウザを閉じても削除されない"にあたりますね。
よくログインしてなくてもすぐにマイページとかが開くことができるのはこれのおかげなんですね。
ただ、セキュリティの観点から考えると、このCookie情報が抜き取られたら終わりですね。。
Sessionとは?
Cookieを調べていくと"Session"って言葉も一緒に見かけることが多いいので、Sessionについても調べていこうと思います。
Sessionとは、一連の処理が始まり終わりまでを表す概念のことのよう。
具体的には、サーバー側で保存する情報のことで通常はログイン状態の管理などに使用されるらしい。
Sessionの特徴
- HTTPのセッションは「セッションID」を使って同一のセッションを管理する
- セッションIDはCookieに保存し、リクエストヘッダに格納してPOSTメソッドで送受信したり、URLに埋め込んでGETメソッドで送受信したりする
- CookieとSessionを利用することで、ステートレスをステートフルにできる
- セッションはWebブラウザを閉じるまで保存される。
- リンクなどでページを移動しても内容を保持することができる
- セッションIDのやりとりはCookieを用いる方法が一般的だが、Cookieが使えるかどうかはブラウザによるため、GETパラメータを用いてやりとりするなど他の方法をとるケースもある(セキュリティ上は非推奨)
- モダンブラウザでは、同一PC上では、必ず同じセッションとなる(IE7以前では、新規ウィンドウを開かない限り異なるセッションとなる)
Cookieの保存について
Cookieはブラウザの設定によっては保存されないことがあるので、Cookieは必ず保存されるものと思ってはいけない。
(実装する際は気をつけなくてはいけないところ)
Cookieの実装について
JavaScritpを使用することで、Cookieのデータ操作を行うことができる。
Sessionの実装について
各バックエンド言語でライブラリが用意されており、そのライブラリを使用することで実装することが可能。
CookieとSessionを使ってできること
今まで調べたことから、以下のことができるのではないかと思いました。
- ブラウザを閉じてしまった際のログイン情報の保持
- ユーザーの操作履歴を保存・分析。
- ユーザーが意識しなくても上記内容で、情報のやりとり
実際に使ってみる
今回はexpressを使用してcookieとsessionを試してみようと思います。
ライブラリは"express-session"と"cookie-session"が存在するみたいで
今回使用するライブラリは"express-session"を使っていきます。
またsession情報を保存するには、"redis"を使用していこうと思います。
const express = require("express");
const session = require("express-session");
const cookieParser = require("cookie-parser");
const { createClient } = require("redis");
let RedisStore = require("connect-redis")(session);
let redisClient = createClient({ legacyMode: true });
redisClient.connect().catch(console.error);
const app = express();
app.use(cookieParser())
app.use(
session({
// セッションIDの名前
name: "testSessionId",
// Cookieの署名に利用するキー
// クライアントには[セッションID].[secretを利用した署名]の値が返される
secret: "1hajhrhu46bul5",
// リクエスト中にセッションデータが書き換えられなかったとしてもストレージに保存するか。
resave: false,
// 初期化されていない(何も入っていない)セッションに値を入れてストレージにほぞんするか
saveUninitialized: false,
// セッションデータを保管するストレージを指定する。
store: new RedisStore({ client: redisClient }),
// Cookieに関するオプション。
cookie: {
secure: false,
httpOnly: false,
},
})
);
// session情報の確認
function checkSession(req){
if (!req.session.views) {
console.log("sessionデータの初期化")
req.session.views = {};
}
console.log("sessionIDの確認",req.session.id)
console.log("sessionデータの確認",req.session.views)
}
// cookieの情報確認
function checkCookie(req){
console.log("Cooikeの受信内容の確認",req.cookies)
}
// 各処理を実行する前に実施される共通処理
app.use((req, res, next) => {
checkSession(req)
checkCookie(req)
next();
});
function sessionSave(req){
const inputValid = "sessionValue" in req.query && req.query.sessionValue !== "" && req.query.sessionValue !== null
if(inputValid) {
req.session.views[req.session.id] = req.query.sessionValue
req.session.save(function( err ){
if( err )console.log("エラー発生")
})
console.log("Sessionデータを保存しました")
return;
}
console.log("リクエストパラメーターは存在しませんでした。")
return;
}
app.get("/", function (req, res, next) {
res.sendFile(__dirname + '/index.html');
});
app.get("/session", function (req, res, next) {
sessionSave(req)
res.send(req.session.views[req.session.id] )
});
app.listen(3000, () => {
console.log("Application available!");
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" id="input-text">
<button onclick="cookieRequest()">送信ボタン</button>
<script>
function cookieRequest(){
const inputText = document.getElementById("input-text")
console.log("入力内容の確認",inputText.value)
const params = {sessionValue : inputText.value};
const query = new URLSearchParams(params);
fetchRequest(query)
}
async function fetchRequest(query){
try{
await fetch(`http://localhost:3000/session?${query}`)
}catch(e){
console.log("エラー発生",e)
}
}
</script>
</body>
</html>
動きとしては..
- アクセスされるとSessionIDをブラウザのCookieに保存。
- 入力欄に任意の文字を入力し、送信ボタンをクリックすることで、サーバー側にsessionに保存するデータが送信される。
- サーバー側では、sessionIDを元にリクエスト内容をsessionに保存する。
と、こんな感じに動作します。
気になったので、異なるブラウザからアクセスされた場合、きちんとデータを分けて管理されているか確認したところ分けて管理されていましたね。(さすがライブラリ)
まとめ
記載内容をまとめると、Cookieはブラウザに保存されるデータのことで、Sessionはサーバー側で保存されるデータのことを指す。
使い方としては、ログインした際にログインIDをSessionに保存し、Cookieにも同じログインIDを保存する。
こうすることで、再度ログインしようとした際にCookieのログインIDを使用すればいちいちログイン情報を入力しなくてもログインすることができる。
ただセキュリティのリスクがあるので、実務で実装する際はセキュリティ面を考慮した設計を行う必要がある。
(話は変わるが、AWSではSessionを管理するElastiCaCheとゆうインメモリサービスがあるみたいです)
■参考サイト
-
Cookieのセキュリティについて
https://expressjs.com/en/advanced/best-practice-security.html#use-cookies-securely -
Sessionのセキュリティについて
https://viblo.asia/p/nodejs-expressでの安全なセッション管理-yZjJYxY64OE -
expressでのCookieの使い方
https://qiita.com/yuta-katayama-23/items/068900da9271c4d1be1c -
Fetch APIの使い方
https://jsprimer.net/use-case/ajaxapp/http/ -
expressでのSessioの使い方
https://www.investor-daiki.com/express-session -
Cookieについて
https://qiita.com/EasyCoder/items/8ce7dfd75d05079be9d7
Discussion