我正在创建一个用户可以登录或注册的项目,但我刚刚发现了一个安全漏洞。
首先让我告诉流用户何时从服务器收到令牌,
用户可以注册或登录,如果所有验证都正确,则服务器将响应:
{
"meta": {
"issueDate": 1592078419167,
"expToken": "10800000"
},
"payload": {
"user": {
"id": 3,
"email": "new3@gmail.com",
"username": "new3"
},
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiZW1haWwiOiJuZXczQGdtYWlsLmNvbSIsInVzZXJuYW1lIjoibmV3MyIsImlhdCI6MTU5MjA3ODQxOSwiZXhwIjoxNTkyMDg5MjE5fQ.cjLgQjc2QbIhgumG2X2VsLk70c58F7NrKboYL2F2Sa0"
}
}
然后使用该令牌,他可以通过将其作为架构附加到标头Authorization: 'bearer TOKEN'
来执行 CRUD 操作。使用令牌后,服务器中的中间件会检查它是否被篡改或过期(我在这里使用 JWT),如果它已过期或被篡改,然后服务器响应401 unauthorized
.
不过我注意到的是这个。假设我有USERS
和POSTS
表(1:N)。User1 有 2 个 ID 分别为 1 和 2 的帖子,然后另一个用户注册,让我们称他为 User2,User2 注册成功收到令牌然后转到终端使用 curl,向他不拥有api/post/1/edit
路由发送请求。这是我的问题。了解互联网/API 工作原理的用户只需获取令牌并访问任何资源即可。
我脑子里几乎没有解决方案。
创建一个中间件,通过查询数据库来检查用户是否拥有资源。但我不认为这是一个高性能的解决方案,因为我必须在不同的控制器(如users_controller、posts_controller another_controller)上进行查询和执行。
我将使用 UUID 而不是递增 ID
api/post/1akA13124S5129/edit
<---,因此每当他们尝试访问帖子编辑路线时,他们都无法轻松找出 ID。
PS:我在Facebook上尝试过,但它不起作用。我认为他们有很多令牌和饼干。顺便说一句,我现在不在任何请求中使用/包含任何 cookie。
您已经设计了两个常见的解决方案。第一个是最简单和最常见的。您每秒处理多少个请求,您看到性能问题?在构建更复杂的解决方案之前,我将首先研究如何优化数据库查询。
但在某些情况下,第二种解决方案("稀疏"标识符)非常方便。我一直在使用它们。唯一重要的是,这意味着知道标识符意味着访问。因此,您可能不能拥有像api/post/1akA13124S5129/edit
这样的 URL。"读取但不编辑"的 URL 会是什么样子?它不能包含"1akA13124S5129",因为知道该标识符的任何人都可以通过添加/edit
来编辑帖子。
我最喜欢的使用"知道标识符授予访问权限"的方法是使用数据的 SHA-256 哈希作为标识符。这意味着为了猜测标识符,您需要已经知道内容。这是处理图像等内容的非常好的方法。但它不适用于您想要编辑的内容,就像您在这里所做的那样。
如果你真的不能做数据库查找(如果没有,你真的应该扩展这个问题来解释你的问题),那么还有许多其他方法,但它们在很大程度上取决于你的精确要求。例如:
- 如果用户可以编辑自己的帖子,但任何人都可以阅读任何帖子,则让 URL 定义权限。例如:
api/post/alice/1
只能由 Alice 编辑。 - 不要仅将 JWT 用于身份验证 (authC),还要将其用于授权 (authZ)。例如,当您要访问
api/post/1/edit
时,首先您必须连接到 authZ 服务器并请求 JWT。该服务器将执行数据库查找,然后专门为该 URL 授予 JWT(或者您可以批处理一组 URL,或像"管理员"这样的权限组)。然后,您可以多次重复使用该 JWT,并且系统的资源服务端不必重新验证您的访问权限。它可以依赖 JWT,直到它过期。这可以减轻数据库的大量负载。
但是,除非您的系统非常大,并且您已经尽了一切努力来优化数据库查询,否则我只会查询数据库。这就是他们的目的。
绝对是第一名。这就是Facebook,StackExchange,Google,Microsoft和大多数流行网站用来保护其API的方法。
我将使用 UUID,因此每当他们尝试访问帖子编辑路线时
他们最初是什么时候得到这些UUID的?实质上是为每个资源生成一个令牌。这样,当用户只想查看今天的帖子时,您的后端最终仍将查询数据库以生成/选择每个帖子的 UUID,即使用户不太可能更新他们正在寻找的每个帖子。哦,你想延迟 UUID 直到用户实际调用编辑路由吗?由于用户仍然需要首先获取该UUID,因此它只是一个额外的"将UUID传递给用户"步骤,不会增加任何安全性。
那么,如果资源不是完全秘密的并且只能供一个用户使用,这也将成为令人头疼的问题。在Facebook上,个人帖子只能由提交者删除/编辑,但其他人可以对其进行评论或做出反应。由于Facebook需要跟踪谁对什么做出反应,因此他们最终仍将检查身份验证令牌并与用户表相关联。同时,您的UUID方法最终会为每个不同的用户创建多个UUID,因此,如果说一个帖子被数百个用户看到,您最终会为该帖子存储数百个UUID。然后考虑评论(可以由评论者和帖子创建者删除),页面/组帖子(可以由页面/组管理员删除),甚至这个网站,任何人都可以编辑问题和答案。