SPA 身份验证和安全小指南
AWS 安全上线!
这绝不是一份详尽的指南,仅供您入门。
设置:假设我们要构建一个部署到 的新 SPA 应用m.example.com
,同时我们还有一个旧应用,例如 Ruby on Rails,也部署到www.example.com
。新应用将是一个静态网站,例如,我们只会在其中部署资源(JS、HTML、CSS、图片)(它可能是一个带有后端和 SSR 的应用,但为了简单起见,我们先忽略这一点)。此外,我们将为api.example.com
SPA 应用提供一个 API 端点。
共享会话
我们希望在新旧应用程序之间共享会话。为此,我们需要在根域中使用 Cookie - Cookie 的 HTTP 标头如下所示:
set-cookie: SID=...; Domain=.example.com
注意域名开头的点。这样,浏览器就会将 Cookie 发送到我们所有的子域名,例如m.example.com
、www.example.com
、api.example.com
。一旦用户在我们的某个服务中通过身份验证,他们的身份验证也将在所有服务中进行。
Cookie 的安全性
所有这些考虑都是为了api.example.com
和www.example.com
。
HttpOnly
HttpOnly
指令不允许 JavaScript 访问 cookie,以防止通过 XSS 劫持会话。
set-cookie: SID=...; HttpOnly
Secure
Secure
该指令指示浏览器仅通过 HTTPS 发送 Cookie,以防止通过中间人攻击劫持会话。(如果攻击者能够伪造证书,则仍然可能发起攻击)
set-cookie: SID=...; Secure
SameSite
SameSite
指令可以防止 CSRF 攻击。我选择使用这个指令的更宽松版本 ( Lax
),在大多数情况下它应该足够了(请阅读说明并自行判断它是否足够)。
set-cookie: SID=...; SameSite=Lax
资产安全
所有这些 HTTP 标头都是针对m.example.com
和 的www.example.com
。
Strict-Transport-Security
Strict-Transport-Security: max-age=86400
X-Content-Type-Options
X-Content-Type-Options: nosniff
X-Frame-Options
X-Frame-Options: DENY
X-XSS-Protection
X-XSS-Protection: 1; mode=block
Content-Security-Policy
我在这篇文章中没有用到它Content-Security-Policy
,但我强烈推荐你使用它。(也许我会另写一篇文章专门介绍它)
API 安全性
跨域资源共享 (CORS)
access-control-allow-methods: GET,HEAD,PUT,PATCH,POST,DELETE
access-control-max-age: 86400
指定允许访问 API 的域
access-control-allow-origin: https://m.example.com
否则allow-credentials
,Cookie 将不起作用。请注意,您不能将星号 ( *
) 与 credentials 指令一起使用。
access-control-allow-credentials: true
JSON API
对于所有请求(可能无需身份验证即可访问的端点除外),都需要Content-Type
,这将触发 CORS 检查(通过预检请求):
Content-Type: application/json; charset=utf-8
JS 客户端
现在我们已经掌握了所有基础知识,是时候从前端实际调用 API 了。让我们使用fetch
API来实现这一点。
匿名请求
对于允许匿名用户访问的端点,请使用“普通”获取。不要使用Content-Type
,否则,它会变得更慢,并且对用户没有任何好处。
fetch(url)
经过身份验证的请求
对于其他请求,请使用credentials: "include"
启用 Cookie(这是最新 Fetch 规范中的默认选项,但并非所有浏览器都实现了它)。用于headers: { "Content-Type": "application/json; charset=utf-8"}
触发 CORS 检查并真正通过后端(我们之前已“实现”)的检查。
对于GET
请求:
fetch(url, {
credentials: "include",
headers: { "Content-Type": "application/json; charset=utf-8"}
})
对于POST
请求:
fetch(url, {
credentials: "include",
headers: { "Content-Type": "application/json; charset=utf-8"},
method: "POST",
body: JSON.stringify(params)
})
鏂囩珷鏉ユ簮锛�https://dev.to/stereobooster/a-small-guide-to-authentication-and-security-for-spa-1am3照片由 Tianshu Liu 在 Unsplash 上拍摄