2011-11-21 10 views
11

Estoy diseñando la API para un grupo de sitios. Los sitios son muy similares (algo así como StackOverflow, SuperUser y ServerFault), y tiene sentido que tengan un back-end compartido. Por lo tanto, decidimos probar y tener una buena API REST como backend, y un grupo de interfaces muy similares pero diferentes que consumen dicha API. Las interfaces deben ser preferiblemente estáticas, pero no es un requisito difícil si resulta imposible.¿Cómo se asegura una API RESTful que consume un navegador de los ataques CSRF?

Estoy trabajando en el diseño de esa API ahora, y estoy preocupado por las implicaciones de seguridad, particularmente CSRF. Desde mi comprensión básica de los ataques CSRF, que consisten en dos componentes importantes:

  1. Ser capaz de nombrar el recurso y el cuerpo de la petición.

  2. Engañar al usuario/navegador para que use la autenticación de ambiente (como sesiones) para realizar una solicitud a ese recurso que parece autenticado.

Muchos de los enfoques clásicos para solucionar los ataques de CSRF se basan en la sesión. Como mi API REST no hace realmente sesiones, eso evita muchos de los vectores y también casi todas las formas de solucionarlos. Por ejemplo, enviar dos veces no tiene sentido porque no hay nada que enviar dos veces.

Mi enfoque inicial consistió en atacar a la parte 2 de un ataque CSRF. Si autentico todas las solicitudes (digamos usando HTTP Basic Auth), y el navegador no mantiene esas credenciales almacenadas (por ejemplo, algunas JS hicieron la solicitud), solo el JS que tiene las credenciales puede hacer la solicitud, y hemos terminado . El inconveniente obvio es que la aplicación necesita saber las credenciales del usuario. La otra desventaja, algo menos obvia, es que si quiero guardar credenciales de forma segura en el extremo de la API, la verificación de una contraseña debe tomar una cantidad de tiempo fija y no trivial. Si verificar una contraseña de forma segura toma 100 ms, entonces cada otra solicitud va a tomar al menos 100 ms + eps, y va a tomar un poco de astucia inteligente lado del cliente para que no se sienta lento. Puede que sea capaz de guardarlo en caché (ya que las credenciales siempre serán las mismas), y si soy muy cuidadoso, podría lograrlo sin introducir una vulnerabilidad temporal, pero eso suena como un nido de avispas.

OAuth 2.0 parece un poco exagerado, pero supongo que podría ser la mejor solución después de todo, no sea que acabe implementándolo mal. Supongo que podría hacer lo de HTTP Basic Auth por ahora, y pasar a OAuth cuando tengamos desarrolladores de aplicaciones de terceros.

Hay una cierta falta de impedancia con OAuth. OAuth realmente quiere ayudar a las aplicaciones a acceder a cosas en otra aplicación, básicamente. Quiero que los usuarios se registren en una de las interfaces antes de que esa cuenta exista.

También consideré atacar el punto 1 haciendo las URL aleatorias, es decir, agregando tokens a una cadena de consulta. Esto ciertamente funcionaría y está muy cerca de cómo funciona el token aleatorio tradicional en una forma, y ​​dado HATEOAS incluso debería ser bastante RESTful, aunque esto genera dos preguntas: 1) ¿dónde empiezas? ¿Hay un punto de inicio API obligatorio en el que inicie sesión utilizando HTTP Basic Auth? 2) ¿Cuánto encantarían a los desarrolladores de aplicaciones si no pueden predecir un URL por adelantado, HATEOAS sea condenado?

He visto How to prevent CSRF in a RESTful application?, pero estoy en desacuerdo con la premisa de que los URI aleatorios son necesariamente irrelevantes. Además, esa pregunta realmente no tiene respuestas satisfactorias y no menciona a OAuth. Además, la solución de envío doble de la sesión no es válida, como mencioné anteriormente (dominio diferente para la interfaz estática que el punto final API).

Me doy cuenta de que lo que estoy tratando fundamentalmente de hacer aquí es tratar de permitir solicitudes entre sitios de un dominio y rechazarlos del otro, y eso no es fácil. Seguramente tiene que haber alguna solución razonable?

+0

El problema, como dices, es que deseas evitar y permitir solicitudes entre sitios. Pregúntale a Schrödinger. –

+0

Eso no significa que no hay formas de resolver el problema. Incluso he proporcionado uno que lo haría, aunque un poco lentamente. – lvh

Respuesta

0

La respuesta depende de lo que necesite soportar. Si suponemos que desea admitir una aplicación web que utiliza el servicio REST y utiliza el mismo servicio REST para una API que es algo diferente a una aplicación web que resulta RESTFUL (puede decidir que 'sesiones' son para ti!).

Por ahora (/ estoy algo cansado) Creo que la forma en que sugirió usar javascript con HTTP Basic Auth es un buen comienzo.

+0

HTTP Basic Auth es vulnerable a CSRF. – rook

+0

¿Te importaría elaborar? Me doy cuenta de que los navegadores guardan en caché la autenticación básica HTTP, pero no le pedimos al navegador que lo haga; estamos sugiriendo que algunos JS construyan el encabezado HTTP Auth Auth. De ahí que las credenciales no terminan en la piscina de autenticación ambiente del navegador, y el sistema se convierte en CSRF-inmunes. ¿Correcto? – lvh

3

Un token CSRF es, por definición, "por estado de usuario" y, por lo tanto, no RESTful. La mayoría de las API incumplen sus requisitos de "estado de usuario" a los efectos de la seguridad y requieren un token CSRF pasado como un parámetro de encabezado HTTP.

Hay otras formas de preventing CSRF. Comprobar el referer no es tan fuerte como un token CSRF, pero es RESTful y MUY es poco probable que se socave. Tenga en cuenta que la falta de un referer debe considerarse una solicitud fallida.

XSS se puede utilizar para evitar la prevención de CSRF basada en token y la prevención de CSRF basada en referencia.

+0

Mismo comentario que en mi respuesta a su comentario. ¿Podría explicar por qué la Autenticación Básica HTTP no previene a CSRF cuando agrega el encabezado de autenticación usando JS? Además, ¿cómo OAuth no previene el CSRF? ¿La parte del token de OAuth de una vez no garantiza eso? – lvh

+0

@lvh un nombre de usuario y contraseña es un estado por usuario que se envía con la solicitud. Un Nonce criptográfico utilizado como un token CSRF también se envía por estado de usuario con cada solicitud, pero es más seguro porque debe ser un valor dinámico vinculado a una sesión. Por lo general, los protocolos RESTful se basan en un nocne criptográfico estático o "clave api", que es un estado por usuario y también detiene el CSRF. Enviarlo en el encabezado HTTP mágicamente no lo deja tranquilo, pero puede abrir la puerta al ataque si otras solicitudes enviadas por ese navegador contienen este valor. – rook

+0

Pero ese es mi punto. ¿Cuál es el vector de ataque si no se convierte en navegador de ambiente? Ya sea que se trate de un nonce o de la credencial real, no importa para fines de CSRF: el navegador no * sabe *, entonces, ¿cómo podría engañarse para que lo proporcione? – lvh

0

Tengo otra posible solución, así que la estoy enumerando como una respuesta. Por favor, siéntete libre de desarmarlo :)

auth.platform.com toma la autenticación y establece las cookies. Si auth.site.com es un CNAME para auth.platform.com, ¿la solicitud a auth.site.com (terminando en auth.platform.com después de la resolución) podrá establecer una cookie para site.com? De esa forma podría enviar dos veces las cookies de sesión. Por ejemplo, auth.platform.com solo configurará cookies para algunos dominios de la lista blanca.

EDIT: Excepto, por supuesto, esto no funciona en absoluto, ya que tendría que utilizar HTTPS para hacer la autenticación de forma segura, y HTTPS será ver a través de su engaño.

0

El diseño de su API como un verdadero REST, evita que los vectores CSRF más comunes:

  • galletas evitando les impide ser "robado",
  • utilizando GET para las operaciones de "seguros" impide img y iframes para desencadenar acciones inseguras sin la interacción del usuario.

Luego debe implementar CORS para permitir que los navegadores de los usuarios bloqueen las solicitudes de orígenes en los que no confía.

+1

-1 Nada de esto tiene que ver con CSRF, excepto por el comentario sobre CORS. CORS es peligroso y se puede utilizar para leer un token CSRF u otra información sensible. CORS no se puede usar para prevenir CSRF. – rook

Cuestiones relacionadas