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:
Ser capaz de nombrar el recurso y el cuerpo de la petición.
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?
El problema, como dices, es que deseas evitar y permitir solicitudes entre sitios. Pregúntale a Schrödinger. –
Eso no significa que no hay formas de resolver el problema. Incluso he proporcionado uno que lo haría, aunque un poco lentamente. – lvh