Cada vez más nos encontramos con el concepto API en todas partes. Las API’s son recursos de red que brindan acceso a las características de una aplicación o a la información de la base de datos, funcionan como método para comunicar aplicaciones o como interfaces que entre 2 o más plataformas diferentes y como tal lo normal es que se necesite identificar a los usuarios de estas con algún tipo de autenticación para proteger las API’s.
Stateful vs Stateless
Dentro de los mecanismos más conocidos para proteger el acceso las API’s está la basada en cookies (stateful) y la basada en token (stateless), es importante destacar que ni las cookies ni el token son por sí solos mecanismos autenticación.
Las cookies trabajan en conjunto con las sesiones y son consideradas como mecanismos stateful debido a que desde el backend se debe validar su autenticidad con alguna búsqueda en archivos o en bases de datos (SQL o NoSql) lo cual representa consumo de recursos y se puede convertir en un cuello de botella para aplicaciones de alta demanda.
Las cookies son solo un valor enviado por el navegador en forma de header HTTP automáticamente cuando realiza una petición al dominio que la guardó lo cual las convierte en proceso conveniente y ágil para los navegadores pero difícil o imposible de implementar por otros dispositivos que tengan acceso a internet y que requieran consumir características de alguna API.
Por otro lado los token cuentan con mecanismos propios para ser validados por el backend sin tener que recurrir a búsquedas ya que contienen la información necesaria para demostrar su autenticidad así como datos de usuarios, roles y otras características que nos evitan el consumo de recursos de base de datos por lo cual son considerados mecanismos stateless.
Si una API admite tokens entonces aumentará el rango de clientes y dispositivos que puede atender, lo cual la vuelve más útil si debe atender clientes más allá de los navegadores.
¿Qué es JWT?
Bueno después de esta breve introducción comencemos con lo que nos trajo a este artículo, que és JWT.
JWT o JSON Web Token es un estándar abierto (descrito en RFC 7519 por IETF) que define un formato compacto y autónomo de tokens para compartir de forma segura información entre las parte mediante un JSON válido.
¿Estructura de JWT?
Un JSON Web Token está dividido en tres partes separadas por un punto:
Header (JOSE o JavaScript Object Signing and Encryption): esta parte establece el Algoritmo que se usará para securizar la información que puede ser HMAC SHA256 o RSA y determina que lo que se está transmitiendo es un token de tipo JWT.
{
"alg": "HS256",
"typ": "JWT"
}
Payload (Claims): Aquí se almacena la información de negocio, no tiene una estructura fija y dependerá de los atributos que cada quien requiera que puede ir desde el nombre de un usuario, su id y su rol, es importante aclarar que esta información viaja en base64 lo cual es totalmente reversible y nunca debe contener información sensible o contraseñas.
{
"id": 1234567890,
"name": "John Doe",
"admin": true
}
Signature (firma): Esta es la última sección y es la que se encarga de brindar validez al token con una cadena hash con la estructura.
HMAC(base64UrlEncode(JOSE)+"." +base64UrlEncode(Payload),contraseña)
El resultado del token anterior usando como contraseña la palabra secret es:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UiLCJhZG1pbiI6dHJ1ZX0.YEXaARAQFK9cwliLSMU94Rq5yaVuoLPw5iINhfzl0Q4
Aun cuando el estándar de JWT es RFC 7519 este solo define cómo debe estar construido, no nos ata a ningún mecanismo de persistencia de datos en el lado del cliente y tampoco a ninguna regla de cómo se debe transportar el token.
Los tokens se envían generalmente como un Authorization header, con el valor Bearer: JWT; pero pueden enviarse también en el cuerpo de una petición POST o incluso como un query parameter.
Si quieren ver más a detalle cómo se construye un token y validarlos pueden usar la pagina de https://jwt.io/ que cuenta con un Debugger muy bueno.
Implementación
Bueno ya para cerrar este tema añadiré un pequeño gráfico que permite ejemplificar cómo funciona JWT y por último algunos ejemplos de cómo implementar estos tokens en diversos lenguajes.
En este gráfico se presenta el escenario más común para el uso de JWT. Como primer paso el usuario realiza una autenticación con su usuario y contraseña si la respuesta es 200 el server responde con un Token JWT válido que el cliente deberá almacenar localmente, después cada solicitud subsiguiente incluirá el JWT, permitiendo que el cliente acceda a rutas, servicios y recursos con ese token.
Implementar con npm
$ npm install jsonwebtoken - saveimport jwt from 'jsonwebtoken'export function verifyJWTToken(token)
{
return new Promise((resolve, reject) =>
{
jwt.verify(token, process.env.JWT_SECRET, (err, decodedToken) =>
{
if (err || !decodedToken){
return reject(err)
}
resolve(decodedToken)
})
})
}import jwt from 'jsonwebtoken'
import _ from 'lodash'export function createJWToken(details)
{
if (typeof details !== 'object'){
details = {}
}
if (!details.maxAge || typeof details.maxAge !== 'number'){
details.maxAge = 3600
}
details.sessionData = _.reduce(details.sessionData || {}, (memo, val, key) =>{
if (typeof val !== "function" && key !== "password"){
memo[key] = val
}
return memo
}, {})
let token = jwt.sign({
data: details.sessionData
}, process.env.JWT_SECRET, {
expiresIn: details.maxAge,
algorithm: 'HS256'
})
return token
}
Implementar con maven
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.5.0</version>
</dependency>//HMAC
Algorithm algorithmHS = Algorithm.HMAC256("secret");//RSA
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instanceAlgorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);
final JwkStore jwkStore = new JwkStore("{JWKS_FILE_HOST}");
final RSAPrivateKey privateKey = //Get the key instance
final String privateKeyId = //Create an Id for the above keyRSAKeyProvider keyProvider = new RSAKeyProvider() {
@Override
public RSAPublicKey getPublicKeyById(String kid) {
//Received 'kid' value might be null if it wasn't defined in the Token's header
RSAPublicKey publicKey = jwkStore.get(kid);
return (RSAPublicKey) publicKey;
}
@Override
public RSAPrivateKey getPrivateKey() {
return privateKey;
}
@Override
public String getPrivateKeyId() {
return privateKeyId;
}
};Algorithm algorithm = Algorithm.RSA256(keyProvider);
//Use the Algorithm to create and verify JWTs.
Implementar con composer
composer create-project laravel/laravel jwtauth - prefer-dist
composer require tymon/jwt-auth
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\User;
use JWTFactory;
use JWTAuth;
use Validator;
use Response;class APIRegisterController extends Controller
{
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'email' => 'required|string|email|max:255|unique:users',
'name' => 'required',
'password'=> 'required'
]);
if ($validator->fails()) {
return response()->json($validator->errors());
}
User::create([
'name' => $request->get('name'),
'email' => $request->get('email'),
'password' => bcrypt($request->get('password')),
]);
$token = JWTAuth::fromUser($user);
return Response::json(compact('token'));
}
}
Implementar con pip
pip install PyJWT
pip install cryptography>>> import jwt
>>> encoded = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')
jwt.decode(encoded, 'secret', algorithms=['HS256'])
Por último
Esta vez sí lo prometo, por último me gustaría hacerles algunas recomendaciones para mejorar la seguridad de sus API’s:
Validar los datos
Tener una forma segura de validar las entradas de información a tu API debe ser la primera regla de seguridad y la mejor forma de esto es validar los datos contra un SCHEMA estricto.
Sanitiza tus datos
Debes limpiar la entrada de datos para evitar ataques comunes como los injection attacks, tanto de SQL como de scripts.
Siempre usa TLS
Todas las API’s que puedas desarrollar deben tener como mínimo un canal de conexión segura el protocolo SSL o TLS no debe ser considerado un plus sino un prerrequisito para tu desarrollo.
Implementa Áreas de Juego
Si tu API representan componentes de aplicaciones compartidas considera desarrollar ambientes de prueba o sandbox como zonas seguras y controladas de desarrollo.
Saludos.
Deja un comentario