少し間が空いてしまいましたが、久々にbottleについてまとめていきます。今回はCookieあたりを見ていきます。
Cookie(クッキー)
cookie はクライアント側のブラウザの機能で、サーバから送られてきたデータについてテキスト形式で保持されています。また、同一サーバへのアクセス時はブラウザが保持しているcookie をサーバ側に送信します。
利用用途は様々ですが、通常は初回アクセス時に発行したIDをcookie に保存させることで、同一ブラウザからのアクセスを識別することに用いられます。
この動作は、会員ページのログインやEC2サイトに於ける買い物かご等の実現に必要不可欠です。
bottleでとりあえず使ってみる
bottleでcookie を使う場合はresponse.set_cookie
とrequest.get_cookie
を使用します。以下はマニュアルと同じサンプルにコメントを追加したものです。
# coding:utf-8 from bottle import ( run, route, request, response ) @route('/hello') def hello_again(): # cookieにvistedというKeyがあるかをチェック if request.get_cookie("visited"): return "Welcome back! Nice to see you again" else: # cookieに該当Keyがない(初回のアクセス時)はvisted:yes をcookieに書き込む response.set_cookie("visited", "yes") return "Hello there! Nice to meet you" if __name__ == "__main__": # テスト用のサーバをlocalhost:8080で起動する # reloader=Trueにより、ソースを書き換えると自動的に再起動される run(host='localhost', port=8080, reloader=True, debug=True)
これをcookie.py
として保存し、python cookie.py
として起動するとhttp://localhost:8080/hello
への初回アクセス時はHello there! Nice to meet you
と表示され、2度め以降はWelcome back! Nice to see you again
が表示されます。
response
はサーバがブラウザへ戻す情報、request
はブラウザからサーバが受け取る情報です。cookieはサーバがブラウザに保存を命令するのでresponse.set_cookie
であり、ブラウザはサーバへアクセス時に送信するためrequest.get_cookie
で扱います。
cookieのオプション
cookieを保存させる場合は以下のオプションを追加することが可能です。
オプション | 意味 | デフォルト |
---|---|---|
max_age | クッキーの残存時間の秒数 | None |
expires | クッキーの有効期限のUnixタイムスタンプ | None |
domain | クッキーを読み取れるドメイン名 | current domain |
path | クッキーが送信されるURLパス | / |
secure | HTTPSでのみクッキーが使用される | off |
httponly | クライアントサイドのJSでクッキーを読み取れないようにする | off |
same_site | クロスサイトリクエストと共にクッキーを送信しない | None |
たとえば、max_age=10
を指定してみます。
@route('/max_age') def max_age_test(): # cookieにmax_age_testというKeyがあるかをチェック if request.get_cookie("max_age_test"): return "Welcome back! Nice to see you again" else: # cookieに該当Keyがない(初回のアクセス時)はmax_age_test:yes を10秒期限でcookieに書き込む response.set_cookie("max_age_test", "yes", max_age=10) return "Hello there! Nice to meet you"
この場合、2度めのアクセスでは同じようにWelcome back! Nice to see you again
が表示されますが、初回アクセスから10秒経過するとcookie が削除され、再びHello there! Nice to meet you
が表示されます。
Signed Cookiesについて
cookieはクライアントのブラウザ内に格納されているため、悪意のあるクライアントによって容易に偽造されます。例えば以下のコードを見ていきます。
@route('/visit_count') def visit_count(): """ cookie に訪問回数を格納して表示するサイト :return: """ cookie_name = "visit_count" visited_count = 0 if not request.get_cookie(cookie_name): response.set_cookie(cookie_name, '1') visited_count = 1 else: visited_count = int(request.get_cookie(cookie_name)) # Secret key required for non-string cookies.'というエラーになるので一旦strにしている response.set_cookie(cookie_name, str(visited_count + 1)) return "あなたは {} 回目のアクセスです。".format(visited_count)
アクセスすると、これまでのアクセス回数が表示されます。
あなたは 4 回目のアクセスです。
しかし、この値はブラウザ側に保存されているため例えばChromeのデベロッパーツール等で簡単に書き換えることができます。
この状態でアクセスすると
あなたは 1000 回目のアクセスです。
というように値を書き換えられてしまっています。これを避けるには以下のように書き換えます。
@route('/visit_count') def visit_count(): """ cookie に訪問回数を格納して表示するサイト :return: """ cookie_name = "visit_count" visited_count = 0 if not request.get_cookie(cookie_name, secret='my secret cookie'): # secret を指定 response.set_cookie(cookie_name, '1', secret='my secret cookie') visited_count = 1 else: visited_count = int(request.get_cookie(cookie_name, secret='my secret cookie')) # secret を指定 response.set_cookie(cookie_name, str(visited_count + 1), secret='my secret cookie') return "あなたは {} 回目のアクセスです。".format(visited_count)
ほとんど内容はかわりませんがresponse.set_cookie(cookie_name, '1', secret='my secret cookie')
のようにcookie 生成時にmy secret cookie
を元にcookie が暗号かされてクライアント側に保存されます。
そのためクライアント側では意図した値に書き換えられないようになります。なお、set_cookie
でstr
以外を渡した場合、bootleは自動でpickle
をしますが、その際はsecret
が必須になっています。未指定の場合はSecret key required for non-string cookies.
が発生しますのでそのような値を入れる場合はsecret
を必ず指定するようにします。
def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **options): : : elif not isinstance(value, basestring): raise TypeError('Secret key required for non-string cookies.')
まとめ
cookie の基本的な使い方をまとめました。直接cookie を操作するケースというのはあまりないかもしれませんがbottleでシンプルなアプリケーションを作る場合には必要になることもあるので、どなたかの一助になれば幸いです。