HS* jwt)RS* , PS* , ES* et EdDSA Algorithms)boringUne nouvelle implémentation JWT (JSON Web Tokens) pour Rust qui se concentre sur la simplicité, tout en évitant les pièges communs de sécurité JWT.
jwt-simple n'est pas opposé et prend en charge tous les algorithmes d'authentification et de signature couramment déployés:
| Nom de l'algorithme JWT | Description |
|---|---|
HS256 | HMAC-SHA-256 |
HS384 | HMAC-SHA-384 |
HS512 | HMAC-SHA-512 |
BLAKE2B | Blake2b-256 |
RS256 | RSA avec PKCS # 1V1.5 Padding / SHA-256 |
RS384 | RSA avec PKCS # 1V1.5 Padding / SHA-384 |
RS512 | RSA avec PKCS # 1V1.5 Padding / SHA-512 |
PS256 | RSA avec rembourrage PSS / SHA-256 |
PS384 | RSA avec padding PSS / SHA-384 |
PS512 | RSA avec rembourrage PSS / SHA-512 |
ES256 | ECDSA sur P256 / SHA-256 |
ES384 | ECDSA sur P384 / SHA-384 |
ES256K | ECDSA sur SECP256K1 / SHA-256 |
EdDSA | ED25519 |
jwt-simple peut être compilé hors de la boîte à WebAssembly / Wasi. Il est entièrement compatible avec un service de calcul rapide.
Important: le but de JWT est de vérifier que les données ont été créées par une partie connaissant une clé secrète. Il ne fournit aucune sorte de confidentialité: les données JWT sont simplement codées sous forme de base64 et ne sont pas cryptées.
cargo.toml :
[ dependencies ]
jwt-simple = " 0.12 "Rouiller:
use jwt_simple :: prelude :: * ; Les erreurs sont renvoyées en tant que valeurs jwt_simple::Error (Alias pour le type Error de la caisse thiserror ).
HS* jwt)Les schémas d'authentification utilisent la même clé pour créer et vérifier les jetons. En d'autres termes, les deux parties doivent finalement se faire confiance, sinon le vérificateur pourrait également créer des jetons arbitraires.
Création clé:
use jwt_simple :: prelude :: * ;
// create a new key for the `HS256` JWT algorithm
let key = HS256Key :: generate ( ) ; Une clé peut être exportée sous forme d'octets avec key.to_bytes() et restaurée avec HS256Key::from_bytes() .
Création de jetons:
/// create claims valid for 2 hours
let claims = Claims :: create ( Duration :: from_hours ( 2 ) ) ;
let token = key . authenticate ( claims ) ? ;-> Terminé!
let claims = key . verify_token :: < NoCustomClaims > ( & token , None ) ? ;-> Terminé! Aucune étape supplémentaire requise.
L'expiration des clés, l'heure de début, les balises d'authentification, etc. sont automatiquement vérifiées. La fonction échoue avec JWTError::InvalidAuthenticationTag si la balise d'authentification n'est pas valide pour la clé donnée.
L'ensemble complet des réclamations peut être inspecté dans l'objet claims si nécessaire. NoCustomClaims signifie que seul l'ensemble standard des réclamations est utilisé par la demande, mais les réclamations définies par l'application peuvent également être prises en charge.
Les étapes de vérification supplémentaires peuvent éventuellement être activées via la structure 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 ) ) ? ; Notez que allowed_issuers et allowed_audiences ne sont pas des chaînes, mais des ensembles de chaînes (en utilisant le type HashSet à partir de la bibliothèque Standard Rust), car l'application peut permettre plusieurs valeurs de retour.
RS* , PS* , ES* et EdDSA Algorithms)Une signature nécessite une paire de clés: une clé secrète utilisée pour créer des jetons et une clé publique, qui ne peut que les vérifier.
Utilisez toujours un schéma de signature si les deux parties ne se font finalement pas se faire confiance, telles que les jetons échangés entre les clients et les fournisseurs d'API.
Création clé:
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 ( ) ;Les clés peuvent être exportées sous forme d'octets pour une réutilisation ultérieure et importées à partir d'octets ou, pour RSA, à partir de paramètres individuels, de données codées par Der ou de données codées PEM.
Création de paires de clés RSA, en utilisant l'OpenSSL et l'importation PEM de la clé secrète:
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 ) ? ; La création et la vérification des jetons fonctionnent de la même manière qu'avec les algorithmes HS* , sauf que les jetons sont créés avec une paire de clés et vérifiés à l'aide de la clé publique correspondante.
Création de jetons:
/// create claims valid for 2 hours
let claims = Claims :: create ( Duration :: from_hours ( 2 ) ) ;
let token = key_pair . sign ( claims ) ? ;Vérification des jetons:
let claims = public_key . verify_token :: < NoCustomClaims > ( & token , None ) ? ;Les options de vérification disponibles sont identiques à celles utilisées avec des algorithmes symétriques.
Les objets de réclamation prennent en charge toutes les revendications standard par défaut, et ils peuvent être définis directement ou via des aides pratiques:
let claims = Claims :: create ( Duration :: from_hours ( 2 ) ) .
with_issuer ( "Example issuer" ) . with_subject ( "Example subject" ) ; Mais les réclamations définies par l'application peuvent également être définies. Ceux-ci doivent simplement être présents dans un type sérialisable (cela nécessite la caisse 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 ( ) ,
} ;Réclamer la création avec des données personnalisées:
let claims = Claims :: with_custom_claims ( my_additional_data , Duration :: from_secs ( 30 ) ) ;Réclamer la vérification avec des données personnalisées. Remarque la présence du type de données personnalisé:
let claims = public_key . verify_token :: < MyAdditionalData > ( & token , None ) ? ;
let user_is_admin = claims . custom . user_is_admin ;Les propriétés telles que l'identifiant clé peuvent être utiles avant la vérification de la balise ou de la signature afin de choisir la bonne clé d'un ensemble.
let metadata = Token :: decode_metadata ( & token ) ? ;
let key_id = metadata . key_id ( ) ;
let algorithm = metadata . algorithm ( ) ;
// all other standard properties are also accessibleIMPORTANT: Ni l'ID clé ni l'algorithme ne peuvent être fiables. Il s'agit d'un défaut de conception non flexible de la norme JWT.
En conséquence, algorithm ne doit être utilisé qu'à des fins de débogage et pour ne jamais sélectionner un type de clé. De même, key_id ne doit être utilisé que pour sélectionner une clé dans un ensemble de touches conçues pour le même algorithme.
Au minimum nu, la vérification utilisant HS* doit être interdite si un schéma de signature était à l'origine utilisé pour créer le jeton.
Les identifiants clés indiquent aux vérificateurs quelle clé publique (ou clé partagée) doit être utilisée pour la vérification. Ils peuvent être attachés à tout moment aux clés partagées existantes, aux paires de clés et aux clés publiques:
let public_key_with_id = public_key . with_key_id ( & "unique key identifier" ) ; Au lieu de déléguer cela aux applications, jwt-simple peut également créer un tel identifiant pour une clé existante:
let key_id = public_key . create_key_id ( ) ;Cela crée un identifiant codé de texte pour la clé, l'attache et le renvoie.
Si un identifiant a été attaché à une clé partagée ou à une paire de clés, les jetons créés avec eux l'incluront.
jwt-simple comprend des mécanismes pour atténuer les attaques de relecture:
create_nonce() . La procédure de vérification peut ensuite rejeter n'importe quel jeton qui n'inclut pas l'option NONCE attendue (Obligation required_nonce ). Le code de développement comprend une fonction de cargaison cwt qui permet l'analyse expérimentale et la validation des jetons CWT.
Veuillez noter que CWT ne prend pas en charge les réclamations personnalisées. Les identificateurs requis n'ont pas encore été standardisés.
De plus, les caisses de rouille existantes pour la désérialisation JSON et CBOR ne sont pas sûres. Une partie non fiable peut envoyer un objet sérialisé qui nécessite beaucoup de mémoire et de processeur pour désérialiser. Les pansements ont été ajoutés pour JSON, mais avec l'outillage de rouille actuel, il serait difficile à faire pour CBOR.
En tant qu'atténuation, nous vous recommandons fortement de rejeter des jetons qui seraient trop importants dans le contexte de votre application. Cela peut être fait avec l'option de vérification max_token_length .
boring En tant que solution de contournement temporaire pour les problèmes de portabilité avec l'une des dépendances (la caisse boring ), cette bibliothèque peut être compilée pour utiliser uniquement les implémentations de la rouille.
Pour ce faire, importez la caisse avec default-features=false, features=["pure-rust"] dans votre configuration de cargaison.
Ne le faites pas inconditionnellement. Ceci n'est requis que pour des configurations et des cibles très spécifiques, et uniquement jusqu'à ce que les problèmes avec la caisse boring aient été résolus. La façon de configurer cela dans le fret peut également changer dans les versions futures.
Les constructions statiques ciblant la bibliothèque musl ne nécessitent pas cette solution de contournement. Utilisez simplement cargo-zigbuild pour construire votre projet.
L'objectif de wasm32-freestanding (encore parfois appelé wasm32-unknown-unknown dans la rouille) est soutenu (comme dans "It Compile").
Cependant, l'utilisation d'une implémentation JavaScript native est fortement recommandée à la place. Il existe des implémentations JWT de haute qualité dans JavaScript, en tirant parti de l'API WebCrypto, qui offre de meilleures garanties de performances et de sécurité qu'un module WebAssembly.
Cette caisse n'est pas une approbation de JWT. JWT est un design horrible, et l'un des nombreux exemples que "mais c'est une norme" ne signifie pas nécessairement qu'il est bon.
Je recommande vivement pasto ou biscuit à la place si vous contrôlez à la fois la création de jetons et la vérification.
Cependant, JWT est toujours largement utilisé dans l'industrie et reste absolument obligatoire pour communiquer avec les API populaires.
Cette caisse a été conçue pour:
None pour des raisons évidentes).