Chome 80が正式リリースされSameSite属性が付与されていないクッキーを「SameSite=None」ではなく「SameSite=Lax」として取り扱うように仕様変更されました。
これによりクロスドメインでクッキーを送信する事が難しくなりました。この対応に苦慮している開発者やインフラエンジニアは多いのではないかと思います。
A10 ACOSのパーシステンスクッキー
A10はセッションを維持させるためにパーシステンスクッキーを発行できます。パーシステンスクッキーであればSameSite属性があってもなくても問題ないように思えます。しかし現実には問題が発生する場合があります。
問題が発生する例
たとえばログイン中のサイトAで送金処理をするとします。A10はエンドユーザーがサイトAにログインする際にエンドユーザーのWEBブラウザへパーシステンスクッキーを発行しています。このクッキーをWEBブラウザがA10へ送信する事によってA10はクッキーの情報からどのPoolメンバーへ振り分けるべきか判断できます。ログイン中は常に同じサーバーへ振り分けますからログインセッションが切れることはありません。
送金処理に話しを戻しましょう。サイトAでは送金処理をサイトBと連携しているためWEBブラウザの画面をサイトAからサイトBに遷移させます。そしてサイトBで送金が問題なく完了したらWEBブラウザの画面を元のサイトAへ遷移させます。
ここでどのようにサイトAに遷移させるか、これが問題です。
このときPOSTメソッドでサイトAへ遷移するとパーシステンスクッキーのSameSite属性がNoneでない場合を除いてWEBブラウザはサイトAにパーシステンスクッキーを送信しません。WEBブラウザがパーシステンスクッキーを送信しない場合、A10は新たにPoolメンバーから振り分け先を選んでしまいます。ここで最初のログイン時とは別のサーバーに振り分けてしまうとログインセッションは切れてしまいます。
aFlexでブラウザ判定しながらSameSite属性を付与する
何でもかんでもSameSite属性にNoneを付ければ良いというわけではありません。というのもSameSite=Noneに対応していないWEBブラウザがあるからです。
- Chrome 51~66 は SameSite=None のついたクッキーを弾く
- WebKitのバグのため、iOS 12とmacOS 10.14のSafariは、SameSite=None を SameSite=Strictとして扱う
他にもあるでしょうが、わたしは情報を持っていないので全てを掲載できません。これらのWEBブラウザについてはSameSite属性を付けないようにした方が良いでしょう。
クッキー暗号化解除は必須
A10 ACOSでSameSite属性を付与する際の注意点として、パーシステンスクッキーを暗号化している場合は解除する必要があります。
ACOS 4.1.1-P6からデフォルトでパーシステンスクッキーを暗号化ようになっており、それまでデフォルト設定(クッキー暗号化しない)で動作させていた場合でもAOCSのバージョンアップで暗号化するように変わっていますから注意してください。
サンプルaFlex
SameSite属性はHTTPレスポンス送信時にしか設定できません。しかし送信元のWEBブラウザを判定できるのはHTTPリクエストを受けたときのみです。
そのためHTTPリクエストを受けた際にWEBブラウザ判定を行って結果を変数に設定しておき、レスポンス送信時に判定結果の変数を見てSameSite属性を付与すれば良いでしょう。
when RULE_INIT { set ::PERSIST_COOKIE "PERSIST-COOKIE-NAME" } when HTTP_REQUEST { set PORT [TCP::local_port] set NEEDSAMESITE 0 switch -glob -- [HTTP::header User-Agent] { "*Chrome/5[1-9]*" - "*Chrome/6[0-6]*" - "その他の除外パターン1" - "その他の除外パターン2" { set NEEDSAMESITE 0 } default { set NEEDSAMESITE 1 } } } when HTTP_RESPONSE { set current_time [TIME::clock seconds] if { [HTTP::cookie names] ne "" } { foreach cookie_name [HTTP::cookie names] { if { $cookie_name starts_with $::PERSIST_COOKIE } { if { $NEEDSAMESITE == 0 } { break } set cookie_value [HTTP::cookie value "$cookie_name"] set cookie_expires [HTTP::cookie expires "$cookie_name"] set cookie_domain [HTTP::cookie domain "$cookie_name"] set cookie_path [HTTP::cookie path "$cookie_name"] set new_cookie "$cookie_name=$cookie_value" if { $cookie_expires > $current_time } { set new_expire [clock format $cookie_expires -format {%a, %d %b %Y %H:%M:%S GMT} -gmt 1] append new_cookie "; Expires=$new_expire" } if { $cookie_domain ne "" } { append new_cookie "; Domain=$cookie_domain" } if { $cookie_path ne "" } { append new_cookie "; Path=$cookie_path" } if { $PORT == 443 } { append new_cookie "; Secure" } if { $PORT == 80 or $PORT == 443 } { append new_cookie "; HttpOnly" append new_cookie "; SameSite=none" } HTTP::cookie remove "$cookie_name" HTTP::header insert Set-Cookie "$new_cookie" } } } }
変数 PERSIST_COOKIE はパーシステンスクッキーの名前です。実際に設定しているパーシステンスクッキーの名前を設定してください。
WEBブラウザの判定はswitchの箇所で行っています。「-glob」で正規表現を使いマッチさせると良いでしょう。
aFlexの正規表現ではglobスタイルとregexpスタイルがありますが、通常はglobスタイルの方がシンプルで使いやすいと思います。
正規表現 | globスタイル | regexpスタイル |
* | 0 文字以上䛾文字にマッチ | 0 文字以上䛾文字にマッチ |
? | 1 文字にマッチ | 0 または 1 文字にマッチ |
. | (未対応) | 1 文字にマッチ |
+ | (未対応) | + 1 文字以上にマッチ |
^ | (未対応) | 行の先頭にマッチ |
$ | (未対応) | 行の最後にマッチ |
[…] | []の中の1文字にマッチ | []の中の1文字にマッチ |
\x | 文字 x にマッチ | 文字 x にマッチ |
{a,b,…} | a,b 等文字列にマッチ | a,b 等文字列にマッチ |
exp1 | exp2 | (未対応) | exp1またはexp2にマッチ |
(exp) | (未対応) | 部分パターンにマッチ |
まとめ
インフラエンジニアの方はSameSite属性への対応要件がちらほら入ってくるかも知れません。そういう場合の対処になれば幸いです。