資安議題 -- Cookies
前言
cookie可以讓網頁帶有狀態性,達到Session 管理、個人化、追蹤的功能,cookie是面向客戶端,儲存在瀏覽器裡面。
cookie由伺服器端設置後放到response的header裡,瀏覽器取得後放入儲存cookie的地方,向伺服器發送request時,會將存放的cookie放入request的header中。
javascript設置的cookie不能帶有安全性標誌,如Secure、HttpOnly、SameSite。
設置Cookie
設置cookie的方式有兩種:
- 使用伺服器設定 cookie,通過 response 的 Set-Cookie header,瀏覽器會根據 Set-Cookie 儲存 cookie。
- 使用客戶端的 javascript 設定 cookie,例如:document.cookie = "name=John;"。
Cookie flag
Expires
設定一個日期,到日期時就會自動失效。
Max-Age
設定秒數,秒數過後自動失效,比Expires優先度高。
Domain
設定作用網域,設定之後會包含子網域,若無設定則默認當前網域,不包含子網域。
Path
設定作用路徑,設定/admin將匹配/admin/users,/admin/roles等路徑。
Secure
只能使用https傳到伺服器。
HttpOnly
只能經由伺服器存取cookie,不能經由document.cookie。
SameSite
cookie不能跨域發送。
cookie行為
1. 當server設定cookie,會在response的header加入Set-Cookie告知瀏覽器,瀏覽器會將cookie保存在瀏覽器中。
2. 設定完成後,request會帶著儲存的cookie去server。
response header的set cookie:
request header的cookie:
3. server端設定的cookie會帶有httponly,javascript無法讀取跟修改此類的cookie,若javascript先設定了id = 1,之後 server 設定了 id = 2,cookie將會被覆蓋並會被加上httponly,javascript就無法在讀取id的值。
4. 當使用者用瀏覽器去訪問網站,server可以藉由request取得由javascript設定的cookie,但若是使用框架( 如:laravel ),有些框架會去加密server設定的cookie,則request裡面javascript的cookie無法直接取得,因為cookie經過serve端已經加密,javascript設定的cookie會保留明文,如下圖。
javascript如下:
5. javascript無法設定httponly。
Laravel XSRF-TOKEN的問題
Set-Cookie: XSRF-TOKEN= << value >> ; expires=Tue, 22-Dec-2020 12:56:36 GMT; Max-Age=7200; path=/; samesite=lax;
可以發現這個cookie是由server設定的但卻沒有httponly,是因為在XSRF-TOKEN上加入httponly無法增加安全性上的優勢。
可以參考此處:https://security.stackexchange.com/questions/175536/does-a-csrf-cookie-need-to-be-httponly
但是資安檢查不會管你需不需要加,他只在乎有沒有加,所以就變成在laravel上的XSRF-TOKEN一定要有httponly。
解法如下:
在middleware裡面的 VerifyCsrfToken 重載 addCookieToResponse function,將 new Cookie 的最後一個參數設為true。
Symfony\Component\HttpFoundation\Cookie 的建構子參考:https://github.com/symfony/symfony/blob/2.7/src/Symfony/Component/HttpFoundation/Cookie.php
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
use Symfony\Component\HttpFoundation\Cookie;
class VerifyCsrfToken extends Middleware
{
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* @var bool
*/
protected $addHttpCookie = true;
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
//
];
protected function addCookieToResponse($request, $response)
{
$response->headers->setCookie(
new Cookie('XSRF-TOKEN',
$request->session()->token(),
time() + 60 * 120,
'/; samesite=strict',
null,
config('session.secure'),
true)
);
return $response;
}
}
留言
張貼留言