Rob,不确定你在哪里找到这个问题,但想加我的 2 美分,以防其他人遇到这个问题。
几个月前我或多或少有同样的问题,并且在一年的大部分时间里听说过“OAuth”。我正在开发一个我需要保护的 REST API,所以我开始阅读有关 OAuth 的内容......然后我的眼睛开始在我的脑海中向后滚动。
我可能给了它一两天的浏览和阅读,直到我决定,就像你一样,OAuth 是令人困惑的垃圾并放弃了它。
因此,我开始研究一般保护 API 的方法,并开始更好地掌握如何做到这一点。最流行的方法似乎是向 API 发送请求以及整个消息的校验和(使用只有您和服务器知道的秘密编码),服务器可以使用该校验和来确定消息是否在途中被篡改来自客户端,如下所示:
- 客户端发送 /user.json/123?showFriends=true&showStats=true&checksum=kjDSiuas98SD987ad
- 服务器得到所有这些,在数据库中查找用户“123”,加载他的密钥,然后(使用客户端使用的相同方法)在给定请求参数的情况下重新计算它的 OWN 校验和。
- 如果服务器生成的校验和与客户端发送的校验和匹配,则请求成功并执行,否则认为被篡改并拒绝。
校验和称为 HMAC,如果您想要一个很好的例子,它就是 Amazon Web Services 使用的(尽管他们称参数为“签名”而不是“校验和”)。
因此,鉴于此工作的关键组件之一是客户端和服务器必须以相同的方式生成 HMAC(否则它们将不匹配),必须有关于如何组合所有参数的规则。 . 然后我突然从 OAuth 中理解了所有“参数的自然字节排序”的废话......它只是定义了如何生成签名的规则,因为它需要。
另一点是,您在 HMAC 生成中包含的每个参数都是一个值,当您发送请求时无法篡改。
因此,如果您只是将 URI 词干编码为签名,例如:
- /user.json == askJdla9/kjdas+Askj2l8add
那么您的消息中唯一不能被篡改的是 URI,所有参数都可以被篡改,因为它们不是服务器将重新计算的“校验和”值的一部分。
或者,即使您在计算中包含每个参数,您仍然面临“重放攻击”的风险,其中恶意中间人或被窃听者可以拦截 API 调用,然后一遍又一遍地将其重新发送到服务器。
您也可以通过在 HMAC 计算中添加时间戳(始终使用 UTC)来解决此问题。
提醒:由于服务器需要计算相同的 HMAC,因此您必须发送您在计算中使用的任何值,除了您的密钥(我认为 OAuth 将其称为 consumer_secret)。因此,如果您添加时间戳,请确保在您的请求中发送时间戳参数。
如果要使 API 免受重放攻击,可以使用 nonce 值(它是服务器生成的 1 次使用值,提供给客户端,客户端在 HMAC 中使用它,发回请求,服务器确认然后在数据库中将该 nonce 值标记为“已使用”,并且永远不会让另一个请求再次使用它)。
注意:“nonce”是解决“重放攻击”问题的一种非常精确的方法——时间戳很好,但是因为计算机并不总是有同步的时间戳值,所以你必须在服务器端允许一个可接受的窗口在我们接受或拒绝之前,请求可能有多“旧”(比如 10 分钟、30 分钟、1 小时……亚马逊使用 15 分钟)。在这种情况下,您的 API 在整个时间窗口内都存在技术漏洞。
我认为 nonce 值很棒,但只需要在对它们保持完整性至关重要的 API 中使用。在我的 API 中,我不需要它,但是如果用户需要它,以后添加它会很简单......我实际上只需要在我的数据库中添加一个“nonce”表,向客户端公开一个新的 API,例如:
然后当他们在 HMAC 计算中将其发回给我时,我需要检查数据库以确保它之前从未使用过并且曾经使用过,在数据库中将其标记为这样,这样如果请求再次出现同样的随机数我会拒绝它。
概括
无论如何,长话短说,我刚才描述的一切基本上都是所谓的“2-legged OAuth”。没有向权威机构(Twitter、Facebook、谷歌等)授权客户端的额外步骤,该步骤被删除,而是服务器隐式信任客户端,如果他们发送的 HMAC 匹配。这意味着客户端拥有正确的 secret_key 并正在使用它签署它的消息,因此服务器信任它。
如果您开始在网上四处寻找,这似乎是当今保护 API 方法或类似方法的首选方法。亚马逊几乎完全使用这种方法,只是他们在对整个事物进行签名以生成 HMAC 之前对其参数使用了稍微不同的组合方法。
如果您有兴趣,我会在学习过程中写下整个旅程和思考过程。这可能有助于为这个过程提供一个有指导的思维导览。