テレビのアニメはどこから来る? / 第50話

第50話:イトは、Cookie cardだけでlogin中と決めなかった|sessionはどこで切れた?

ピコルート第50話。Cookieが送られても401になる一件を、イトがclient identifierとserver session rowに分け、fresh authenticationで作り直します。

authenticated sessionsession identifierとserver stateidleとabsolute timeoutinvalidationとreauthentication10分更新

第49話で、イトはCookieを全部のrequestへ付けなかった。

matching HTTPS account request一本だけへsidが届き、finale startは200になった。

static別hostとinsecure requestには送っていない。

intermission後、イトがencore controlを押す。

今度は401 cardが返った。

request traceにはCookie: sid=opaque-7Kがある。

browser shelfにも、同じCookie cardが残っている。

「Cookieの期限を延ばせば、login中へ戻るはずだ」

イトはclient-side expiry clockの針を先へ動かせる延長leverへ手を掛けた。

encore signalを出すまで、あと五十五秒。

Cookieが届いたことと、sessionが生きていることは別

ピコはbrowser shelfとserver ledgerの間にtransparent separatorを立てた。

イト

さっき直したCookieがrequestに乗っているのに、どうしてlogin中じゃないの?

ピコ

clientがidentifierを持つことと、serverがそのsessionをcurrentとして受け付けることは別だよ

イト

Cookie cardだけ残って、対応するledger rowは終わることがある?

ピコ

ある。両側のevidenceを分けて見よう

この話のaccount service、session ledger、clock、fresh-auth gateは、外のInternetから切り離した学習fixtureだ。

実serviceのcredential、session ID、timeout値は使わない。

framework API、distributed session store、JWT等のstateless tokenも再現しない。

sessionは、authentication後の続き

NIST SP 800-63B-4のSession Managementは、authentication event後の複数interactionでapplication利用を続けるため、session subject側softwareとsession host側serviceをsession secretでbindする考え方を整理する。

毎requestでpassword等credential全部を送り直すのではない。

session hostがauthenticationを受けてsession secretを発行し、client側softwareが後のinteractionでそれを提示する。

Web applicationでは、そのsecret / identifierをCookieで運ぶ設計がよくある。

ただし、NIST文書はdigital identity assurance向けだ。

このfixtureへ政府serviceのassurance levelやtimeout値をそのまま当てはめない。

ここで借りるのは、authentication event、session secret、session host、termination、reauthenticationを別々に見る枠組みだ。

identifierは、台帳そのものではない

browser側には短いidentifierがある。

client Cookie
name: sid
value: opaque-7K

server側には、そのidentifierをkeyにしたfixture rowがある。

server session row
key: opaque-7K
state: operator authentication accepted
privilege: encore control
idle deadline: closed
absolute deadline: open
status: invalidated

このvalueは説明用labelで、実session secretではない。

OWASP Session Management Cheat Sheetは、client側session IDをmeaninglessにし、meaning / business logicをserver-side session objectやrepositoryへ置く設計を勧める。

identifier自体から、username、role、cart、deadlineを読める必要はない。

また、予測しにくい十分なrandomnessを持つidentifierが必要になる。

この回はrandom generator実装やbit数を作らず、framework / trusted session mechanismの役割として境界に置く。

two clocksは、同じ期限ではない

browser Cookieにはclient-side lifetimeがある。

server sessionにはserver-side acceptance deadlineがある。

両方が同じ時刻に終わるとは限らない。

Cookieがbrowserに残っていても、server rowがexpired / revokedなら、そのidentifierはlogin stateを復元しない。

逆に、server rowが残っていても、client identifierが失われれば、そのbrowser requestから対応rowを見つけられない場合がある。

このfixture ledgerには二つのclockがある。

idle timeoutは、accepted activityがない期間による区切り。

absolute timeoutは、activityが続いてもsession全体を一定時点で区切る上限。

OWASPのSession Expirationは、idle / absolute timeoutをserver-sideでenforceし、purpose / risk / usabilityに応じて値を決めるよう整理する。

「すべてのsiteは何分」と一律に決めない。

今回のrowはintermission中にfixture idle deadlineを越え、serverがinvalidatedへ移した。

absolute deadlineはまだopenでも、idle条件だけでcurrent sessionではない。

logoutも、server側の状態を閉じる

sessionはtimeout以外でも終わる。

explicit logout。

account protection action。

server-side administrative revocation。

application deployment / repository loss等のoperation。

この回で再現するのはidle expirationだけだ。

原因不明の401を、すべてtimeoutと断定しない。

exact identifierがrequestへ含まれたか、serverがどのstatusで照合したか、deadline / logout / revocation event、auth service healthを分けて確認する。

client clockを延ばしても、server rowは戻らない

イトが触れたexpiry leverは、browser側のCookie lifetimeだけを変えるfixture controlだ。

server ledgerのinvalidated rowをcurrentへ戻さない。

clientが「まだ有効」と主張しても、serverがexpired identifierを受け付ければtimeoutをenforceしたことにならない。

session expirationをclient clockだけへ任せない。

old identifierを再びactive rowへ結び直す案にも問題がある。

fresh authenticationなしでold authorityを復活させれば、「今そのoperatorが操作しているか」を確かめずにencore privilegeを戻す。

Cookie cardはauthentication evidenceそのものではなく、accepted sessionをたどるbindingの一部だ。

イトの前に、三つの選択肢が開く

encore signalまで、あと三十秒。

A browser Cookieのexpiryだけを延ばす

cardは長く残る。

でも、serverのinvalidated rowは戻らず401の原因を直さない。

B old ledger rowをcurrentへ書き戻す

requestは通るかもしれない。

しかし、idle timeoutとfresh authenticationを飛ばし、old identifier / privilegeをそのまま復活させる。

C old sessionを閉じたまま、fresh authenticationからnew sessionを作る

server eventとclient cardを別trayへ置く。

old identifierをserverでrejectし続ける。

old Cookieもmatching attributesでexpireする。

fresh-auth gateを通ったあと、new unpredictable identifierとnew server rowを発行する。

ピコはexpiry leverにもledger rowにも触れなかった。

「残ったcardを長生きさせるより、今のauthenticationからnew sessionを始めよう」

イトはCを選んだ。

client expiry leverへclosed coverを戻し、old Cookie cardとinvalidated server rowをseparatorの両側へ自分の手で置いた。

old identifierを、両側で閉じる

serverはopaque-7K rowをinvalidatedのまま保持する。

同じidentifierを持つrequestが来ても、authenticated stateへ戻さない。

fixture responseはbrowser側のold Cookieも閉じる。

Set-Cookie: sid=; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/account; Secure; HttpOnly

client cleanupとserver invalidationを、どちらか一方だけの作業にしない。

OWASPのSession Expirationも、expiration / logout時にはclient identifierを無効化し、特にserver-side sessionをactiveに閉じる必要を説明する。

イトはold cardをnew rowのkeyとして再利用しなかった。

fresh authenticationから、new rowを作る

イトはfixture fresh-auth gateへoperatorのcurrent authentication evidenceを渡した。

この回はgateがacceptedを返すところだけを使い、password / second factorの内部検証は次話へ送る。

accepted eventを受け、session hostはnew identifierを生成した。

new client identifier label: opaque-9M

server ledgerにもnew rowを作る。

key: opaque-9M
state: fresh authentication accepted
privilege: encore control
idle deadline: new
absolute deadline: new
status: current

fixture responseはnew Cookieを返した。

Set-Cookie: sid=opaque-9M; Path=/account; Secure; HttpOnly

イトがtransparent separatorのleft browser trayへold amber Cookie card、right server ledgerへmatching invalidated old rowをclosed状態で残し、中央のfresh-auth gateを通したあと、single new violet identifier cardとsingle blue current ledger rowを自分の両手で一対にしている。old replay railはclosed、new request railだけがgreen
残ったCookieの期限だけを延ばさず、old client identifierとserver rowを閉じ、fresh authenticationからnew pairを作る。

newは200、oldは401のまま

イトはtwo-request traceを流した。

new browser requestはsid=opaque-9Mを送る。

serverはcurrent new rowとdeadlineを照合し、encore controlへ200 cardを返した。

別のold tabは、保存していたsid=opaque-7Kをもう一度送った。

server rowはinvalidatedのままだ。

old replayには401 cardを返し、fresh authenticationへ案内した。

oldも200に戻ることが成功ではない。

new sessionだけがcurrentになり、old identifierが拒否され続けることまでがobservable resultだ。

encore signalがprojectorへ届いた。

sessionを疑うときは、両側の時刻を見る

workbenchには、四つの結果が残っている。

client trayでexpiredになったold Cookie card。

server ledgerでinvalidatedのままのold row。

fresh authenticationから生まれたnew identifier + new current row。

new 200とold 401を分けるtwo response cards。

Cookie cardが残っているかだけでlogin中と決めず、client identifierとserver-side row / deadlinesを分ける。idle / absolute timeout、logout / revocation event、exact requestを確認する。expired sessionをclient expiry延長やold row復活で戻さず、必要ならfresh authenticationからnew sessionを作る。

「終わったsessionを消すだけじゃなく、終わったままにするんだ」

イトはold identifierをredrawせず、new pairだけをcurrent railへ置いた。

fresh-auth gateの奥で、二つ目のcheck lampが消えたまま残っている。

今回のfixtureはfirst authentication evidenceだけでacceptedになった。

でも、account protection modeでは、もう一種類のevidenceが必要になる。

次回:second factorは、何を増やす?

fresh-auth gateには、password laneと別device laneがある。

「checkを二回押すことと、factorを二種類にすることは同じ?」

イトはtwo lampsを同じwireへつながず、別々のevidence holderへ分けた。

次回、ピコルート第51話。

イトは、同じpasswordを二回入れて2FAにしなかった|second factorは何を増やす?

この物語を、解説で深める

第50話で生まれた疑問を、解説記事で少し深く見ていきます。物語で感じた不思議さを残したまま、言葉と仕組みを順番に整理しましょう。

セッションとは?ログイン状態・Cookie・セッションIDの違いを読む