2个不稳定版本
0.2.0 | 2022年4月21日 |
---|---|
0.1.0 | 2022年1月31日 |
#986 in 密码学
25KB
445 代码行
API签名
一个用于使API签名更符合语义并减少实现开销的库。该库帮助构建所需的API签名结构,并确保所有使用此库的服务将进行相同的验证。
前言
今天,许多系统都在使用登录和API密钥来保护公共API。不幸的是,这并不正确;当用户获得授权时,系统会在客户端和服务器之间发送一个授权令牌。如果这些令牌被窃取,恶意攻击者就可以代表有效用户解析有效负载。API签名可以帮助缓解这个问题。
一次性令牌
一次性令牌是一个持续增加的单一ID,这意味着永远不能使用相同的ID,并且它总是需要大于上一个ID。此ID绑定到当前的API密钥,并用于混淆服务器和客户端之间发送的信息。
一次性令牌锁定安全系统
如果使用无效的一次性令牌,应阻止API密钥,并且系统必须要求创建新的API密钥或用户必须解锁密钥。一次性令牌将通过确保攻击者无法发送伪造的有效负载来保护系统,并且在有人成功尝试这样做的情况下,有很高的可能性最初使用API密钥的应用程序最终会阻止它。
示例
攻击者可能会使用一个很大的数字作为一次性令牌,导致另一个客户端发送一个更小的数字并被拒绝 - 阻止密钥。此时,被盗的令牌将不再有效,进一步的非法使用将被拒绝。
##用法
定义API结构
API结构可以通过程序生成或从配置文件导入。有多个可用的签名操作器
操作器 | 函数 | 描述 |
---|---|---|
HMAC SHA256 | HmacSha256(Box<SignCal>[API加密密钥], Box<SignCal>[数据]) | 这将使用API密钥和数据创建SHA256 HMAC[^1] |
HMAC SHA512 | HmacSha512(Box<SignCal>[API加密密钥], Box<SignCal>[数据]) | 这将使用API密钥和数据创建SHA512 HMAC[^1] |
SHA256 | Sha256(Box<SignCal>) | 以SHA256格式编码数据 |
SHA512 | Sha512(Box<SignCal>) | 以SHA512格式编码数据 |
Base64编码器 | Base64Encode(Box<SignCal>) | 以Base64格式编码 |
Base64解码器 | Base64Decode(Box<SignCal>) | 解码Base64 |
Base58编码器 | Base58Encode(Box<SignCal>) | 以Base58格式编码 |
Base58解码器 | Base58Decode(Box<SignCal>) | 解码Base58 |
数据附加 | 追加(<SignCal> Vec) | 合并数据 |
字符串连接 | JoinAsString(<SignCal> Vec) | 将内部数据作为字符串处理并连接它们 |
从原始数据变量 | VarData(String) | 每个签名都会传递一组变量,这是定义预期数据为原始数据的变量的地方 &[u8] |
从字符串变量 | VarString(String) | 每个签名都会传递一组变量,这是定义预期数据为文本 `string` 的变量的地方 |
从整数变量 | VarInteger(String) | 每个签名都会传递一组变量,这是定义预期数据为数字 `i32` 的变量的地方 |
原始数据 | Raw(<u8> Vec) | 这不是一个变量,而是直接插入的数据。这可以用于配置中的盐值 |
[^1]: HMAC:基于哈希的消息认证码
配置
在此配置示例中,我们使用库提供的默认配置。
let config = Base64Encode(
HmacSha512(
Base64Decode(VarString("secret_key".to_string()).into()).into(),
Append(vec![
VarString("url".to_string()),
Sha256(
Append(vec![
VarInteger("nonce".to_string()),
JoinAsString(vec![
Raw("nonce=".to_string().into_bytes()),
VarInteger("nonce".to_string()),
Raw("&".to_string().into_bytes()),
VarString("payload".to_string()),
]),
])
.into(),
),
])
.into(),
)
);
此配置需要4个变量 payload
、secret_key
、url
和 nonce
。这些可以通过使用签名库的 .var([key], [data])
来设置。
常见用法
let nonce = 1616492376594usize;
let config = Base64Encode(
HmacSha512(
Base64Decode(VarString("secret_key".to_string()).into()).into(),
Append(vec![
VarString("url".to_string()),
Sha256(
Append(vec![
VarInteger("nonce".to_string()),
JoinAsString(vec![
Raw("nonce=".to_string().into_bytes()),
VarInteger("nonce".to_string()),
Raw("&".to_string().into_bytes()),
VarString("payload".to_string()),
]),
])
.into(),
),
])
.into(),
)
);
let mut signature = Signature::default();
signature.var("payload", format!("nonce={}&ordertype=limit&pair=XBTUSD&price=37500&type=buy&volume=1.25",nonce))
.var("secret_key", "kQH5HW/8p1uGOVjbgWA7FunAmGO8lsSUXNsu3eow76sz84Q18fWxnyRzBHCd3pd5nE9qa99HAZtuZuj6F1huXg==")
.var("url", "/0/private/AddOrder")
.nonce(Arc::new(move || -> Vec<u8> {nonce.to_string().as_bytes().to_vec()}))
.config();
let api_sign = b"4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ==".to_vec();
assert_eq!(api_sign, signature.sign());
依赖关系
~21-35MB
~580K SLoC