CookieとセッションとsessionStorageとlocalStorage
- localStorage と sessionStorage はローカル ストレージ (リクエストの送信時には送信されません) であり、ドメインを越えることはできません。
- localStorage はブラウザを閉じた後はクリアされないため、手動でクリアする必要があります。
- sessionStorage はブラウザを閉じると破棄されます。
- http はステートレスであり、Cookie を通じて状態を作成できます (ブラウザとサーバーの両方が Cookie を設定できます)。デフォルトでは、Cookie はリクエストが送信されるたびに送信されます。
- Cookie は安全ではありません。データはクライアント側に保存されるため、改ざんされる可能性があります。機密情報は保存できません。
- セッションは Cookie に基づいており、Cookie メカニズムを通じてサーバー側のストレージ スペースを作成します。
- セッションは Cookie より安全です。サービスを再起動するたびに失われます。セッションを保存するには Redis を使用できます。
サーバー経由で Cookie を書き込む
- ドメイン ドメイン名の設定。デフォルトは現在のドメイン名です。設定されている場合
Domain=mozilla.org
、Cookie はサブドメイン名にも含まれます (例developer.mozilla.org
: )。 - パス path (/ は任意のパスを表します) は、書き込み時の Cookie のパスを制限します。
- exipres 絶対時間/最大経過時間相対時間
- httpOnly クライアントが Cookie を操作できるかどうか
例:
const http = require("http");
const querystring = require("querystring");
const server = http.createServer(function (req, res) {
// 通过服务端写入 cookie
console.log(req.headers.cookie);
if (req.url === "/read") {
// 读取cookie
res.end(JSON.stringify(querystring.parse(req.headers.cookie, "; ", "=")));
} else if (req.url === "/write") {
// 设置cookie
res.setHeader("Set-Cookie", [
`name=kaimo; domain=.kaimo.com; expires=${
new Date(Date.now() + 10 * 1000).toGMTString()}`,
"age=313; max-age=10; httpOnly"
]);
res.end("write ok");
} else {
res.end("NOT FOUND");
}
});
server.listen(3000);
console.log("Server running at http://127.0.0.1:3000/");
注:C:\Windows\System32\drivers\etc\hosts
ファイルを開いて、カスタムのローカル アクセス ドメイン名を追加できます。
127.0.0.1 a.kaimo.com
127.0.0.1 b.kaimo.com
http://a.kaimo.com:3000/write
次に、それぞれ Cookie の書き込み、Cookie の読み取りにアクセスしhttp://a.kaimo.com:3000/read
、http://b.kaimo.com:3000/read
Cookie の読み取りと書き込みをカプセル化する
const http = require("http");
const querystring = require("querystring");
const server = http.createServer(function (req, res) {
// 读取cookie
req.getCookie = function (key) {
let cookieObj = querystring.parse(req.headers.cookie, ";", "=");
return cookieObj[key];
};
// 设置cookie
let arr = [];
res.setCookie = function (key, value, options = {
}) {
let opts = [];
if (options.domain) {
opts.push(`domain=${
options.domain}`);
}
if (options.path) {
opts.push(`domain=${
options.path}`);
}
if (options.maxAge) {
opts.push(`max-age=${
options.maxAge}`);
}
if (options.httpOnly) {
opts.push(`httpOnly`);
}
arr.push(`${
key}=${
value}; ${
opts.join("; ")}`);
res.setHeader("Set-Cookie", arr);
};
// 通过服务端写入 cookie
console.log(req.headers.cookie);
if (req.url === "/read") {
res.end(req.getCookie("name") || "empty");
} else if (req.url === "/write") {
res.setCookie("name", "kaimo", {
domain: ".kaimo.com",
expires: `${
new Date(Date.now() + 10 * 1000).toGMTString()}`
});
res.setCookie("age", "313", {
maxAge: "10",
httpOnly: true
});
res.end("write ok");
} else {
res.end("NOT FOUND");
}
});
server.listen(3000);
console.log("Server running at http://127.0.0.1:3000/");
Cookieのセキュリティ問題
ブラウザにCookieを設定する際に署名を追加し、データの内容に基づいて独自の署名(MD5)を作成することができますが、この方法は偽造される可能性があります。
もう 1 つの方法は、sha256 ソルティング アルゴリズムを使用して、コンテンツと秘密キーに基づいて署名を計算することです (元に戻すことはできません)。同じ秘密キーを使用した署名の結果は同じです。
暗号学においては、パスワードの任意の固定位置に特定の文字列を挿入し、元のパスワードでハッシュした結果と一致しないようにすることを「ソルティング」と呼びます。
const http = require("http");
const querystring = require("querystring");
const crypto = require("crypto");
// 秘钥
const secret = "kaimo313";
// 为了编码能在网络中安全顺畅传输,这里可以直接把 / + = 三个符号置空
const sign = (value) =>
crypto
.createHmac("sha256", secret)
.update(value)
.digest("base64")
.replace(/\/|\+|\=/g, "");
const server = http.createServer(function (req, res) {
// 读取cookie
req.getCookie = function (key, options) {
let cookieObj = querystring.parse(req.headers.cookie, ";", "=");
if (options.signed) {
let [value, s] = (cookieObj[key] || "").split(".");
let newSign = sign(value);
if (newSign === s) {
// 签名一致,说明这次的内容是没有被改过的
return value;
} else {
// 签名被篡改了,不能使用
return undefined;
}
}
return cookieObj[key];
};
// 设置cookie
let arr = [];
res.setCookie = function (key, value, options = {
}) {
let opts = [];
if (options.domain) {
opts.push(`domain=${
options.domain}`);
}
if (options.path) {
opts.push(`domain=${
options.path}`);
}
if (options.maxAge) {
opts.push(`max-age=${
options.maxAge}`);
}
if (options.httpOnly) {
opts.push(`httpOnly`);
}
if (options.signed) {
value = value + "." + sign(value);
}
arr.push(`${
key}=${
value}; ${
opts.join("; ")}`);
res.setHeader("Set-Cookie", arr);
};
// 通过服务端写入 cookie
console.log(req.headers.cookie);
if (req.url === "/read") {
res.end(
req.getCookie("name", {
signed: true
}) || "empty"
);
} else if (req.url === "/write") {
res.setCookie("name", "kaimo", {
domain: ".kaimo.com",
expires: `${
new Date(Date.now() + 10 * 1000).toGMTString()}`,
signed: true // 加签名
});
res.setCookie("age", "313", {
maxAge: "10",
httpOnly: true
});
res.end("write ok");
} else {
res.end("NOT FOUND");
}
});
server.listen(3000);
console.log("Server running at http://127.0.0.1:3000/");
Cookieの値を変更する