前陣子在陪好友練習 Cookie 操作時,無意間發現瀏覽器跳出一個奇怪的警告訊息:
若現在不理它,未來可能會被瀏覽器無視,所以先來了解要如何解決。
SameSite 定義
MDN 中對 sameSite 說明如下:
Values
The
SameSite
attribute accepts three values:
Lax(一般;預設值)
Cookies are allowed to be sent with top-level navigations and will be sent along with GET request initiated by third party website. This is the default value in modern browsers.
Strict(最嚴格)
Cookies will only be sent in a first-party context and not be sent along with requests initiated by third party websites.
None(不限制)
Cookies will be sent in all contexts, i.e sending cross-origin is allowed.
None
used to be the default value, but recent browser versions madeLax
the default value to have reasonably robust defense against some classes of cross-site request forgery (CSRF) attacks.
None
requires theSecure
attribute in latest browser versions. See below for more information.
雖然沒有說明是否為必須設定,而且預設值是 Lax,現在不處理好像也沒關係。
程式解法
依照 iTHome 【Chrome 80將採用新的Cookie安全模型預設無法跨站存取Cookie】報導指出,不是每一家函式庫和語言都有支援 SameSite,像 Delphi 的 WebBroker 就沒有……
PHP 則是到 7.3 版之後有內建支援:
PHP 7.2 以前 = header(‘Set-Cookie: cross-site-cookie=name; SameSite=None; Secure’);
PHP 7.3 = setcookie(‘cross-site-cookie’, ‘name’, [‘samesite’ => ‘None’, ‘secure’ => true]);
PHP 都有這種問題,那 Delphi 應該也不例外,也就是說,PHP 7.2 以前的寫法很值得參考。
回到 Delphi,和 Cookie 有關的設定在 HTTPApp 單元裡的 TCookie 類別:
其中的 GetHeaderValue 就是 TCookie 兜出等同 PHP 7.2 Response 自訂 header 的內容:
function TCookie.GetHeaderValue: AnsiString; var S: string; begin S := Format('%s=%s; ', [HTTPEncode(FName), HTTPEncode(FValue)]); if Domain <> '' then S := S + Format('domain=%s; ', [Domain]); { do not localize } if Path <> '' then S := S + Format('path=%s; ', [Path]); { do not localize } if Expires > -1 then S := S + Format(FormatDateTime('"expires="' + sDateFormat + ' "GMT; "', Expires), { do not localize } [DayOfWeekStr(Expires), MonthStr(Expires)]); if Secure then S := S + 'secure'; { do not localize } if Copy(S, Length(S) - 1, MaxInt) = '; ' then SetLength(S, Length(S) - 2); Result := AnsiString(S); end;
以這個基礎,改寫如下:
function GetHeaderValue(FName, FValue:string; Domain:string=''; Path:string=''; SameSite: string='Lax'; Expires: TDateTime=-1; Secure: Boolean=False): string; var S: string; begin S := Format('%s=%s; ', [HTTPEncode(FName), HTTPEncode(FValue)]); if Domain <> '' then S := S + Format('domain=%s; ', [Domain]); { do not localize } if Path <> '' then S := S + Format('path=%s; ', [Path]); { do not localize } if Expires > -1 then S := S + Format(FormatDateTime('"expires="' + sDateFormat + ' "GMT; "', Expires), { do not localize } [DayOfWeekStr(Expires), MonthStr(Expires)]); if SameSite='' then S := S + 'SameSite=None;' else S := S + Format('SameSite=%s;', [SameSite]); if Secure then S := S + 'secure'; { do not localize } if Copy(S, Length(S) - 1, MaxInt) = '; ' then SetLength(S, Length(S) - 2); Result := (S); end;
增加了對 SameSite 的支援,對現代的瀏覽器支援又更進一步。
沒有留言:
張貼留言