HS* JWT算法)示例RS* , PS* , ES*和EdDSA算法)示例boring板条箱围绕编译问题进行工作RUST的新的JWT(JSON Web令牌)实现,重点是简单,同时避免了常见的JWT安全性陷阱。
jwt-simple未经封闭,并支持所有通常部署的身份验证和签名算法:
| JWT算法名称 | 描述 |
|---|---|
HS256 | HMAC-SHA-256 |
HS384 | HMAC-SHA-384 |
HS512 | HMAC-SHA-512 |
BLAKE2B | Blake2B-256 |
RS256 | 带有PKCS#1V1.5填充 / SHA-256的RSA |
RS384 | 带有PKCS#1V1.5填充 / SHA-384的RSA |
RS512 | 带PKCS#1V1.5填充 / SHA-512的RSA |
PS256 | 带PSS填充 / SHA-256的RSA |
PS384 | 带PSS填充 / SHA-384的RSA |
PS512 | 带PSS填充 / SHA-512的RSA |
ES256 | ECDSA超过P256 / SHA-256 |
ES384 | p384 / sha-384的ECDSA |
ES256K | ecdsa超过secp256k1 / sha-256 |
EdDSA | ED25519 |
可以将jwt-simple从开箱即用到WebAssembly/Wasi。它与快速计算服务完全兼容。
重要的是:JWT的目的是验证数据是由知道秘密钥匙的一方创建的。它没有提供任何形式的机密性:JWT数据简单地编码为base64,并且没有加密。
cargo.toml :
[ dependencies ]
jwt-simple = " 0.12 "锈:
use jwt_simple :: prelude :: * ;错误将以jwt_simple::Error值( thiserror Crate的Error类型的别名)返回。
HS* JWT算法)示例身份验证方案使用相同的密钥来创建和验证令牌。换句话说,双方都需要最终相互信任,否则验证者也可以创建任意令牌。
关键创建:
use jwt_simple :: prelude :: * ;
// create a new key for the `HS256` JWT algorithm
let key = HS256Key :: generate ( ) ;可以用key.to_bytes()将键导出为字节,并使用HS256Key::from_bytes()恢复。
代币创建:
/// create claims valid for 2 hours
let claims = Claims :: create ( Duration :: from_hours ( 2 ) ) ;
let token = key . authenticate ( claims ) ? ;- >完成!
let claims = key . verify_token :: < NoCustomClaims > ( & token , None ) ? ;- >完成!无需其他步骤。
密钥到期,开始时间,身份验证标签等将自动验证。如果身份验证标签对给定键无效,则使用JWTError::InvalidAuthenticationTag失败。
如有必要,可以在claims对象中检查完整的索赔。 NoCustomClaims意味着只有应用程序使用的标准索赔集,但也可以支持应用程序定义的索赔。
可以选择通过ValidationOptions结构启用额外的验证步骤:
let mut options = VerificationOptions :: default ( ) ;
// Accept tokens that will only be valid in the future
options . accept_future = true ;
// Accept tokens even if they have expired up to 15 minutes after the deadline,
// and/or they will be valid within 15 minutes.
// Note that 15 minutes is the default, since it is very common for clocks to be slightly off.
options . time_tolerance = Some ( Duration :: from_mins ( 15 ) ) ;
// Reject tokens if they were issued more than 1 hour ago
options . max_validity = Some ( Duration :: from_hours ( 1 ) ) ;
// Reject tokens if they don't include an issuer from that set
options . allowed_issuers = Some ( HashSet :: from_strings ( & [ "example app" ] ) ) ;
// see the documentation for the full list of available options
let claims = key . verify_token :: < NoCustomClaims > ( & token , Some ( options ) ) ? ;请注意, allowed_issuers和allowed_audiences不是字符串,而是字符串集(使用Rust Standard库中的HashSet类型),因为该应用程序可以允许多个返回值。
RS* , PS* , ES*和EdDSA算法)示例签名需要一个钥匙对:用于创建令牌和公共密钥的秘密密钥,只能验证它们。
如果双方最终不相互信任,例如客户和API提供商之间交换的代币,请务必使用签名方案。
关键创建:
use jwt_simple :: 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 ( ) ; use jwt_simple :: 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编码的数据。
RSA密钥对创建,使用openssl和PEM导入秘密密钥:
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板条箱):
# [ 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和算法都无法信任。这是JWT标准的不可修复的设计缺陷。
结果, algorithm应仅用于调试目的,而不选择关键类型。同样,应仅使用key_id在为同一算法制作的一组密钥中选择一个键。
最低限度,如果最初使用签名方案来创建令牌,则必须禁止使用HS*验证。
密钥标识符指示验证器应使用哪种公共密钥(或共享密钥)进行验证。它们可以随时将它们附加到现有的共享密钥,钥匙对和公共密钥上:
let public_key_with_id = public_key . with_key_id ( & "unique key identifier" ) ; jwt-simple也可以为现有密钥创建这样的标识符,而不是将其委派给应用程序:
let key_id = public_key . create_key_id ( ) ;这会为键创建文本编码的标识符,将其附加并返回。
如果标识符已连接到共享密钥或键对,则使用它们创建的令牌将包括在内。
jwt-simple包括减轻重播攻击的机制:
create_nonce()索赔功能创建Nonces并将其附加到新令牌上。验证过程稍后可以拒绝任何不包括预期nonce的令牌( required_nonce验证选项)。开发代码包括cwt货物功能,该功能可以实现CWT代币的实验解析和验证。
请注意,CWT不支持自定义索赔。所需的标识符尚未标准化。
同样,现有的JSON和CBOR避难所的生锈板条也不安全。一个不信任的一方可以发送一个需要大量内存和CPU的序列化对象。 JSON添加了创可贴,但是使用当前的生锈工具,对于CBOR来说,这将是棘手的。
作为缓解措施,我们强烈建议您拒绝在您的应用程序中太大的代币。可以通过max_token_length验证选项来完成。
boring板条箱围绕编译问题进行工作作为一个依赖关系之一( boring板条箱)的临时解决方法,可以编译该库以仅使用Rust实现。
为了这样做,请在货物配置中导入具有default-features=false, features=["pure-rust"] 。
不要无条件地这样做。这仅对于非常特定的设置和目标需要,并且只有在解决了boring箱子问题之前。在货物中配置此设置的方法也可能会在以后的版本中改变。
针对musl库的静态构建不需要解决方法。只需使用cargo-zigbuild来构建您的项目即可。
支持wasm32-freestanding目标(有时仍称为Rust中的wasm32-unknown-unknown )(如“ IT Compiles”)。
但是,强烈建议使用本机JavaScript实现。 JavaScript中有高质量的JWT实现,利用WebCrypto API,比WebAssembly模块提供了更好的性能和安全保证。
这个板条箱不是JWT的认可。 JWT是一种可怕的设计,也是“但这是标准”的众多示例之一,并不一定意味着它是好的。
如果您控制令牌创建和验证,我强烈建议您推荐帕斯托或饼干。
但是,JWT仍在行业中广泛使用,并且与流行的API沟通绝对是必不可少的。
该板条箱的设计为:
None签名方法外)。