Web 应用安全,了解 BFF 模式的含义
身份和安全概述
说到安全,有很多指南、建议和评论,告诉你为什么应该或不应该使用每种方法。很难理解所有不同的选择以及它们所面临的安全问题。
您还拥有不同的客户端(移动/网络/应用程序),并且每个客户端对以令牌、cookie 或会话形式管理、存储和处理数据都有不同的安全要求。
最后,您的应用程序可能会扩展,并且并非所有选项都可以部署为多个实例节点,而无需对应用程序和基础架构进行额外的更改。
这意味着您确实需要在一开始就做出决定,并提出以下问题:您的客户是谁?您的要求是什么?将来可以在系统中添加什么?如何处理所选择的方法?等等。
本文是开源培训的一部分。
身份验证与授权
Authentication
- 验证用户身份(身份)的过程。Authorization
- 检查用户有权访问什么内容的过程(检查访问特定资源的权限)。
Tokens 与 Cookie 和 Sessions
令牌、cookie 和会话可以被解释为各种协议/标准/模式(OAuth、OpenID、BFF)用于执行与身份相关的任务的资源或工具。
理解基础知识很重要,因为它们最终会被组合使用。
最后,您可以将 JWT 令牌包装在 cookie 中,并将其与会话数据一起使用以进行“授权”和“身份验证”。
曲奇饼
Cookie 可以理解为由服务器创建的小数据块,它们在响应中写入一次,并在 Web 浏览器的每个后续请求中自动重新发送,直到其生命周期到期。
它们可用于身份验证/授权、追踪和营销目的。以及更多……
Cookie 有 3 个主要属性:
HttpOnly
- 仅限 http 的 Cookie 无法通过客户端 API(例如 JavaScript)访问。浏览器不允许您从前端代码访问此 Cookie。Secure
安全 cookie 只能通过加密的 HTTPS 连接传输。SameSite
3 个选项值Strict
,Lax
或者None
- 这告诉浏览器可以将 cookie 发送到哪个域和地址。
起源是什么?
来源一般是ip和端口或者域名和子域名。
// This are different origins since subdomain are different
https://developer.mozilla.org
https://mozilla.org
// This are also different origins since port number is different
https://localhost:5001
https://localhost:7001
另一个cookie定义:
Session cookies
- 仅为浏览器会话(在内存中)创建,关闭后删除/丢失。Third-party cookies
- 通常,cookie 的域属性与 Web 浏览器地址栏中显示的域匹配。 为first-party cookies
。 与third-party cookies
当前域不匹配,并用作tracking cookies
跟踪用户活动。
会议
Session 用于在服务器上临时存储信息,以供网站多个页面使用。它通常与 Cookie 相关联,Cookie 用于标识存储在服务器上的会话,但不包含任何数据。
代币
令牌是允许应用系统执行授权和身份验证过程的数据元素。它们通常编码为 base64 字符串。
有几种类型的令牌:
access token
- 包含用户声明并使用密钥进行签名。它使用 JWT 令牌。refresh token
- 用于在其生命周期到期后“刷新”并获取新的“访问令牌”。id token
- 有关用户个人资料信息的 JSON 编码数据- 等等等等...
JWT 令牌
JSON Web Token 是一种开放标准,它定义了如何以 JSON 对象的形式在各方之间安全地传输信息。
它们用于authorization
提供information exchange
安全证明,证明其中包含的信息是有效的并且由受信任的来源编写。
您可以轻松地将任意数据写入令牌,并对其进行签名,然后让客户端使用它来访问服务器资源。服务器可以验证令牌是否已签名且仍然有效。
基本 JWT 令牌流示例:
JWT 内容:
JWT 由 3 个部分组成:
-
Header
- 包含令牌类型(JWT)和使用的签名算法等信息,例如 HMAC SHA256 或 RSA。{ "alg": "HS256", "typ": "JWT" }
-
Payload
- 安全签名的数据(声明){ "sub": "1234567890", "name": "John Doe", "admin": true }
-
Signature
- 加密的标头、加密的有效负载、秘密并由标头中指定的算法签名。HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret
有关 JWT 令牌的更多附加信息,请参阅:官方文档。
最常用的授权访问API的令牌是
Bearer
token。
身份协议
有几种协议/规范可用于管理您的身份或授权过程。
这对于标准化服务和客户端之间的身份验证和授权至关重要。这样,我们就可以采用不同的全球身份/身份验证提供商,例如 Facebook、Google(外部/内部),并标准化流程的实施方式。
该演示重点介绍最常用的协议OAuth
和OpenID Connect
。
这两种协议默认使用 JWT 令牌来加密和签名敏感数据,或验证请求是否来自可信来源。您也可以在前端使用 Cookie,让后端为您完成会话和令牌授权。
您还可以观看和学习各种演讲:
OAuth
主要用于授权应用访问特定资源。无需与外部网站共享密码即可完成此操作。
如果您曾经登录过某个新应用并同意访问您的联系人、日历等信息,那么您就使用了OAuth 2.0 。该协议不提供任何有关用户终端的信息,仅提供访问特定资源的令牌。您可以阅读此文档,了解更多关于OAuth 的信息。
OAuth 通常为客户端提供对某些资源的“安全委托访问”。假设您是 Google 用户,某个应用想要访问您的日历数据。以下流程可以作为示例:
OAuth
流程示例:
在上面的例子中,像 Slack、Jira 等应用程序只获得访问特定资源(例如日历)的权限,但没有用户本身的权限,因此用户名和电子邮件等配置文件数据不会被传输并保持受保护。
如果您想了解有关 OAuth 的更多信息,可以观看以下演示:
代币兑换流程
有几种方法grant
可以替代。选择哪种方法取决于请求访问的客户端类型以及该客户端的可信度。
- 授权码流程
- 使用 PKCE 的授权码流程
- 隐式流
- 客户端凭证流
图片来自Okta
OpenID 连接
OpenID是一种去中心化身份验证协议
多个内部/外部应用程序使用的登录名。如果您使用 Google 或 Facebook 等登录到外部网站或应用程序,那么您就使用了OpenID Connect
。
OpenID Connect 基于OAuth 2.0。(OAuth 是底层协议,OpenId 是构建在其上的身份层)并使用名为 的 JWT 令牌,id_token
该令牌以 JSON 格式封装身份声明。有关 OpenId 的更多信息,请参阅本规范。
id_token
例子:
{
"iss": "http://server.example.com",
"sub": "248289761001",
"aud": "s6BhdRkqt3",
"nonce": "n-0S6_WzA2Mj",
"exp": 1311281970,
"iat": 1311280970,
"name": "Dalibor Kundrat",
"given_name": "Dalibor",
"family_name": "Kundrat",
"gender": "male",
"birthdate": "0000-10-31",
"email": "d.kundrat@example.com",
"picture": "http://example.com/somepicture_of_dalibor.jpg"
}
OpenId
流程示例:
有几种流程可供使用。您可以在此文章中OpenId
阅读更多相关信息。
每个OpenId
服务器根据规范提供多个端点进行交互。
可以使用全局发现端点 (global discovery endpoint)探索所有端点的 URL 。通常称为disco。它位于路径:并返回与指定授权服务器相关的/.well-known/openid-configuration
JSON OpenID Connect元数据。
迪斯科响应示例:
{
"issuer":"https://localhost:5001",
"jwks_uri":"https://localhost:5001/.well-known/openid-configuration/jwks",
"authorization_endpoint":"https://localhost:5001/connect/authorize",
"token_endpoint":"https://localhost:5001/connect/token",
"userinfo_endpoint":"https://localhost:5001/connect/userinfo",
"end_session_endpoint":"https://localhost:5001/connect/endsession",
"check_session_iframe":"https://localhost:5001/connect/checksession",
"revocation_endpoint":"https://localhost:5001/connect/revocation",
"introspection_endpoint":"https://localhost:5001/connect/introspect",
"device_authorization_endpoint":"https://localhost:5001/connect/deviceauthorization",
"frontchannel_logout_supported":true,
"frontchannel_logout_session_supported":true,
"backchannel_logout_supported":true,
"backchannel_logout_session_supported":true,
"scopes_supported":["profile","openid","email","role", "offline_access" //etc..],
"claims_supported":["name","family_name","profile","email", etc..],
"grant_types_supported":["authorization_code","client_credentials", "refresh_token", //etc..],
"response_types_supported":["code","token", "id_token","id_token token" //etc..],
"response_modes_supported":["form_post","query","fragment"],
"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post" ],
"id_token_signing_alg_values_supported":["RS256"],
"subject_types_supported":["public" ],
"code_challenge_methods_supported":["plain","S256"],
"request_parameter_supported":true,
"request_object_signing_alg_values_supported":["RS256","RS384" //etc..],
"authorization_response_iss_parameter_supported":true
}
主要 OpenId 端点:
/authorization_endpoint
- 与资源所有者交互并获得授权许可。/token_endpoint
- 通过提供授权许可(代码)或刷新令牌获取访问和/或ID令牌/revocation_endpoint
- 撤销访问或刷新令牌。/end_session_endpoint
- 结束与指定 ID 令牌关联的会话。/userinfo_endpoint
- 提供有关经过身份验证的最终用户的信息。
⠀
注意:发现端点中的所有值均指当前服务器配置。您可以在 idnetityserver 配置期间调整或启用/禁用代码中的某些选项
。⠀
前端后端模式(BFF)
BBF 是特定前端应用程序使用的后端。
由于端点 API 可能有多个具有不同请求的客户端,因此 BFF 可以提供特定于客户端的后端中介,并充当将多个请求转发和合并到不同服务 API 的代理。
⠀
好的,我们有 Cookie、Token 和 Session。我们将它们用于各种身份验证/授权协议(OpenId、OAuth 等),那么 hack BFF 有什么用处呢?答案是:
- 安全原因
- 架构原因
安全原因
近年来,用 Javascript(React、Angular、Vue……)为 SPA 实现 OpenID Connect 很常见,现在不再推荐这样做:
- 在浏览器中使用访问令牌比使用安全 cookie 具有更多的安全风险。
- SPA 是一个公共客户端,无法保守秘密,因为这样的秘密将成为 JavaScript 的一部分,任何检查源代码的人都可以访问。
- 最近浏览器为了防止跟踪而做出的改变可能会导致“第三方 cookie”被删除。
- 不可能将某些内容长期安全地存储在浏览器中,因为它可能会被各种攻击窃取。
由于上述问题,SPA 的最佳安全建议是避免在浏览器中存储令牌,并创建一个轻量级后端来帮助完成此过程,称为前端模式的后端(BFF)。
这样,您仍然可以使用它acces_tokens
来授权访问所有 API,但在移动设备的情况下,客户端将使用会话 cookie 或令牌,而 BFF 将代理这一方。
BFF 可以是:
statefull
- 将令牌存储在内存中并使用会话来管理它们。stateless
- 将令牌存储在仅加密的 HTTP、同一页面 cookie 中。
建筑原因
在设计应用程序时,您可以选择多种方式从客户端(Web/移动/外部)访问 API。
1) 为所有客户端提供单一 API 的单一 API 网关
2) 为每种类型的客户端提供 API 的单一 API 网关
3) 为每个客户端提供 API 的每个客户端 API 网关。
BFF 与 API 网关
虽然是所有客户端进入系统的API Gateway
单一入口点BFF
,但 仅负责单一类型的客户端。
BBF cookies 终止和令牌隔离
正如文中提到的,最重要的是:
- 避免在浏览器中存储令牌。(浏览器策略中没有令牌)。
- 在服务器端存储令牌并使用加密/签名的仅 HTTP cookie。
推荐使用 BFF 模式来保护 SPA 前端:
- 使用此功能,从 SPA 前端到授权服务器的所有通信现在都通过 BFF,并且令牌不会到达 SPA。
- BFF 现在会发出会话 cookie。这些 cookie 是 API 请求的一部分,并在代理级别交换访问令牌。
- 客户端 cookie 由 BFF 代理终止。
下一步是什么?
下次我将向大家展示BFF的具体实现。
存储库
您可以在开源 Github 仓库中找到该应用程序的完整源代码,包括身份、分布式日志记录、跟踪和监控:
https://github.com/damikun/trouble-training
鏂囩珷鏉ユ簮锛�https://dev.to/damikun/web-app-security-understanding-the-meaning-of-the-bff-pattern-i85