8 个稳定版本
1.4.2 | 2023 年 10 月 10 日 |
---|---|
1.4.1 | 2023 年 3 月 9 日 |
1.4.0 | 2023 年 2 月 27 日 |
1.2.1 | 2023 年 1 月 31 日 |
1.1.0 | 2022 年 10 月 31 日 |
#185 in 加密学
每月 21 次下载
92KB
2K SLoC
keygate-jwt
一个新的 JWT (JSON Web Tokens) 实现,专注于简洁性,同时避免常见的 JWT 安全陷阱。
keygate-jwt
有观点,并且仅支持安全的签名算法
JWT 算法名称 | 功能 | 描述 |
---|---|---|
EdDSA |
eddsa |
Ed25519 (推荐) |
ES256 |
ecdsa |
ECDSA over p256 / SHA-256 |
ES384 |
ecdsa |
ECDSA over p384 / SHA-384 |
ES256K |
ecdsa |
ECDSA over secp256k1 / SHA-256 |
尽可能使用 EdDSA
,但是并非所有 JWT 库都支持它,因此也支持 ecdsa
。
keygate-jwt
使用纯 Rust 实现并可以直接编译为 WebAssembly/WASI。
重要提示:JWT 的目的是验证数据是由知道密钥的实体创建的。它不提供任何机密性:JWT 数据仅以 BASE64 编码,并且未加密。
用法
cargo.toml
:
[dependencies]
keygate-jwt = "1.0"
错误作为 keygate-jwt::Error
值返回
签名
签名需要一个密钥对:用于创建令牌的密钥和只能验证它们的公钥。
当双方最终不信任对方时,始终使用签名方案,例如客户端和 API 提供商之间交换的令牌。
密钥对和令牌创建
密钥创建
ES256
use keygate_jwt::prelude::*;
// create a new key pair for the `ES256` JWT algorithm
let key_pair = ES256KeyPair::generate();
// a public key can be extracted from a key pair:
let public_key = key_pair.public_key();
ES384
use keygate_jwt::prelude::*;
// create a new key pair for the `ES384` JWT algorithm
let key_pair = ES384KeyPair::generate();
// a public key can be extracted from a key pair:
let public_key = key_pair.public_key();
可以将密钥作为字节导出以供以后重用,或从字节导入,对于 RSA,可以从单个参数、DER 编码或 PEM 编码的数据导入。
使用 OpenSSL 和 PEM 导入私钥创建 RSA 密钥对
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
let key_pair = RS384KeyPair::from_pem(private_pem_file_content)?;
let public_key = RS384PublicKey::from_pem(public_pem_file_content)?;
令牌创建和验证与 HS*
算法相同,只是使用密钥对创建令牌,并使用相应的公钥进行验证。
令牌创建
/// create claims valid for 2 hours
let claims = Claims::create(Duration::from_hours(2));
let token = key_pair.sign(claims)?;
令牌验证
let claims = public_key.verify_token::<NoCustomClaims>(&token, None)?;
可用的验证选项与对称算法中使用的选项相同。
高级用法
自定义声明
声明对象默认支持所有标准声明,并且可以直接或通过便捷的帮助函数进行设置。
let claims = Claims::create(Duration::from_hours(2)).
with_issuer("Example issuer").with_subject("Example subject");
您还可以定义自己的声明。这些声明必须存在于可序列化类型中(这需要serde
crate)
#[derive(Serialize, Deserialize)]
struct MyAdditionalData {
user_is_admin: bool,
user_country: String,
}
let my_additional_data = MyAdditionalData {
user_is_admin: false,
user_country: "FR".to_string(),
};
使用自定义数据创建声明
let claims = Claims::with_custom_claims(my_additional_data, Duration::from_secs(30));
使用自定义数据验证声明。注意自定义数据类型的存在
let claims = public_key.verify_token::<MyAdditionalData>(&token, None)?;
let user_is_admin = claims.custom.user_is_admin;
在验证之前查看元数据
在标签或签名验证之前,属性如密钥标识符可能有助于从一组中选择正确的密钥。
let metadata = Token::decode_metadata(&token)?;
let key_id = metadata.key_id();
let algorithm = metadata.algorithm();
// all other standard properties are also accessible
重要:您不能信任密钥ID,也不能信任算法
因此,algorithm
应仅用于调试目的,永远不要用于选择密钥类型。同样,key_id
应仅用于在具有相同算法的一组密钥中选择密钥。
针对重放攻击的缓解措施
keygate-jwt
包含缓解重放攻击的机制
- 可以通过
with_nonce()
声明函数将nonce附加到新的令牌。验证程序可以稍后拒绝任何不包括预期nonce(required_nonce
验证选项)的令牌。 - 验证程序可以拒绝创建时间过长(无论其到期日期如何)的令牌。这可以防止恶意(或受损)的签名者使用的令牌过长时间。
- 验证程序可以拒绝在特定日期之前创建的令牌。对于特定用户,可以将最后一次成功的认证日期存储在数据库中,稍后与该选项一起使用,以拒绝更旧的(重放的)令牌。
为什么还需要另一个 JWT crate
已经有一些针对Rust的JWT crate,但它们都没有满足我们的需求
- 不支持不安全的算法(如
RSA
或HS256
)和哈希函数(如SHA1
) - 最小化、仅Rust的依赖项
致谢
此crate基于Frank Denis的jwt-simple项目。显著变化包括引入cargo功能标志和不需要的依赖项,以及删除对不安全算法的支持。1
依赖项
~1.2–3MB
~66K SLoC