1. 程式人生 > 其它 >跨域問題和使用 cookie 的限制

跨域問題和使用 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 的 SameSiteSameSiteMode.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 阮一峰的網路日誌