2011-09-29 10 views
40

En febrero de 2011, Rails se cambió a solicitudes de require the CSRF token for all non-GET, incluso las de un punto final de API. Entiendo la explicación de por qué este es un cambio importante para las solicitudes del navegador, pero esa publicación de blog no ofrece ningún consejo sobre cómo una API debe manejar el cambio.Diseño de API de rieles sin deshabilitar la protección de CSRF

No estoy interesado en deshabilitar la protección CSRF para ciertas acciones.

¿Cómo se supone que las API se ocupen de este cambio? ¿La expectativa de que un cliente API haga una solicitud GET a la API para obtener un token CSRF, luego incluye ese token en cada solicitud durante esa sesión?

Parece que el token no cambia de un POST a otro. ¿Es seguro asumir que el token no cambiará durante la sesión?

No me gusta el manejo de error adicional cuando la sesión expira, pero supongo que es mejor que tener que OBTENER un token antes de cada solicitud POST/PUT/DELETE.

Respuesta

4

Rails funciona con la convención 'segura por defecto'. La falsificación de solicitudes entre sitios o entre sesiones requiere que el usuario tenga un navegador y otro sitio web confiable. Esto no es relevante para las API, ya que no se ejecutan en el navegador y no mantienen ninguna sesión. Por lo tanto, debe desactivar CSRF para API.

Por supuesto, usted debe proteger su API requiriendo Autenticación HTTP o un token API implementado personalizado o una solución OAuth.

+2

La página que he vinculado no coincide: "Ciertas combinaciones de complementos de navegador y redireccionamientos HTTP se pueden utilizar para engañar al navegador del usuario para realizar solicitudes entre dominios que incluyen encabezados HTTP arbitrarios especificados por el atacante. Un atacante puede utilizarlo para suplantar ajax y las solicitudes API y eludir la protección CSRF incorporada y atacar con éxito una aplicación ". –

+9

"Esto no es relevante para las API, ya que no se ejecutan en el navegador": a veces el navegador * es * el cliente de la API, es decir, – antinome

+1

http://stackoverflow.com/questions/9362910/rails -warning-cant-verify-csrf-token-authenticity-for-json-devise-requests/10049965 # 10049965 – montrealmike

41

Una vieja pregunta pero la seguridad es lo suficientemente importante como para sentir que merece una respuesta completa. Como se discutió en este documento question, aún existe cierto riesgo de CSRF incluso con API. Sí, se supone que los navegadores evitan esto por defecto, pero como no tiene el control completo del navegador y de los complementos que el usuario ha instalado, se debe considerar como una mejor práctica para protegerse contra CSRF en su API.

La forma en que lo he visto a veces es analizar la metaetiqueta CSRF desde la propia página HTML. Realmente no me gusta esto, ya que no encaja bien con la forma en que funcionan muchas aplicaciones de una sola página + API hoy en día y creo que el token CSRF debe enviarse en cada solicitud, independientemente de si es HTML, JSON o XML.

Así que sugeriría pasar un token CSRF como un valor de cookie o encabezado a través de un filtro posterior para todas las solicitudes. La API simplemente puede volver a enviar esa vuelta como un valor de encabezado de X-CSRF-Token que Rails ya verifica.

Así es como lo hice con AngularJS:

# In my ApplicationController 
    after_filter :set_csrf_cookie 

    def set_csrf_cookie 
    if protect_against_forgery? 
     cookies['XSRF-TOKEN'] = form_authenticity_token 
    end 
    end 

AngularJS automatically looks for a cookie nombrados XSRF-TOKEN pero no dude en llamarlo cualquier cosa que desee para sus propósitos. Luego, cuando envíe un mensaje POST/PUT/DELETE, debe establecer la propiedad del encabezado X-CSRF-Token que Rails busca automáticamente.

Desafortunadamente, AngualrJS ya envía de vuelta la cookie XSRF-TOKEN en un valor de encabezado de X-XSRF-TOKEN. Es fácil de anular el comportamiento por defecto de Rails para acomodar esto en ApplicationController así:

protected 

    def verified_request? 
    super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] 
    end 

para los carriles 4.2 hay una construida en ayudante ahora para validar CSRF que se debe utilizar.

protected 

    def verified_request? 
    super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN']) 
    end 

Espero que sea útil.

EDIT: en un discussion on this for a Rails pull-request presenté que pasar el token CSRF a través de la API para iniciar sesión es una práctica particularmente mala (por ejemplo, alguien podría crear un inicio de sesión de terceros para su sitio que use credenciales de usuario en lugar de tokens) . Así que cavet emptor. Depende de usted decidir qué tan preocupado está sobre eso para su aplicación. En este caso, podría seguir utilizando el enfoque anterior, pero solo devolverá la cookie CSRF a un navegador que ya tenga una sesión autenticada y no para cada solicitud. Esto evitará enviar un inicio de sesión válido sin usar la metaetiqueta CSRF.

+3

Tu edición es crucial: el servidor SOLO debe establecer la cookie CSRF para los usuarios autenticados. – brookr

+0

¿Qué sugieres para autenticar entonces? Simplemente formularios basados ​​en autenticación con el token CSRF establecido en el HTML como de costumbre, y luego API con encabezado o CSRF basado en cookies después? –

+0

Establecer una cookie sin 'httponly' no parece seguro. ¿No impediría 'httponly' que JS no acceda a la cookie CSRF? En cambio, se pasarían junto con la solicitud si usa [fetch's 'credentials: 'same-origin'' parameter] (https://github.com/github/fetch#sending-cookies) por ejemplo. ¿Y por qué utilizar un nombre no convencional para la cookie? Si usa 'X-CSRF-Token' ActionDispatch lo comprobará automáticamente sin anular la petición verified? necesario. Recomendaría: 'cookies [" X-CSRF-Token "] = {value: form_authenticity_token, httponly: true}' –

Cuestiones relacionadas