2010-03-06 18 views
66

Cross Site Request Falsificación (CSRF) se suele evitar con uno de los métodos siguientes:¿Cómo prevenir CSRF en una aplicación RESTful?

  • Comprobar árbitro - símbolo
  • inserción REST pero poco fiable en la forma y almacenar el token en la sesión del servidor - no es realmente REST
  • URIs un tiempo crípticos - REST no por la misma razón como tokens
  • enviar la contraseña manualmente para esta solicitud (no la contraseña almacenada en caché se utiliza con autenticación HTTP) - REST pero no es conveniente

Mi idea es utilizar un secreto de usuario, un identificador críptico pero estático y JavaScript para generar tokens.

<form method="POST" action="/someresource" id="7099879082361234103"> 
    <input type="hidden" name="token" value="generateToken(...)"> 
    ... 
</form> 
  1. GET /usersecret/john_doe obtenidos por el JavaScript del usuario autenticado.
  2. Respuesta: OK 89070135420357234586534346 Este secreto es conceptualmente estático, pero se puede cambiar cada día/hora ... para mejorar la seguridad. Esta es la única cosa confidencial.
  3. Leer la forma críptica (pero estática para todos los usuarios!) Id con JavaScript, procesarla junto con el secreto de usuario: generateToken(7099879082361234103, 89070135420357234586534346)
  4. enviar el formulario junto con el token generado en el servidor.
  5. Dado que el servidor conoce el secreto de usuario y el id. De formulario, es posible ejecutar la misma función generateToken que el cliente antes de enviar y comparar ambos resultados. Solo cuando ambos valores sean iguales, la acción será autorizada.

¿Hay algún problema con este enfoque, a pesar de que no funciona sin JavaScript?

Adición:

+0

Su usersecret no es exclusivo del usuario, un atacante simplemente necesita obtener ese número y ajustar sus scripts para usar el nuevo cálculo. ¿Cómo está autenticando usuarios si no tiene ningún estado? – Mike

+0

El secreto de usuario es único por usuario y solo se puede recuperar después de la autenticación (HTTP básico o autenticación resumida o autenticación de certificado) – deamon

Respuesta

21

Aquí hay muchas respuestas y problemas con algunas de ellas.

cosas que no debe hacer:

  1. Si necesita leer el identificador de sesión de JavaScript, que están haciendo algo muy mal. La cookie del identificador de sesión SIEMPRE debe tener HTTPOnly configurado para que no esté disponible para las secuencias de comandos.

    Esta protección hace que el impacto de XSS se reduzca considerablemente, ya que un atacante ya no podrá obtener un token de sesión de usuario conectado, que para todos los efectos es equivalente a las credenciales en la aplicación . No quiere un error para dar claves al reino.

  2. El identificador de sesión no se debe escribir en el contenido de la página. Esto es por las mismas razones por las que configura HTTPOnly. Esto significa que su token de sesión no puede ser su identificación de sesión. Necesitan ser valores diferentes.

cosas que debe hacer:

  1. Siga OWASP's guidance:

  2. En concreto, si se trata de una aplicación REST que pueda require double-submission of CSRF tokens:

simplemente crear algo criptográficamente al azar, guárdelo en codificación ASCII Hex o Base64, una ndúdelo como una cookie y a sus formularios cuando el servidor devuelve la página.En el lado del servidor, asegúrese de que el valor de la cookie coincida con el valor del formulario. Voila, has matado a CSRF, has evitado las indicaciones adicionales para tus usuarios y no te has abierto a más vulnerabilidades.

+0

XSS con una cookie de sesión es tan vulnerable como un XSS con un token que se puede leer desde JavaScript. Si todavía puede crear una solicitud de AJAX que transfiera dinero de la cuenta de usuario a mi cuenta, el servidor lo aceptará gustosamente. – ghayes

+2

@ghayes No estoy de acuerdo. Su token de sesión es mucho más sensible que su token CSRF. Con su token de sesión, puedo acceder a la aplicación completamente, como usted, desde mi máquina. Con el token CSRF, posiblemente pueda tener una lista de acciones sensibles previamente escritas que se ejecutan en su navegador. El segundo escenario es mucho más difícil de lograr, requiere conocimiento de la aplicación, requiere más tiempo para ejecutarse y las acciones se limitan a lo que usted planificó de antemano. El primer escenario toma una línea de código para cualquier sitio web, y una aplicación de Cookie Manager para que el atacante la use en su máquina. – Doug

+0

Vale la pena mencionar aquí. Tener estrictas comprobaciones de solicitud HTTP de origen cruzado en el servidor y encabezados de retorno http de la API, puede limitar gran parte del daño automático que un atacante podría hacer en un usuario conectado. – LessQuesar

6

La forma ID estática no proporciona ninguna protección en absoluto; un atacante puede ir a buscarlo él mismo. Recuerde, el atacante no está obligado a usar JavaScript en el cliente; él puede buscar el ID de forma estática del lado del servidor.

No estoy seguro de entender completamente la defensa propuesta; ¿De dónde viene el GET /usersecret/john_doe? ¿Es eso parte de la página JavaScript? ¿Es esa la URL literal propuesta? Si es así, supongo que username no es un secreto, lo que significa que evil.ru puede recuperar secretos de usuario si un navegador o error de complemento permite solicitudes GET entre dominios. ¿Por qué no almacenar el secreto de usuario en una cookie al momento de la autenticación en lugar de permitir que cualquiera que pueda hacer un GET entre dominios lo recupere?

Leería "Robust Defenses for Cross-Site Forgery" muy cuidadosamente antes de implementar mi propio sistema de autenticación que quería ser resistente a CSRF. De hecho, reconsideraría la implementación de mi propio sistema de autenticación.

+1

El ID de formulario es algo así como una clave pública. Tienes razón, 'GET/usersecret/john_doe' es parte de JavaScript. El nombre de usuario en sí mismo no es el secreto, pero el ID fue recuperado con esta solicitud por un usuario autenticado (!). Gracias por el enlace. – deamon

9

Definitivamente necesita algún estado en el servidor para autenticar/autorizar. Sin embargo, no es necesario que sea la sesión http, puede almacenarla en un caché distribuido (como memcached) o una base de datos.

Si utiliza cookies para la autenticación, la solución más fácil es enviar dos veces el valor de la cookie. Antes de enviar el formulario, lea la identificación de la sesión de la cookie, guárdela en un campo oculto y luego envíela. Del lado del servidor, confirme que el valor de la solicitud sea el mismo que el de la sesión (que obtuvo de la cookie). La secuencia de comandos de Evil de otro dominio no podrá leer la identificación de sesión de la cookie, evitando así CSRF.

Este esquema usa un único identificador en toda la sesión.

Si desea obtener más protección, genere un ID único por sesión por formulario.

Además, NO genere tokens en JS. Cualquiera puede copiar el código y ejecutarlo desde un dominio diferente para atacar su sitio.

+1

Las sesiones no son necesarias para la autenticación, como lo demuestra la autenticación HTTP. El código JavaScript para generar el token no es secreto, solo el secreto del usuario debe ser secreto. – deamon

+3

@Sri aunque acepto que una sesión es la mejor manera de manejar esto desde una seguridad y rendimiento respectivos. Esto no es RESTful porque requiere que el servidor realice un seguimiento del estado por usuario que puede causar problemas de escalabilidad. – rook

+1

¿Es correcto decir que el envío de cookies doble no funcionaría si la página es vulnerable a los ataques XSS? Porque entonces usted podría enviar un formulario directamente desde el propio dominio y el valor se enviaría tanto a través de la cookie como de la forma. –

3

Existen algunos métodos en el CSRF Prevention Cheat Sheet que se pueden utilizar con un servicio de descanso. La mitigación de CSRF sin estado más RESTful es usar Origin o HTTP referer para asegurarse de que las solicitudes se originen en un dominio de confianza.

+2

Este es un consejo peligroso, aunque es difícil falsificar el referer HTTP, no es imposible, también el encabezado del referer no está garantizado (y el no envío de un encabezado del referer romperá su aplicación). – RelaXNow

+3

@RelaXNow Echa un vistazo a este marco de explotación de CSRF que escribí: https://github.com/TheRook/CSRF-Request-Builder. Le permite especificar encabezados http arbitrarios, así como cuerpo. Sin embargo, ** no puede cambiar el referer http ** porque esto está prohibido por Flash. La hoja de referencia de prevención de CSRF es muy buena, debes leer el enlace en mi publicación. – rook

+0

Punto razonable, en el contexto de CSRF, un atacante no podrá (hasta donde yo sé) falsificar el encabezado Referer de la víctima, sin embargo, el encabezado aún no está garantizado y lo requiere para su API solo debería hacerse si puede garantizar que siempre será enviado (como para una aplicación interna para una empresa). – RelaXNow

7

soy que consigue este derecho:

  • desea una protección contra CSRF para los usuarios conectados a través de cookies.
  • Y, al mismo tiempo, desea la interfaz RESTful para las solicitudes autenticadas de Basic, OAuth y Digest de las aplicaciones.

Así que, ¿por qué no si los usuarios se registran en medio de la galleta y aplican CSRF sólo entonces?

No estoy seguro, pero es posible que otro sitio forje cosas como la autenticación básica o los encabezados?

Por lo que sé, CSRF es todo sobre las cookies? La AUTORIZACIÓN RESTful no ocurre con las cookies.

+0

¡Me preguntaba sobre esto también! De acuerdo con este artículo https: //mathieu.fenniak.net/is-your-web-api-susceptible-a-un-csrf-exploit/Debería ser posible activar cheques CSRF si alguien viene a través de una cookie/sesión, y desactivarlo, si la solicitud está llegando algún tipo de esquema de autenticación sin estado Basic ... etc. – CMCDragonkai

+2

Tenga cuidado con la autenticación básica: es efectivamente equivalente a un usuario conectado a través de una cookie, ya que los navegadores enviarán el encabezado de autorización provisto en las siguientes solicitudes para mayor comodidad del usuario. –

0

¿Pasa algo con este enfoque, a pesar de que no funciona sin JavaScript?

Su secreto de usuario no es un secreto si lo envía al cliente. Por lo general, usamos esos secretos para generar hashes y los enviamos con el formulario, y los esperamos para compararlos.

Si desea ser RESTful, la solicitud debe contener toda la información sobre cómo procesarla. Las formas en las que puede hacer esto:

  • Añadir una cookie token CSRF con su cliente REST y enviar la misma señal en la entrada oculta con sus formas. Si el servicio y el cliente están bajo dominios diferentes, debe compartir las credenciales. En el servicio tiene que comparar los 2 tokens, y si son iguales, la solicitud es válida ...

  • Puede agregar la cookie token csrf con su servicio REST y enviar el mismo token con las representaciones de sus recursos (entradas ocultas, etc.). Todo lo demás es lo mismo que el final de la solución anterior. Esta solución está al borde de la RESTfulness.(Está bien hasta que el cliente no llame al servicio para modificar la cookie. Si la cookie es solo http, el cliente no debe saberlo, si no lo está, entonces el cliente debe configurarlo). Puede hacer más solución compleja si agrega tokens diferentes a cada formulario y agrega tiempo de expiración a las cookies. También puede enviar la fecha de vencimiento con los formularios, para que sepa el motivo por el que falla la validación de un token.

  • Puede tener un secreto de usuario (diferente para cada usuario) en el estado del recurso en su servicio. Al construir representaciones, puede generar un token (y tiempo de vencimiento) para cada formulario. Puede generar un hash del token real (y el tiempo de expiración, el método, la url, etc.) y el secreto del usuario, y también enviar ese hash con el formulario. Mantiene el "secreto de usuario" en secreto, por supuesto, por lo que nunca lo envíe con el formulario. Después de eso, si su servicio recibe una solicitud, puede generar nuevamente el hash a partir de los parámetros de solicitud y el secreto de usuario, y compararlos. Si el no coinciden, la solicitud no es válida ...

Ninguno de ellos le protegerá si su cliente REST es javascript inyectable, por lo que tiene que revisar todo el contenido de usuario frente a entidades HTML, y quitar todos ellos, o use TextNodes siempre en lugar de innerHTML. También debe protegerse contra la inyección de SQL y la inyección de encabezado HTTP. Nunca use FTP simple para actualizar su sitio. Y así sucesivamente ... Hay muchas maneras de inyectar código malvado en su sitio ...

Casi olvidé mencionar que las solicitudes GET siempre son para lectura tanto por el servicio como por el cliente. Por el servicio esto es obvio, si el cliente establece que cualquier url en el navegador debe dar como resultado una representación de un recurso o múltiples recursos, nunca debe llamar a un método POST/PUT/DELETE en un recurso. Por ejemplo, GET http://my.client.com/resource/delete -> DELETE http://my.api.com/resource es una solución muy mala. Pero esta es una habilidad muy básica si quieres obstaculizar CSRF.

Cuestiones relacionadas