セッション管理とログアウト
IDを消す場所、期限を判定する場所、サーバーに残る有効状態の関係を説明します。
説明
セッションIDは、ログイン済みの利用者と後続の要求を結び付ける値です。サーバーはIDに対応する利用者と期限を保存し、要求のたびに有効性を確認します。
匿名状態のIDをログイン時に更新し、古いIDでのアクセスを試します。その後、無操作の期限と発行からの期限を別々に確認し、ログアウトによる失効まで追います。固定のIDは比較用の試料です。運用時は予測困難な乱数で発行します。
IDを消す場所、期限を判定する場所、サーバーに残る有効状態の関係を説明します。
1匿名IDの保存先を調べる
DBには生のIDではなくSHA-256値と状態を保存します。
from sd07 import *
db = database()
issue(db匿名セッションを作る,"anon-public","anonymous",0)
print(db.execute("SELECT length(hash)保存項目を調べる,subject,created,last,active FROM sessions").fetchall())64桁のハッシュ値、匿名の主体、発行・最終利用時刻0が保存されています。クライアントは生IDを持ち、サーバーはハッシュで行を検索します。
2ログイン時にIDを更新する
認証成功後の処理としてIDを更新します。
rotate_session旧IDを失効させ新IDを発行する(db,"anon-public","login-public","alice",10)
print(access(db,"anon-public"旧IDを再利用する,11))
print(access(db,"login-public",11))旧IDはNone、新IDはaliceです。匿名時に第三者が知ったIDを認証後も使い続けないための更新です。
3Cookieの送信・参照条件を確認する
Cookie属性とDBの期限は別の役割です。次はHTTPSで返す設定例を標準パーサーで読みます。
cookie = SimpleCookie()Set-Cookieの属性を解析する
cookie.load公開試料の設定を読む("__Host-sid=login-public; Secure; HttpOnly; Path=/; SameSite=Lax")
print({k:cookie["__Host-sid"][k] for k in ("secure","httponly","path","samesite","domain")})SecureはHTTPS送信、HttpOnlyはスクリプトからの参照制限、Path=/は全パス、Domain省略は発行ホスト限定です。これらの属性だけではDBの有効状態は変わりません。
4無操作期限の直前と境界を試す
最後の利用は11。60秒の無操作期限を設定しています。期限直前の利用は時刻を更新します。
print(access(db,"login-public",70期限前に利用する))
print(db.execute("SELECT last FROM sessions WHERE subject='alice'").fetchone())
print(access(db,"login-public",130更新後の期限ちょうどで試す))70で成功してlastが70へ変わります。130では70+60に達し、Noneです。期限判定はサーバーで行っています。
5利用を続けても発行からの期限は延長しない
別IDを時刻0で発行し、50秒ごとに利用します。
issue(db,"active-public","alice",0)
print([access(db,"active-public",t) for t in (50,100,150,200,250,299,300)無操作期限内で利用を繰り返す])299まではalice、300はNoneです。lastは更新されてもcreated+300の上限は固定され、長時間の継続利用を制限します。
6ログアウトでサーバー側の状態を失効する
新しいIDを発行し、利用後にログアウトします。
issue(db,"logout-public","alice",400)
print(access(db,"logout-public",401))
print(logout(db保存されたセッションを失効させる,"logout-public"))
print(access(db,"logout-public",402残存コピーの再利用を試す))更新行数1でactiveが0となり、同じIDを送ってもNoneです。Cookieの削除だけでは残存コピーの再利用を止められないため、サーバー側も更新します。
7認証済み行の最終状態を確認する
期限切れと明示的な失効は異なる条件として残っています。
print(db.execute("SELECT created,last,active発行・利用・失効の条件を照合する FROM sessions WHERE subject='alice' ORDER BY created").fetchall())期限切れの行はactiveが1でも期限条件で拒否されます。ログアウトした行はactiveが0です。運用では不要な期限切れ行の削除も行います。
まとめ
ログイン時にIDを更新し、旧IDを失効させます。
無操作期限と絶対期限をサーバーで評価します。
ログアウトではCookieだけでなく保存状態を失効させます。
操作履歴と解説を振り返れます。
Python 3.15 / 固定の公開試料
from sd07 import *
db = database()
issue(db,"anon-public","anonymous",0)
print(db.execute("SELECT length(hash),subject,created,last,active FROM sessions").fetchall())[(64, 'anonymous', 0, 0, 1)]
rotate_session(db,"anon-public","login-public","alice",10)
print(access(db,"anon-public",11))
print(access(db,"login-public",11))None alice
cookie = SimpleCookie()
cookie.load("__Host-sid=login-public; Secure; HttpOnly; Path=/; SameSite=Lax")
print({k:cookie["__Host-sid"][k] for k in ("secure","httponly","path","samesite","domain")}){'secure': True, 'httponly': True, 'path': '/', 'samesite': 'Lax', 'domain': ''}print(access(db,"login-public",70))
print(db.execute("SELECT last FROM sessions WHERE subject='alice'").fetchone())
print(access(db,"login-public",130))alice (70,) None
issue(db,"active-public","alice",0)
print([access(db,"active-public",t) for t in (50,100,150,200,250,299,300)])['alice', 'alice', 'alice', 'alice', 'alice', 'alice', None]
issue(db,"logout-public","alice",400)
print(access(db,"logout-public",401))
print(logout(db,"logout-public"))
print(access(db,"logout-public",402))alice 1 None
print(db.execute("SELECT created,last,active FROM sessions WHERE subject='alice' ORDER BY created").fetchall())[(0, 299, 1), (10, 70, 1), (400, 401, 0)]
演習完了