0%

[Cookie] Cross site cookie

前言

這篇文章主要是記錄一下使用跨域 cookie 需要設定及注意的地方,以下範例程式後端是使用 Python/Flask, 前端則是用 Axios 來送出 request 到後端。

Cookies

首先,先來介紹一下 Cookie 幾個比較重要的屬性:

  • HttpOnly: 只能被用在 HTTP 傳輸,不可被 JavaScript 存取
  • Domain: 可使用的 domain, 如果沒有設定,則只能被設定此 cookie 的 domain 所使用,如果希望設定子網域都可使用此 cookie, 則可以設為 .domain.com
  • Path: 指定可存取 cookie 的路徑
  • Max-Age: cookie 的期限,單位為秒,或者可以用 Expires 來設定,格式是 Unix time
  • SameSite: 用來防止瀏覽器將 cookie 跨網站傳送,可以幫助避免 CSRF 攻擊,以下是 SameSite 的三種設定值:
    • Strict: 最嚴謹,request 的網域與目前網址相同才會發送 cookie
    • Lax: 除了 same site request 能夠使用 cookie之外,部分的 cross-site request 也能夠使用 cookie, 包含 HTML 中的 <a> , <link rel="prerender">, <form method="GET">
    • None: Cross site 的 request 都能夠使用 cookie, 注意在 Chrome 80 之後,若使用這個選項,則必須要設定 Secure
  • Secure: 若設為 True, 表示只能透過 HTTPS 傳輸

注意: 在 Chrome 80 之後,如果沒有設定 SameSite, 則預設為 Lax, 如果 SameSite 設為 None, 則必須要有 Secure.

Backend

後端需要設定 CORS (Cross Origin Resource Sharing), 因為會使用到 Cross site cookie, 所以也記得要設定 Access-Control-Allow-Credentials = true , for example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# In app.py
from flask import Flask, jsonify
from flask_cors import CORS

app = Flask(__name__)
whitelist = ['http://localhost:8080', 'https://domain1.com', 'https://domain2.com']
resource = {
r'/.*': {
'origins': whitelist
}
}
CORS(app, resources=resource, supports_credentials=True, methods='GET,POST,OPTIONS', allow_headers='*')

@app.route('/login', methods=['POST'])
def login():
# User authentication

# Set cookie
response = jsonify(status='success')
response.set_cookie(
'access_token', 'your_access_token',
domain='.your_domain.com',
path='/',
max_age=600,
secure=True,
httponly=True,
samesite=None
)
response.set_cookie(
'refresh_token', 'your_refresh_token',
domain='.your_domain.com',
path='/refresh',
max_age=86400,
secure=True,
httponly=True,
samesite=None
)
return response

if __name__ == '__main__':
app.run()

注意: 設定 supports_credentials 的話, origins 就不能用 *

Frontend

前端的部分,我們是使用 Axios 來送出 request 到後端,這邊記得要設定 withCredentials: true, for example:

1
2
3
4
5
axios.post('/login', data, {
withCredentials: true
}).then((response) => {
console.log(response);
});

如果順利的話,會在在網頁的 Cookie 中看到新增了兩個 Cookie (access_token, refresh_token).

但是,在 localhost 測試時會發現無法設定 cookie, 這是因為要跨域使用 cookie, SameSite 就必須設為 None, 而 SameSite 設為 None 就必須要設定 Secure, 但我們目前的 localhost 是沒有設定 SSL 的,所以會無法設定 cookie. 解決方式是把 localhost 加上 SSL.

Localhost 加上 SSL 可參考以下文章:

參考資料