私はつい最近まで、JavaScriptが送ったHTTPリクエスト(AjaxやFetch)に対するレスポンスでは、クッキーをセットできないと思い込んでいました。そのため、それ相当のことをするには、レスポンスでクッキーに入れたい値を受け取り、document.cookie = newCookie;
のようにJavaScriptでセットしないといけないものだと思い込んでいました。そのためHttpOnlyディレクティブを使うことができないとも。
これは間違いで、JavaScriptからのHTTPリクエストに対するレスポンスがSet-Cookieヘッダーを持っていれば、自動的にブラウザにそれが保存されます。喜ばしいことに、このときHttpOnlyディレクティブを有効にすることもできます。
ただしJavaScriptでCookieをセットするときに、HttpOnlyディレクティブを使えないのは真です。HttpOnlyディレクティブつきのCookieはJavaScriptから読めないだけではなく書くこともできません。
クッキーの付与と表示を行うPythonスクリプトを用意して確かめる
これをcookie.pyとします。python3 cookie.py
で起動できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | from typing import ( List, TYPE_CHECKING, ) import wsgiref.simple_server if TYPE_CHECKING: import wsgiref.types def return_html(start_response: 'wsgiref.types.StartResponse') -> List[bytes]: start_response('200 OK', [('Content-type', 'text/html')]) return [ b"""<html> <head> <script> fetch('/', { method: 'POST', credentials: 'same-origin' }); </script> </head> </html> """, ] def give_cookie(start_response: 'wsgiref.types.StartResponse') -> List[bytes]: start_response('200 OK', [ ('Content-type', 'text/plain'), ('Set-Cookie', 'key=value; Path=/; HttpOnly; SameSite=strict'), ]) return [] def app( environ: 'wsgiref.types.WSGIEnvironment', start_response: 'wsgiref.types.StartResponse', ) -> List[bytes]: print('Cookieの値', environ.get('HTTP_COOKIE')) for method, handler in [('GET', return_html), ('POST', give_cookie)]: if environ['REQUEST_METHOD'] == method: return handler(start_response) raise NotImplementedError() if __name__ == '__main__': with wsgiref.simple_server.make_server('', 8080, app) as httpd: httpd.serve_forever() |
起動すると8080番ポートで待ち受けます。リクエストを受けるとまずリクエストで送られてきたCookieを出力します。そしてそのリクエストがGETのときには10行目の関数が呼ばれJavaScriptを返し、POSTのときは24行目の関数が呼ばれクッキーをセットするヘッダーを返しています。GETのときに返されるJavaScriptは同サーバにPOSTリクエストを送信します。
ブラウザでブラウザでhttp://localhost:8080
にアクセスすると次のような流れになります。
- ブラウザがGETリクエストを送信する。
- Cookieの値はNone
- JavaScriptを含んだHTMLが返される。
- JavaScriptがPostリクエストを送信する。
- Cookieの値はNone
- クッキーがセットされる
ここでブラウザに再読込させます。
- ブラウザがGETリクエストを送信する。
- Cookieの値 key=value
- JavaScriptを含んだHTMLが返される。
- JavaScriptがPostリクエストを送信する。
- Cookieの値 key=value
- クッキーがセットされる
HttpOnlyディレクティブ
クッキーにこれが指定されていると、JavaScriptがそのクッキーに一切触れられなくなります。そのためXSRFに対していくらか効果があります。有効にできることなら有効にしたいものです。今回私はこれを有効にしたいと思い、試してみたらできたという話です。