更新:
我已经把这个链接添加到我的另一个如何为ASP使用JWT身份验证的答案中。NET Web API,对JWT感兴趣的人可以在这里找到。
我们已经成功地将HMAC身份验证应用于安全的Web API,并且运行良好。HMAC身份验证为每个消费者使用一个密钥,消费者和服务器都知道这个密钥来HMAC哈希消息,应该使用HMAC256。在大多数情况下,使用者的散列密码被用作密钥。
消息通常由HTTP请求中的数据构建,或者甚至是添加到HTTP头中的自定义数据,消息可能包括:
时间戳:发送请求的时间(UTC或GMT)
HTTP动词:GET, POST, PUT, DELETE。
发布数据和查询字符串,
URL
在引擎盖下,HMAC身份验证将是:
消费者向web服务器发送一个HTTP请求,在构建签名(hmac hash的输出)后,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