connectのセッションミドルウェアのcookieのsecure属性について
SSLでCookie使う場合はsecure属性つけて非SSLの場合にはCookie送信しないようにするって仕組みがあるわけだけど、connectのsessionもその実装はされてる。
app.use(express.session({ secret: yourSecret, cookie: { secure: true } }));
こんな感じ(コードはexpressだけど)。で、こうするとSSLの場合だけCookieが有効になるはずなんだけど、herokuでやってたらSSLのときも有効にならなくて(Set-Cookieされない)なんぞと思って調べてみた。
現状(v1.8.5)のmiddleware/session.jsの該当のコードはこうなってる。
// only send secure session cookies when there is a secure connection. // proxySecure is a custom attribute to allow for a reverse proxy // to handle SSL connections and to communicate to connect over HTTP that // the incoming connection is secure. var secured = cookie.secure && (req.connection.encrypted || req.connection.proxySecure); if (secured || !cookie.secure) { res.setHeader('Set-Cookie', cookie.serialize(key, req.sessionID)); }
cookie.secureってのがmiddleware設定のオプションで渡した値なのでここではtrueになってる。ので
req.connection.encrypted || req.connection.proxySecure
このどっちかが真じゃないとSet-Cookieされないってことになる。んでこのreq.connection.encryptedってのはapp自体がhttps.Serverで起動してるときになんか値が入るらしい。でもherokuの場合はリバースプロキシがいてそいつからはhttpでリクエストがくるのでアプリ自体はhttp.Serverで立ち上がってる。だからreq.connection.encryptedはundefined。
んでreq.connection.proxySecureだけど、これはコメントに以下のように書いてある。
// proxySecure is a custom attribute to allow for a reverse proxy // to handle SSL connections and to communicate to connect over HTTP that // the incoming connection is secure.
つまりリバースプロキシを介したhttpsのアクセスはreq.connection.proxySecureを設定しとけってことかなと理解した。つまりこうすればいい。たぶん。
app.use(function(req, res, next) { if (req.header('x-forwarded-proto') === 'https') { req.connection.proxySecure = true; } next(); }); app.use(express.session({ secret: yourSecret, cookie: { secure: true } }));
x-forwarded-protoってのがリバースプロキシがアプリにアクセスするときにつけてくれるヘッダ。これで判定できるみたいなので、これを見てhttpsかどうかを判定すればOKっぽい。
ちなみにこれはconnectのv1.8.5の話で、次期バージョンのv2.xを見ると実装が変わっててこうなってる。
var cookie = req.session.cookie , proto = (req.headers['x-forwarded-proto'] || '').toLowerCase() , tls = req.connection.encrypted || (trustProxy && 'https' == proto) , secured = cookie.secure && tls; // browser-session cookies only set-cookie once if (null == cookie.expires && !sessionIsNew) return; // only send secure cookies via https if (cookie.secure && !secured) return debug('not secured'); debug('set %s to %s', key, req.sessionID); res.setHeader('Set-Cookie', cookie.serialize(key, req.sessionID));
x-forwarded-protoが直接書いてあるのがわかる。ので2.xからはさっきみたいのは必要なくなるっぽい。んでtrustProxyってのがoptions.proxyになってるんで、proxyオプションを指定すればよさそう。
app.use(express.session({ secret: yourSecret, proxy: true, cookie: { secure: true } }));
まあこの辺はまだリリースされてないんで変わるかもしれんけど。
てかconnectの2.xとexpressの3.xはもう別のネームスペースで出したほうがいいんじゃないかと思い始めたよ。マル。