跨域問題和使用 cookie 的限制
前言
在我的文章 使用 cookie 的身份驗證和授權 的最後,講到了跨域問題,這篇文章就簡單介紹跨域的相關知識,並說明在 net core 中怎麼設定跨域。
使用的版本為 net6,並使用 MiniApi 演示。
XSS 攻擊
跨域的由來不得不提到 XSS 攻擊。
全名:Cross-site scripting(跨站指令碼攻擊)。這是一種安全漏洞,攻擊者可以利用這種漏洞在網站上注入惡意的客戶端程式碼。若受害者執行這些惡意程式碼,攻擊者就可以突破網站的訪問限制並冒充受害者。簡單地說,就是我可以在你的網站偷偷上執行我的程式碼,那有多危險。
為了應對這種情況,便有了瀏覽器的同源策略。
同源策略
這個策略是由瀏覽器去實現的,其目的在於限制請求方如何與另一個源的資源進行互動。
“源”你就理解為地址,但它的完整格式由協議、域名、埠組成,如:
https://store.company.com:8000
另一個客戶端請求上面的地址,如果協議、域名、埠其中有一個不同,這兩個就算兩個源,客戶端的行為要受到限制。
下表給出了與 URL http://store.company.com/dir/page.html
的源進行對比的示例:
URL | 結果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html |
同源 | 只有路徑不同 |
http://store.company.com/dir/inner/another.html |
同源 | 只有路徑不同 |
https://store.company.com/secure.html |
失敗 | 協議不同 |
http://store.company.com:81/dir/etc.html |
失敗 | 埠不同 ( http:// 預設埠是 80) |
http://news.company.com/dir/other.html |
失敗 | 主機不同 |
不同源的請求,就被認為是跨域,是不被允許的。如果發起了跨域了 GET 請求,你可能會在控制檯中看到了如下輸出:
其原因就是因為跨域了,且伺服器沒有允許你這個源,所以請求被拒絕了。
而在我們的前後端分離專案中,前端和後端是分別部署在不同的源上的,那麼我前端請求後端的資料也會遭受到跨域的限制,那麼我們需要放寬這個限制,不然就獲取不到資料了。放寬限制,一般是要在後端進行操作,我以 net6 為例子。
net6 配置跨域
在 net6 中放寬我們的跨域限制,需要先配置我們的跨域策略。新增如下程式碼:
builder.Services.AddCors(options =>
options.AddPolicy(name: "CORS Name", b =>
{
b.WithOrigins("https://example.net");
})
);
使用 AddCors
配置跨域,在引數 options
中,使用 options.AddPolicy
新增一個跨域策略,第一個引數 name
,是策略的名字,第二個引數是這個策略的內容。在上面的內容中,使用了 WithOrigins
來配置允許的源。源的最後是沒有斜槓的,不可以寫成 https://example.net/
。
接下來,我們的客戶端源:https://example.net
在發起 GET 請求的話,就會突破同源的限制,成功請求。
在上面配置了源的情況下,如果使用了 PUT 請求,也會因為跨域請求失敗。這是因為跨域還限制了請求方法,預設是支援 GET 和 POST 的,如果要支援其他的方法,也要配置,如下:
builder.Services.AddCors(options =>
options.AddPolicy(name: "CORS Name", b =>
{
b.WithOrigins("https://example.net")
.WithMethods("PUT", "DELETE");
})
);
在上述程式碼上,我們就支援了 PUT 和 DELETE 請求。此外如果說要允許所有的源、所有方法的話,可以直接使用 .AllowAnyOrigin()
、.AllowAnyMethod()
。一般不會這樣用,但也說不定,畢竟防呆不防傻。
在中介軟體中使用 UseCors
擴充套件方法啟用跨域,在引數中指定策略名,順序必須在 UseRouting
之後,但在 UseAuthorization
之前:
app.UseRouting();
// ...
app.UseCors("CORS Name");
// ...
app.UseAuthorization();
使 cookie 跨域
如果你使用 cookie 來存放我們的憑證資訊,且使用的前後端是不同的源,那麼還需要做一些配置,來使 cookie 可以跨域。
在跨域配置中使用 .AllowCredentials()
來允許身份憑證資訊跨域傳遞,還需要再身份驗證中配置 cookie 的 SameSite
為 SameSiteMode.None
,並且設定 Secure
屬性。這樣,在不同源也可以接收 cookie。SameSite
用於限制第三方 cookie,是出於安全考慮,值為 SameSiteMode.None
表示不限制第三方 cookie。
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
// 配置 cookie
options.Cookie.SameSite = SameSiteMode.None;
});
builder.Services.AddCors(options =>
options.AddPolicy(name: "CORS Name", b =>
{
b.WithOrigins("https://example.net")
.AllowCredentials();
})
);
然後在我們的前端中發起請求,因為是不同源,如果要攜帶上 cookie,在發起請求時要設定 credentials: "include"
,以 fetch
為例:
fetch(`https://example.net/logout`, {
method: "post",
credentials: "include",
});
總結
如你所見,使用 cookie 有很多的限制,這些限制的本意是讓 cookie 更加安全。而我們為了要讓 cookie 跨域,又不得不一點點解除限制,很是難受。
所以不推薦在跨域的情況下使用 cookie 存放身份憑證資訊,不安全。
參考資料
Cross-site scripting(跨站指令碼攻擊) by MDN
瀏覽器的同源策略 by MDN
在 ASP.NET Core 中啟用跨源請求 (CORS) by Microsoft
Cookie 的 SameSite 屬性 by 阮一峰的網路日誌