更新:
对于任何对 JWT 感兴趣的人,我已将此链接添加到我的其他答案中,如何在此处为 ASP.NET Web API 使用 JWT 身份验证。
我们已经设法将 HMAC 身份验证应用于保护 Web API,并且效果很好。HMAC 身份验证为每个消费者使用一个密钥,消费者和服务器都知道对消息进行 hmac 哈希处理,应使用 HMAC256。大多数情况下,消费者的哈希密码被用作密钥。
消息通常由 HTTP 请求中的数据构建,甚至是添加到 HTTP 标头的自定义数据,消息可能包括:
- 时间戳:发送请求的时间(UTC 或 GMT)
- HTTP 动词:GET、POST、PUT、DELETE。
- 发布数据和查询字符串,
- 网址
在引擎盖下,HMAC 身份验证将是:
消费者向 Web 服务器发送 HTTP 请求,在构建签名(hmac 哈希的输出)后,HTTP 请求的模板:
User-Agent: {agent}
Host: {host}
Timestamp: {timestamp}
Authentication: {username}:{signature}
GET 请求示例:
GET /webapi.hmac/api/values
User-Agent: Fiddler
Host: localhost
Timestamp: Thursday, August 02, 2012 3:30:32 PM
Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=
要散列以获取签名的消息:
GET\n
Thursday, August 02, 2012 3:30:32 PM\n
/webapi.hmac/api/values\n
带有查询字符串的 POST 请求示例(下面的签名不正确,仅作为示例)
POST /webapi.hmac/api/values?key2=value2
User-Agent: Fiddler
Host: localhost
Content-Type: application/x-www-form-urlencoded
Timestamp: Thursday, August 02, 2012 3:30:32 PM
Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=
key1=value1&key3=value3
要散列以获取签名的消息
GET\n
Thursday, August 02, 2012 3:30:32 PM\n
/webapi.hmac/api/values\n
key1=value1&key2=value2&key3=value3
请注意,表单数据和查询字符串应按顺序排列,因此服务器上的代码获取查询字符串和表单数据以构建正确的消息。
当 HTTP 请求到达服务器时,实现一个身份验证动作过滤器来解析请求以获取信息:HTTP 动词、时间戳、uri、表单数据和查询字符串,然后基于这些构建带有密钥的签名(使用 hmac 哈希)服务器上的密钥(散列密码)。
密钥是使用请求中的用户名从数据库中获取的。
然后服务器代码将请求上的签名与构建的签名进行比较;如果相等,则认证通过,否则,认证失败。
构建签名的代码:
private static string ComputeHash(string hashedPassword, string message)
{
var key = Encoding.UTF8.GetBytes(hashedPassword.ToUpper());
string hashString;
using (var hmac = new HMACSHA256(key))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
hashString = Convert.ToBase64String(hash);
}
return hashString;
}
那么,如何防止重放攻击呢?
为时间戳添加约束,例如:
servertime - X minutes|seconds <= timestamp <= servertime + X minutes|seconds
(servertime:请求到达服务器的时间)
并且,将请求的签名缓存在内存中(使用MemoryCache,要在时限内保存)。如果下一个请求与前一个请求具有相同的签名,它将被拒绝。
演示代码放在这里:
https ://github.com/cuongle/Hmac.WebApi