本サイトはプロモーションを含みます。

BIG-IP iRulesでブラウザ判定しながらSameSite属性を付与する

Chome 80が正式リリースされSameSite属性が付与されていないクッキーを「SameSite=None」ではなく「SameSite=Lax」として取り扱うように仕様変更されました。

これによりクロスドメインでクッキーを送信する事が難しくなりました。この対応に苦慮している開発者やインフラエンジニアは多いのではないかと思います。

BIG-IPのパーシステンスクッキー

BIG-IPはセッションを維持させるためにパーシステンスクッキーを発行できます。パーシステンスクッキーであればSameSite属性があってもなくても問題ないように思えます。しかし現実には問題が発生する場合があります。

問題が発生する例

たとえばログイン中のサイトAで送金処理をするとします。BIG-IPはエンドユーザーがサイトAにログインする際にエンドユーザーのWEBブラウザへパーシステンスクッキーを発行しています。このクッキーをWEBブラウザがBIG-IPへ送信する事によってBIG-IPはクッキーの情報からどのPoolメンバーへ振り分けるべきか判断できます。ログイン中は常に同じサーバーへ振り分けますからログインセッションが切れることはありません。

送金処理に話しを戻しましょう。サイトAでは送金処理をサイトBと連携しているためWEBブラウザの画面をサイトAからサイトBに遷移させます。そしてサイトBで送金が問題なく完了したらWEBブラウザの画面を元のサイトAへ遷移させます。

ここでどのようにサイトAに遷移させるか、これが問題です。

このときPOSTメソッドでサイトAへ遷移するとパーシステンスクッキーのSameSite属性がNoneでない場合を除いてWEBブラウザはサイトAにパーシステンスクッキーを送信しません。WEBブラウザがパーシステンスクッキーを送信しない場合、BIG-IPは新たにPoolメンバーから振り分け先を選んでしまいます。ここで最初のログイン時とは別のサーバーに振り分けてしまうとログインセッションは切れてしまいます。

iRulesでSameSite=Noneを付与する

単純にSameSite=Noneを設定するだけならば簡単です。パーシステンスクッキーの名前はデフォルトの「BIGipServer」+Pool名と仮定しています。設定でクッキー名を変更している場合は読み替えてください。

when HTTP_RESPONSE {
    # パーシステンスクッキー名
    set persitcookie {BIGipServerXXX}

    foreach cookie [HTTP::cookie names] {
        if { ($cookie equals $persitcookie) } {
            HTTP::cookie attribute $cookie insert "SameSite" "None"
        }
    }
}

特定のパーシステンスクッキーを対象にする場合はこのままで良いのですが、すべてのパーシステンスクッキーを対象にするのであれば以下のように3行目と6行目を変更します。

when HTTP_RESPONSE {
    # パーシステンスクッキー名
    set persitcookie {BIGipServer}

    foreach cookie [HTTP::cookie names] {
        if { ($cookie starts_with $persitcookie) } {
            HTTP::cookie attribute $cookie insert "SameSite" "None"
        }
    }
}

「starts_with」でパーシステンスクッキーすべてに対応させます。

iRulesでブラウザ判定しながらSameSite属性を付与する

何でもかんでもSameSite属性にNoneを付ければ良いというわけではありません。というのもSameSite=Noneに対応していないWEBブラウザがあるからです。

  • Chrome 51~66 は SameSite=None のついたクッキーを弾く
  • WebKitのバグのため、iOS 12とmacOS 10.14のSafariは、SameSite=None を SameSite=Strictとして扱う

他にもあるでしょうが、わたしは情報を持っていないので全てを掲載できません。これらのWEBブラウザについてはSameSite属性を付けないようにした方が良いでしょう。

サンプルiRules

SameSite属性はHTTPレスポンス送信時にしか設定できません。しかし送信元のWEBブラウザを判定できるのはHTTPリクエストを受けたときのみです。

そのためHTTPリクエストを受けた際にWEBブラウザ判定を行って結果を変数に設定しておき、レスポンス送信時に判定結果の変数を見てSameSite属性を付与すれば良いでしょう。

when HTTP_REQUEST {
    # SameSite属性付与要否フラグ
    set needsamesite 0
    
    # SameSite=Noneを付与しないWEBブラウザか判定
    # この判定は厳密でないので必要に応じて修正が必要です
    switch -glob -- [HTTP::header User-Agent] {
        "*Chrome/5[1-9]*" -
        "*Chrome/6[0-6]*" -
        "その他の除外パターン1" -
        "その他の除外パターン2" {
            # SameSite属性付与不要
            set needsamesite 0
        }
        default {
            # SameSite属性付与必要
            set needsamesite 1
        }
    }
}

when HTTP_RESPONSE {
    # SameSite=Noneを付与するクッキー名「starts_with」と「equals」で設定するべき値が変わるので注意
    set persitcookie {BIGipServer}

    # クッキー名を厳密に指定する場合は「starts_with」でなくて「equals」を使います
    if { ($needsamesite == 1) } {
        foreach cookie [HTTP::cookie names] {
            if { ($cookie starts_with $persitcookie) } {
                HTTP::cookie attribute $cookie insert "SameSite" "None"
                # Secure属性付与は別の箇所で行っているのであれば不要です
                HTTP::cookie secure $cookie enable
            }
        }
    }
}

34行目でSecure属性を付与しています。既に別の方法でSecure属性を付与しているのであれば削除してしまってかまいません。

WEBブラウザの判定はswitchの箇所で行っています。「-glob」で正規表現を使いマッチさせると良いでしょう。

iRulesの正規表現では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属性への対応要件がちらほら入ってくるかも知れません。そういう場合の対処になれば幸いです。

尚、サンプルiRulesでSecure属性を付与しています。SameSite属性とは別にSecure属性も付与する必要があるので注意してください。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)