2017-5-25
クッキーに関して
バックエンドを Node.js に切り替えようとして Express 使って express-session を使ってセッションを扱おうとしたら Cookie に関するオプションがよくわからなかったのでまとめておく。
サーバー側からの送信方法
HTTP ヘッダーにSet-Cookie
を付与することによって送信出来る。
例えば、以下のような形。
Set-Cookie: hoge_cookie=var; path=/; httponly
パラメータに関しての説明は後述。
クライアント側での取得方法
サーバーから送られた Cookie のデータは、document.cookie
に保存されるので、単純に以下のコードで取得することが可能。
var cookie = document.cookie;
ただ、これで取得できるのはオブジェクトではなく、fuga_cookie=hoge
のような単純な文字列なので扱いづらい。cookie.js
等のライブラリを使用したほうが良い。
Set-Cookie の各種パラメータ
NAME=VALUE
Cookie の本体、キーバリュー形式で値をクライアントに保管する。終わり。
expires
Set-Cookie: fuga=hoge; expires=Tue, 19 Jan 2038 03:14:07 GMT
みたいな感じで使用する。expires に指定された日付まで Cookie が有効、超えると無効になる。
cache-control でも見たなこれ。
max-age
Set-Cookie: fuga=hoge; max-age=3600
みたいな感じで使用する。max-age に指定された秒数間 Cookie が有効。
expires と同時に指定された場合は max-age が有効になる。
cache-control でも(ry
domain
Set-Cookie: name=value; domain=example.com
Cookie が有効なドメインを指定する。現在のホストと domain で指定された物が後方一致すればその Cookie は有効になる。
なお、domain には現在のホストと全く関係の無いものを設定しても無効になる。
例えば、http://www.sd-milieu.net
にアクセスがあってdomain=www.sd-milieu.net
なら有効だが、domain=google.com
とかは無効。そりゃ他人のドメインの Cookie を操作できちゃうので当然っちゃ当然。
じゃあどんな時に使うのかって言うとhttp://www.sd-milieu.net
にアクセスがあった際にdomain=sd-milieu.net
として(これは有効)、http://blog.sd-milieu.net
でも Cookie を有効にしたりとかしたい場合に使う。同一ホスト間で Cookie をやり取りしたい場合に使う。多分。(試してない)
path
Set-Cookie: name=value; path=/
Cookie が有効なパスを指定。現在のパスと path で指定された物が前方一致すれば有効になる。
例えばpath=/blog
の場合、アクセスがあった URL がhttp://www.sd-milieu.net/blog/
なら Cookie が有効だが、http://www.sd-milieu.net/about/
なら無効になる。パスが違うから。
secure
このオプションが設定されている Cookie は、https 通信の時のみ送信される。
セッションを利用する場合は、セッションを利用するページは全て https にしておきこの secure オプションをセッション ID に付与しておかないと、http 通信をしているページでもセッション ID が通信に平文で乗ってしまって MITM 攻撃でセッションハイジャックされるよ、って感じなのかなと。
httponly
このオプションが付与された Cookie は、JavaScript から参照できなくなる。
XSS 脆弱性があったとしても、このオプションをセッション ID に付与しておけばセッションハイジャックを防ぐことが出来る。セッション ID には付けよう。
検証のために書いた Node.js のコード
var http = require("http");
var url = require("url");
var path = require("path");
var fs = require("fs");
var server = http.createServer();
server.on('request', function(req, res) {
var uri = url.parse(req.url).pathname;
var filename = path.join(process.cwd(), uri);
fs.exists(filename, function(exists){
if (!exists) {
console.log('file or directory not found');
return;
}
if (fs.statSync(filename).isDirectory()) {
filename += '/index.html';
}
fs.readFile(filename, "binary", function(err, file) {
if (err) {
console.log('file can\'t read.');
return;
}
var extname = path.extname(filename);
var header = {
"Set-Cookie": ["hoge_cookie=var; path=/; httponly", "fuga_cookie=test;"]
};
res.writeHead(200, header);
res.write(file, "binary");
res.end();
});
});
});
server.listen(8000);