introducción a rest - symfonyvlc

Post on 03-Jul-2015

1.578 Views

Category:

Technology

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

Transparencias de la charla sobre introducción a REST en SymfonyVLC el 8/10/2013

TRANSCRIPT

Servicios Web REST

Breve introducción a REST

Contenido de la charla• Qué es REST?

o Definición

o Conceptos

o Verbos HTTP

o Códigos de respuesta

o Ejemplos

• APIs CRUDo Operaciones sobre recursos

o Problemas de concurrencia

o Operaciones asíncronas

• Seguridad en REST

• Testing y pruebas

Sobre mi• Programador web PHP

desde hace 4 años

(aprox. 1 con

Symfony2).

• Muy fan de

NodeJS, Angular y

Javascript en general

• Aficionado a la

seguridad informática y

redes.

Qué es REST?Definición y conceptos

Qué es REST? Es una tecnología?

Qué es REST? Es una tecnología? ✗

Es una arquitectura?

Qué es REST? Es una tecnología? ✗

Es una arquitectura? ✗

Es un “estilo arquitectónico”?

Qué es REST? Es una tecnología? ✗

Es una arquitectura? ✗

Es un “estilo arquitectónico”? ✓

Cuando hablamos de REST nos referimos a una forma

de implementar una arquitectura en concreto

(arquitectura WebService) por eso solemos decir que

REST es sólo un estilo de elaborar servicios web.

Qué significa REST?

REpresentational

State

Transfer

¿Qué es REST?• La primera vez que se habló de REST fue en el año

2000 en la tesis doctoral de Roy Fielding, uno de los

principales autores del protocolo HTTP

• Está orientado a transferencias de recursos, no a

llamadas de procedimientos remotos (RPC)

• Hace un uso muy eficiente del protocolo

HTTP, rompe con el esquema de la web que

funciona solo con GET y POST

REST en la actualidad• A día de hoy se conoce por REST a casi cualquier

servicio Web que trabaje con XML y HTTP

• La mayor parte de las APIs existentes son simples

interfaces para interactuar con la base de datos

(CRUD básico)

• REST nos permite modelar totalmente nuestro

negocio sin importar el lenguaje en que este

implementado el servicio o el cliente

Conceptos y reglas• El servicio representará recursos, no acciones

o No se publican verbos como “comprar”

o Se usan recursos como “pedido” o “compra”

• Todos los recursos deben de poseer un UUIDo Universal Unique Identifier

o Todo recurso en REST necesita un UUID para ser identificado dentro del sistema

• La forma en que se represente internamente un recurso debe ser privada

• Todos los recursos deben de poseer un interfaz de operaciones, y todos deben de implementarlas.

Cómo funciona?• Las operaciones se realizan por transferencia de

estadoo El cliente pide una copia del recurso al servicio

o El cliente modifica el recurso

o El cliente transfiere el recurso actualizado al servicio

• Los recursos deben ser multimediao Los recursos deben de poder expresarse en más de un formato

• JSON, XML, CSV…

o En las peticiones podemos y debemos indicar el tipo de formato que admitimos, o una lista con varios, ordenada por prioridades.

• Según la implementación que se consiga, un API se cataloga en uno de los niveles REST

Verbos HTTP• El protocolo HTTP define un conjunto de verbos que nos

permiten realizar todo tipo de operaciones:

o GET (LEER)

o PUT (CREAR O ACTUALIZAR RECURSO COMPLETO)

o POST (CREAR, ACTUALIZAR PARCIALMENTE UN RECURSO)

o PATCH (ACTUALIZAR PARCIALMENTE UN RECURSO)

o DELETE (BORRAR)

o OPTIONS (CONSULTAR OPERACIONES)

• http://www.restapitutorial.com/lessons/httpmethods.html

• Usarlos incorrectamente lleva a desastres como el de Basecamp.o http://www.mail-archive.com/isn@attrition.org/msg04213.html

POST e idempotencia• POST es él verbo “maldito” por REST, al no ser

idempotente.

• La idempotencia asegura que ejecutar n veces

una transferencia obtendrá el mismo resultado.

• Se usa para modelar operaciones como:o Crear recursos

o Modificar parcialmente recursos…

Códigos de Respuesta• El resultado de una transferencia REST viene dado por el código de

estado de la respuesta HTTP, en REST, los más habituales son:

o 200 (OK)

o 201 (Creado)

o 202 (Aceptado)

o 204 (Sin contenido)

o 304 (No modificado)

o 400 (Petición mal formada)

o 401 (No autorizado)

o 403 (Acceso no permitido)

o 404 (No encontrado)

o 409 (Conflicto)

o 412 (Fallo de pre-condición)

o 500 (Problema de Servidor)

• http://www.restapitutorial.com/httpstatuscodes.html

VEAMOS EJEMPLOS!

EjemplosEn nuestra API de pruebas usaremos:

• GETo Obtener recursos o colecciones de recursos

• POSTo Creación de recursos

• PATCHo Actualizaciones parciales

• PUTo Actualizaciones totales de recursos

• DELETEo Eliminar recursos

• OPTIONSo Consultar acciones disponibles

La URL sobre la que trabajaremos será:

http://symfonyvalencia.es/api/usuarios

El servidor puede devolver datos en XML o en JSON

Transferencia OPTIONSConsultar acciones

Cliente ServidorOPTIONS /api/usuarios/B2K1E3

Host: www.symfonyvalencia.es

HTTP/1.1 200 OK

Allow: GET, PUT, POST, OPTIONS, DELETE, PATC

H

Transferencia GET Obtener una Colección

Cliente ServidorGET /api/usuarios HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

Transferencia GET Obtener una Colección

Cliente ServidorGET /api/usuarios HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

HTTP/1.1 200 OK

Content-Type: application/json

[{

“id”: “X1B4Z3”,

“nombre”: “Miguel Ángel”,

“edad”: 27,

“lenguajes”: [“PHP”, “Javascript”]

},

{

“id”: “AB325J”,

“nombre”: “Ángel Miguel”,

“edad”: 26,

“lenguajes”: [“Ruby”, “Java”]

}]

Transferencia GETObtener un registro

Cliente ServidorGET /api/usuarios/X1B4Z3 HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

Transferencia GETObtener un registro

Cliente ServidorGET /api/usuarios/X1B4Z3 HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

HTTP/1.1 200 OK

Content-Type: application/json

Etag: W/2cc7-3f3ba34cc25d

{

“id”: “X1B4Z3”,

“nombre”: “Miguel Ángel”,

“edad”: 27,

“lenguajes”: [“PHP”, “Javascript”]

}

Transferencia POSTCrear un recurso

Cliente ServidorPOST /api/usuarios HTTP/1.1

Host: www.symfonyvalencia.es

Content-Type: application/x-www-

form-urlencoded

Accept: application/json

nombre=Manolo&edad=26&localidad

=Valencia

Transferencia POSTCrear un recurso

Cliente ServidorPOST /api/usuarios HTTP/1.1

Host: www.symfonyvalencia.es

Content-Type: application/x-www-

form-urlencoded

Accept: application/json

nombre=Manolo&edad=26&localidad

=Valencia

HTTP/1.1 201 Created

Content-Type: application/json

Location: http://symfonyvalencia.es/usuarios/B2

K1E3

Etag: W/2cc7-3f3ba34cc25d

{

”id”: “B2K1E3”

}

Transferencia PUTActualizar un recurso

Cliente ServidorPUT /api/usuarios/X1B4Z3

Host: www.symfonyvalencia.es

Content-Type: application/json

Accept: application/json

{

“nombre”: “Miguel Ángel”,

“edad”: 27,

“lenguajes”: [„PHP‟, „Javascript‟],

“localidad”: “Valencia”

}

Transferencia PUTActualizar un recurso

Cliente ServidorPUT /api/usuarios/X1B4Z3

Host: www.symfonyvalencia.es

Content-Type: application/json

Accept: application/json

{

“nombre”: “Miguel Ángel”,

“edad”: 27,

“lenguajes”: [„PHP‟, „Javascript‟],

“localidad”: “Valencia”

}

HTTP/1.1 204 No Content

Content-Type: application/json

Etag: W/2cc7-3f3ba34cc25d

Transferencia DELETEEliminar un recurso

Cliente ServidorDELETE /api/usuarios/B2K1E3

Host: www.symfonyvalencia.es

HTTP/1.1 204 No Content

API’S CRUDLas API‟s CRUD son el tipo de API más extendido en el mundo

REST

APIs CRUD• Son las APIs más habituales

• CRUDo Create (crear)

o Read (leer)

o Update (actualizar)

o Delete (borrar)

• Cubren las operaciones básicas que realizamos habitualmente sobre entidades

• Implementan todos los verbos HTTP

• Son lo contrario a APIs read-only

• Son bastante sencillas de implementar

• En Symfony2 existen bundles que facilitan su implementación, como FOSRESTBundle

APIs CRUD• Si pretendemos ceñirnos absolutamente al

concepto de API REST, no debemos de diseñar

nuestra API como un reflejo de las entidades, ya

que expone nuestro esquema de base de datos.

• Un cambio en una entidad no debería de afectar

al uso del API.

• Se trata de modelar nuestro negocio con

operaciones sobre recursos no de crear un interfaz

para gestionar nuestra base de datos.

Acceso concurrente• Si a nuestro Web Service se conectan

simultáneamente varias personas y modifican el

mismo registro… ¿Puede haber problemas de

inconsistencia?

• El problema de la inconsistencia en REST se

soluciona habitualmente con la inclusión de

cabeceras Etag y If-Match.

• Se utilizan Etags débiles, ya que las fuertes son

demasiado estrictas para nuestro propósito

Ejemplo 1/3

Cliente ServidorGET /api/usuarios/X1B4Z3 HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

HTTP/1.1 200 OK

Content-Type: application/json;charset=utf-8

Etag: W/689438a788bbc2e

{

“id”: “X1B4Z3”,

“nombre”: “Miguel Ángel”,

“edad”: 27,

“lenguajes”: [“PHP”, “Javascript”]

}

Ejemplo 2/3

Cliente Servidor (Match OK)PUT /api/usuarios/X1B4Z3 HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

If-Match: W/689438a788bbc2e

Content-Type: application/json

{

“id”: “X1B4Z3”,

“nombre”: “Miguel Ángel Sánchez”,

“edad”: 23,

“lenguajes”: [“PHP”, “Javascript”],

“localidad”: “Valencia”

}

HTTP/1.1 204 No Content

Etag: W/91246ef669ab7

Ejemplo 3/3

Cliente Servidor (Match KO)PUT /api/usuarios/X1B4Z3 HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

If-Match: W/689438a788bbc2e

Content-Type: application/json

{

“id”: “X1B4Z3”,

“nombre”: “Miguel Ángel Sánchez”,

“edad”: 23,

“lenguajes”: [“PHP”, “Javascript”],

“localidad”: “Valencia”

}

HTTP/1.1 412 Precondition Failed

Etag: W/91246ef669ab7

Actualizaciones parciales• Otro problema básico en REST es la actualización

parcial del contenido.

• Si usamos PUT, sobrescribimos todo el recurso, ¿y si solo

queremos cambiar un atributo del recurso?

• El método habitual se basa en crear nuevos tipos MIME

de cambio de estado o diff y usar POST.

• La solución moderna pasa por usar el verbo

PATCH, funciona igual que con POST, pero es más

semántico y solo admite tipos MIME que representen

diffs

Actualizaciones parciales• Cuando hablamos de diffs hablamos de crear un

formato o tipo MIME que represente una operación

de actualización parcial sobre un registro.

• Suelen nombrarse como:o application/recurso-diff+formato

• Recurso es el tipo de entidad (usuario, factura…)

• Formato puede ser cualquier formato (json, xml…)

• Ejemplos:o application/usuario-diff+json

o application/usuario-diff+xml

Ejemplo de actualización

PATCH /api/usuarios/X1B4Z3 HTTP/1.1Host: www.symfonyvalencia.es

Accept: application/jsonIf-Match: W/979c576c5be5fa5Content-Type: application/usuario-diff+json

[{“tipo-cambio”: “sustituir”,

“campo”: “edad”,“valor”: “23”

},{

“tipo-cambio”: “anadir”,“campo”: “localidad”,

“valor”: “Valencia”}]

Operaciones de larga duración o asíncronas

Operaciones de larga duración o asíncronas

Cuando las operaciones no son inmediatas, o deben

ser encoladas porque se procesan en bloque a cierta

hora mediante un cron, o necesitan validación de un

servicio externo (pasarelas de pago, Paypal…), el

usuario debe de poder seguir el estado del recurso.

Con REST es posible modelar operaciones asíncronas

o encolables creando un recurso intermedio que

represente el estado del recurso antes de finalizar su

creación.

Ejemplo• Vamos a realizar un pago a una tienda online vía

REST

• El pago debe de ser validado por la entidad

bancaria que implementa la pasarela de pago.

• El proceso puede tardar entre 3 y 5min

• El cliente desea saber cuando ha sido validado el

pago

Ejemplo 1/4

Cliente ServidorPOST /api/pago HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

Content-Type: application/json

{

“metodo”: “visa”,

“numtarjeta”: 1234567812345678,

“cvv”: 123

“caducidad”: “12/17”

“cantidad”: 2500

}

HTTP/1.1 202 Accepted

Location: /api/cola-pago/XBC3719

{

“location”: “/api/cola-pago/XBC3719”,

“estado”: “pendiente”

}

Ejemplo 2/4

Cliente ServidorGET /api/cola-pago/XBC3719

HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

HTTP/1.1 200 OK

Content-Type: application/json

{

“estado”: “pendiente”

}

Ejemplo 3/4

Cliente Servidor (Pago OK)GET /api/cola-pago/XBC3719

HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

HTTP/1.1 200 OK

Content-Type: application/json

{

“estado”: “ok”,

“id”: “/api/pago/DEB743AC”

}

Ejemplo 3 bis/4

Cliente Servidor (Pago OK)GET /api/pago/DEB743AC HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

HTTP/1.1 200 OK

Content-Type: application/json

{

“metodo”: “visa”,

“numtarjeta”: 1234567812345678,

“cvv”: 123

“caducidad”: “12/17”

“cantidad”: 2500

“estado”: “pagado”

}

Ejemplo 4/4

Cliente Servidor (Error de pago)GET /api/cola-pago/XBC3719

HTTP/1.1

Host: www.symfonyvalencia.es

Accept: application/json

HTTP/1.1 200 OK

Content-Type: application/json

{

“estado”: “error”,

“error”: “sin fondos

}

Seguridad en RESTNo todas las API‟s son públicas

Seguridad en REST

• En el contexto de un servicio REST no existe el concepto

de Sesión, ya que HTTP es un protocolo Stateless no

orientado a conexión.

• Si nuestra API necesita autenticar a sus usuarios, estos

deben de hacerlo en cada petición

• Esto simplifica un ataque por parte de intrusos

• Veamos algunas normas básicas de seguridad en REST

1. No uses IDs predecibles

1. No uses IDs predecibles• Cualquier intruso que sniffe nuestro tráfico y

detecte una petición del tipo:

GET /api/usuario/1/datos-bancarios

• Se verá tentado de probar con:

GET /api/usuario/2/datos-bancarios

GET /api/usuario/3/datos-bancarios

GET /api/usuario/n/datos-bancarios

• Tampoco debemos usar las claves primarias de

nuestra base de datos, nos exponemos a ataques

SQLi

2. Usa HTTPS

2. Usa HTTPS• Con el protocolo HTTPS ya tenemos ganado la

mitad

• Es un protocolo seguro y el contenido va cifrado, tanto para peticiones como para respuestas.

• Si transmitimos información sensible no será fácilmente reproducible por un atacante que haya usado un sniffer

• Nos da seguridad a la hora de transmitir contraseñas

3. Genera Tokens de seguridad

3. Genera Tokens de seguridad

• Si creamos nuestro propio esquema de

seguridad, generando Tokens que caduquen cada

cierto tiempo, evitaremos transmitir la contraseña

demasiadas veces.

• Después de la primera autenticación, el servidor

nos transmite nuestro Token

• En las próximas peticiones lo incorporaremos en

una cabecera, o una cookie

4. Usa criptografía hardcore

4. Algoritmo HMAC• Nos puede ayudar a construir buenos Tokens

PARTE_PUBLICA = UUID + “:” + NIVEL_SEGURIDAD + ”:” + TIMESTAMP

FIRMA = HMAC(CLAVE_SECRETA, PARTE_PUBLICA)

TOKEN = FIRMA + ”_” + PARTE_PUBLICA

• Desde el servidor basta con descomponer el Tokenen las componentes FIRMA y PARTE_PUBLICA, volver a calcular la firma con la PARTE_PUBLICA recibida y comparar la firma generada con la recibida

4. Algoritmos ajustables

• Algoritmos como BCRYPT o PBKDF aceptan un

parámetro de “complejidad”, que permiten indicar

cuanto tardará en calcularse la contraseña.

• Esto limita los ataques de fuerza bruta, por

ejemplo, con una complejidad de 500ms solo

podrían probarse 2 passwords por segundo.

5. Usa OAuth

5. Usa OAuth

• Una opción alternativa a generar nuestros tokens es

implementar un servidor OAuth que nos permita

acceder a nuestra API

• Existen librerías en casi todos los lenguajes que

implementan OAuth

• Podemos incluso usar nuestros credenciales en

redes sociales para acceder a nuestra API.

Testing y pruebas

Asegurándonos de que todo anda bien

Testing y pruebas

Testing y pruebas

• Como cualquier otro programa, aplicación web… que desarrollemos, las APIs REST se pueden implementar usando TDD.

• Es importante testear siempre que todos los códigos de respuesta devueltos son adecuados, tanto para casos de éxito como para casos de error.

• IMPORTANTE: Muchos frameworks implementan listenersde excepciones y evitan, por ejemplo que una página devuelva un 404, enmascarando el resultado con una respuesta 200.

Testing con xUnit

• Podemos usar tranquilamente nuestro framework

de pruebas xUnit para testear, no solo los códigos

de respuesta, sino toda la lógica de negocio.

• Las prácticas habituales de testing con este tipo de

herramientas son totalmente válidas para el testeo.

Testing con cURL

Testing con cURL• Es una alternativa para realizar nuestros tests (cURL

+ Shell Script)

• Con cURL podemos crear exactamente la

transferencia que buscamos.

• Si se tienen suficientes conocimientos sobre cURL se

pueden crear buenos tests con un tiempo de

ejecución bastante bajo.

Testing con Guzzle

… porque cURL no es para todos los públicos

Testing con Guzzle (PHP)• Guzzle es una librería en PHP que actúa como un

wrapper de cURL, exprimiendo al máximo su potencia.

• Permite crear clientes REST muy potentes y escalables

• Es realmente sencillo de utilizar y es orientado a objetos

• Se integra bien con PHPUnit.

Testing con POSTMAN

Testing con POSTMAN

• POSTMAN es un plugin para Chrome que actúa

como cliente REST

• Nos permite guardar las transferencias

preconfiguradas

• Permite autenticación mediante OAuth de forma

bastante sencilla

• No es una herramienta automática (hay que

ejecutar manualmente cada test y comprobar su

resultado), por lo que no es recomendable testear

sólo con POSTMAN

Documentación con Swagger

Documentación con Swagger

• Swagger es un estándar para definición de APIs

REST y un framework que nos permite visualizar su

representación y también consumir el API.

• Es una herramienta muy útil para publicar nuestra

API y que todo el mundo aprenda a usarla

rápidamente.

• Existe un bundle para Symfony2 que implementa

Swagger: NelmioApiDocBundle

top related