HS* JWT)RS* , PS* , ES* y EdDSA ) EjemploboringUna nueva implementación de JWT (tokens web JSON) para Rust se centra en la simplicidad, al tiempo que evita las dificultades de seguridad de JWT comunes.
jwt-simple no es opensado y admite todos los algoritmos de autenticación y firma comúnmente implementados:
| Nombre del algoritmo JWT | Descripción |
|---|---|
HS256 | HMAC-SHA-256 |
HS384 | HMAC-SHA-384 |
HS512 | HMAC-SHA-512 |
BLAKE2B | Blake2B-256 |
RS256 | RSA con PKCS#1v1.5 Pading / SHA-256 |
RS384 | RSA con PKCS#1v1.5 Pading / SHA-384 |
RS512 | RSA con PKCS#1v1.5 Pading / SHA-512 |
PS256 | RSA con PSS Padding / SHA-256 |
PS384 | RSA con PSS Padding / SHA-384 |
PS512 | RSA con PSS Padding / SHA-512 |
ES256 | ECDSA sobre P256 / SHA-256 |
ES384 | ECDSA sobre P384 / SHA-384 |
ES256K | ECDSA sobre SECP256K1 / SHA-256 |
EdDSA | ED25519 |
jwt-simple puede ser compilado fuera de la caja a WebAssembly/WASI. Es totalmente compatible con el servicio de cómputo rápidamente.
IMPORTANTE: El propósito de JWT es verificar que una parte haya creado los datos que conoce una clave secreta. No proporciona ningún tipo de confidencialidad: los datos de JWT simplemente están codificados como Base64, y no está encriptado.
cargo.toml :
[ dependencies ]
jwt-simple = " 0.12 "Óxido:
use jwt_simple :: prelude :: * ; Los errores se devuelven como valores jwt_simple::Error (alias para el tipo Error de thiserror Crate).
HS* JWT)Los esquemas de autenticación usan la misma clave para crear y verificar tokens. En otras palabras, ambas partes deben confiar entre sí, o el verificador también podría crear tokens arbitrarios.
Creación clave:
use jwt_simple :: prelude :: * ;
// create a new key for the `HS256` JWT algorithm
let key = HS256Key :: generate ( ) ; Se puede exportar una clave como bytes con key.to_bytes() , y restaurarse con HS256Key::from_bytes() .
Creación de token:
/// create claims valid for 2 hours
let claims = Claims :: create ( Duration :: from_hours ( 2 ) ) ;
let token = key . authenticate ( claims ) ? ;-> ¡hecho!
let claims = key . verify_token :: < NoCustomClaims > ( & token , None ) ? ;-> ¡hecho! No se requieren pasos adicionales.
La expiración clave, el tiempo de inicio, las etiquetas de autenticación, etc. se verifican automáticamente. La función falla con JWTError::InvalidAuthenticationTag si la etiqueta de autenticación no es válida para la clave dada.
El conjunto completo de reclamos puede inspeccionarse en el objeto claims si es necesario. NoCustomClaims significa que solo el conjunto estándar de reclamos es utilizado por la aplicación, pero también se pueden admitir reclamos definidos por la aplicación.
Los pasos de verificación adicionales se pueden habilitar opcionalmente a través de la estructura 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 ) ) ? ; Tenga en cuenta que allowed_issuers y allowed_audiences no son cadenas, sino conjuntos de cadenas (usando el tipo de HashSet desde la biblioteca estándar de óxido), ya que la aplicación puede permitir múltiples valores de retorno.
RS* , PS* , ES* y EdDSA ) EjemploUna firma requiere un par de claves: una clave secreta utilizada para crear tokens y una clave pública, que solo puede verificarlas.
Siempre use un esquema de firma si ambas partes no confían entre sí, como los tokens intercambiados entre clientes y proveedores de API.
Creación clave:
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 ( ) ;Las claves se pueden exportar como bytes para una reutilización posterior e importarse de bytes o, para RSA, de parámetros individuales, datos codificados por der o datos codificados por PEM.
Creación de pares de teclas RSA, usando la importación de OpenSSL y PEM de la clave secreta:
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 creación y verificación del token funcionan de la misma manera que con los algoritmos HS* , excepto que los tokens se crean con un par de claves y se verifican utilizando la clave pública correspondiente.
Creación de token:
/// create claims valid for 2 hours
let claims = Claims :: create ( Duration :: from_hours ( 2 ) ) ;
let token = key_pair . sign ( claims ) ? ;Verificación del token:
let claims = public_key . verify_token :: < NoCustomClaims > ( & token , None ) ? ;Las opciones de verificación disponibles son idénticas a las utilizadas con algoritmos simétricos.
Los objetos de reclamación admiten todas las reclamaciones estándar de forma predeterminada, y se pueden establecer directamente o a través de ayudantes convenientes:
let claims = Claims :: create ( Duration :: from_hours ( 2 ) ) .
with_issuer ( "Example issuer" ) . with_subject ( "Example subject" ) ; Pero también se pueden definir reclamos definidos por la aplicación. Estos simplemente tienen que estar presentes en un tipo serializable (esto requiere la caja 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 ( ) ,
} ;Creación de reclamos con datos personalizados:
let claims = Claims :: with_custom_claims ( my_additional_data , Duration :: from_secs ( 30 ) ) ;Verificación de reclamos con datos personalizados. Tenga en cuenta la presencia del tipo de datos personalizado:
let claims = public_key . verify_token :: < MyAdditionalData > ( & token , None ) ? ;
let user_is_admin = claims . custom . user_is_admin ;Las propiedades, como el identificador de clave, pueden ser útiles antes de la verificación de la etiqueta o la firma para elegir la tecla correcta de un conjunto.
let metadata = Token :: decode_metadata ( & token ) ? ;
let key_id = metadata . key_id ( ) ;
let algorithm = metadata . algorithm ( ) ;
// all other standard properties are also accessibleIMPORTANTE: Ni se puede confiar en la identificación clave ni el algoritmo. Este es un defecto de diseño no fijo del estándar JWT.
Como resultado, algorithm debe usarse solo para fines de depuración, y nunca para seleccionar un tipo de clave. Del mismo modo, key_id debe usarse solo para seleccionar una clave en un conjunto de teclas hechas para el mismo algoritmo.
Como mínimo, la verificación utilizando HS* debe estar prohibida si un esquema de firma se usaba originalmente para crear el token.
Los identificadores de clave indican a los verificadores qué clave pública (o clave compartida) debe usarse para la verificación. Se pueden adjuntar en cualquier momento a las claves compartidas, pares de claves y claves públicas existentes:
let public_key_with_id = public_key . with_key_id ( & "unique key identifier" ) ; En lugar de delegar esto a las aplicaciones, jwt-simple también puede crear dicho identificador para una clave existente:
let key_id = public_key . create_key_id ( ) ;Esto crea un identificador codificado por texto para la clave, la adjunta y lo devuelve.
Si un identificador se ha conectado a una clave compartida o un par de claves, los tokens creados con ellos lo incluirán.
jwt-simple incluye mecanismos para mitigar los ataques de repetición:
create_nonce() . El procedimiento de verificación puede rechazar más tarde cualquier token que no incluya el NonCE esperado (Opción de verificación required_nonce ). El código de desarrollo incluye una característica de carga cwt que permite el análisis experimental y la validación de tokens CWT.
Tenga en cuenta que CWT no admite reclamos personalizados. Los identificadores requeridos aún no se han estandarizado.
Además, las cajas de óxido existentes para la deserialización de JSON y CBOR no son seguras. Una parte no confiable puede enviar un objeto serializado que requiere mucha memoria y CPU para deserializarse. Se han agregado ayuda para JSON, pero con las herramientas de óxido actuales, sería difícil de hacer para CBOR.
Como mitigación, recomendamos rechazar tokens que sean demasiado grandes en el contexto de su aplicación. Eso se puede hacer con la opción de verificación max_token_length .
boring Como una solución temporal para los problemas de portabilidad con una de las dependencias (la caja boring ), esta biblioteca se puede compilar para usar solo implementaciones de óxido.
Para hacerlo, importe la caja con default-features=false, features=["pure-rust"] en su configuración de carga.
No lo hagas incondicionalmente. Esto solo se requiere para configuraciones y objetivos muy específicos, y solo hasta que se hayan resuelto problemas con la caja boring . La forma de configurar esto en carga también puede cambiar en futuras versiones.
Las construcciones estáticas dirigidas a la biblioteca musl no requieren esa solución. Simplemente use cargo-zigbuild para construir su proyecto.
Se admite el objetivo wasm32-freestanding (todavía a veces llamado wasm32-unknown-unknown en óxido) (como en "Compila").
Sin embargo, el uso de una implementación nativa de JavaScript es muy recomendable. Existen implementaciones de JWT de alta calidad en JavaScript, aprovechando la API Webcrypto, que proporcionan mejores garantías de rendimiento y seguridad que un módulo WebAssembly.
Esta caja no es un respaldo de JWT. JWT es un diseño horrible, y uno de los muchos ejemplos que "pero este es un estándar" no significa necesariamente que sea bueno.
Recomiendo encarecidamente Paseto o Biscuit si controlas la creación y verificación de tokens.
Sin embargo, JWT todavía se usa ampliamente en la industria, y sigue siendo absolutamente obligatorio para comunicarse con las API populares.
Esta caja fue diseñada para:
None por razones obvias).