我想实现一个 API 密钥系统来保护对我的应用程序的 API 调用。
我认为可行的方式是每个帐户都有一个私钥/秘密。每个请求都包含时间、帐户 ID 和哈希(时间+秘密)。
然后,服务器可以对数据库中的用户密码执行相同的操作,并根据客户端发送的哈希值进行检查。
这是一个合理的方法吗?它对蛮力攻击是开放的,但我认为只要秘密很长(即uuid),它就不应该成为太大的问题......
一个想法
任何人都可以同时提交另一个请求并进行哈希处理,并最终被接受,对吧?
问题是可以重放随机数+哈希。真正的身份验证协议至少需要两条消息:
Server Client
---->challenge --->
<----response------
例如,挑战可能是由服务器提供的随机数,而客户端的响应将是密码与随机数的哈希值。
不幸的是,这需要state,而 RESTful 协议的全部问题是它们不想要保持状态的麻烦。然而他们想要验证...
所以你真的有三个选择:
方案一:假装问题不存在,使用无状态的“认证”协议。这与使用 cookie 没有什么不同。nonce + password-hash 并不比 cookie 更安全。Cookies 可以被盗等,并被重放。整个网络现在都受到这些重放攻击的困扰。
选项 2:尝试将身份验证协议绑定到无状态通信方法上。在这里,您将让客户端向您发送 UTC 时间戳而不是随机数。时间戳的使用提供了对重放的有限防御。显然,您的时钟不会与客户端的时钟同步,因此您的服务器将允许在某个误差范围内的任何时间戳,并且该误差范围将是身份验证协议的重放范围。请注意,这违反了 REST,因为身份验证消息不是幂等的。幂等意味着“可以被攻击者成功重放”。
选项 3:不要尝试将身份验证协议绑定到无状态协议上。使用 SSL。使用客户端证书。不要让客户端下载字符串,而是让他们生成证书,或者您可以为他们提供密钥对。它们通过 SSL 进行身份验证,并且不在您的 REST 层中进行身份验证。SSL 有很多“开销”。它不是轻量级的,正是因为它确实解决了这些重播问题。
因此,归根结底,这取决于您对 API 访问的重视程度。
对于仅检索数据(私有数据除外)而不是创建、修改或删除数据的 API,此答案中的选项 1 可能就足够了。例如,请参阅Bing Maps REST API 和Google Maps Premier 网络服务(在这里,Google Maps 还使用数字签名和只有 API 用户知道的特殊密钥对 URL 进行哈希处理,同时提供防止修改 URL ,显然仍然不提供重放攻击保护)。
事实上,某些检索数据的 API 不使用 API 密钥,而是以其他方式限制访问(例如,YouTube API 允许在无需身份验证的情况下检索视频和用户频道的公开可用数据,但限制最近要求)。
对于不仅仅检索公开可用数据的 API 需要选项 2 和/或 3,例如,如果它修改用户配置文件、发布内容或访问私人信息:请参见YouTube 数据 API 身份验证页面,其中OAuth 作为一种可能的身份验证方案被提及。
特别是对于选项 1,此处的 API 密钥用于跟踪用户对您的 API 的访问,最重要的是,限制这些用户的访问。选项 1 可能不适用于允许无限制数据访问的 API。
(这是一个答案,因为评论太长了。)
服务器包含:
客户端发送:
当客户端调用服务器时,服务器创建密码哈希(它自己知道)+随机字符串(通过调用客户端在 GET 中给出)的哈希,并评估是否匹配哈希(通过调用客户端在 GET 中给出)
更好的是创建 1 个函数,该函数从(密码哈希 + 随机数)生成秘密哈希,其中“随机数”(随机数)也存储在服务器上。然后可以使用用户名+密码调用服务器一次,返回秘密哈希;然后让后续调用仅依赖于用户名 + 随机字符串 + (秘密哈希 + 随机字符串)的哈希,使用与上述相同的方法,但秘密是当时的密码。这样,即使你的秘密被截获和逆转,你的通行证仍然是安全的。
显然,好的散列算法:没有 rot13 甚至只有 md5 是有问题的。