#api #security #api-signing

api-signature

一个帮助简化API交易签名并更符合语义的库

2个不稳定版本

0.2.0 2022年4月21日
0.1.0 2022年1月31日

#986 in 密码学

MIT许可证

25KB
445 代码行

API签名

pipeline status coverage report

一个用于使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个变量 payloadsecret_keyurlnonce。这些可以通过使用签名库的 .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