2012-06-26 27 views
20

Estoy tratando de implementar la protección CSRF en una aplicación creada usando node.js usando el framework express.js. La aplicación hace un uso abundante de las llamadas posteriores de Ajax al servidor. Entiendo que connect framework proporciona el middleware CSRF, pero no estoy seguro de cómo implementarlo en el ámbito de las solicitudes de publicación Ajax del lado del cliente.¿Cómo implementar la protección CSRF en llamadas Ajax usando express.js (buscando el ejemplo completo)?

Hay partes y piezas acerca de esto en otras preguntas publicadas aquí en stackoverflow, pero todavía tengo que encontrar un ejemplo razonablemente completo de cómo implementarlo desde el lado del cliente y del servidor.

¿Alguien tiene un ejemplo de trabajo que les interese compartir sobre cómo implementar esto? La mayoría de los ejemplos que he visto suponen que estás renderizando el formulario en el lado del servidor y luego enviándolo (junto con el campo de formulario csrf_token incrustado) al lado del cliente. En mi aplicación, todo el contenido se representa en el lado del cliente (incluidas las plantillas) a través de Backbone.js. Todo lo que hace el servidor es proporcionar valores en formato JSON, que son utilizados por varios Modelos en Backbone.js en el lado del cliente. Según tengo entendido, necesitaría recuperar csrf_token a través de ajax antes de poder usarlo. Sin embargo, me preocupa que esto pueda ser problemático desde el punto de vista de la seguridad. ¿Es esta una preocupación valida?

Respuesta

6

server.js

... 
// All Cookies/Sessions/BodyParser go first 
app.use(express.csrf()); 
... 
// Get the request 
app.post('/ajax', function(req, res){ 
    res.render('somelayout', {csrf_token: req.session._csrf}); 
}); 

En somelayout.jade

input(type='hidden', name='_csrf', value=csrf_token) 

El middleware CSRF sólo genera el CSRF token de una vez por sesión, por lo que probablemente no va a cambiar durante la duración de la visita de un usuario .

Además, no comprueba el token en las solicitudes GET y HEAD. Siempre que el token esté en la solicitud (encabezado, cuerpo o consulta), eres bueno. Eso es todo lo que hay que hacer.

+3

Gracias por la respuesta rápida. En mi aplicación, todo el contenido del lado del cliente se realiza a través de ajax. La representación real del contenido (incluidas las plantillas) se realiza en el lado del cliente. Todo lo que hace el servidor es proporcionar datos variables al lado del cliente en formato JSON. Esto significaría que tengo que recuperar el token CSRF a través de ajax para presentarlo en la página para que pueda enviarse de vuelta en la solicitud posterior ajax. Me preocupa que esto pueda ser problemático desde el punto de vista de la seguridad, ¿es esto una preocupación válida? – Benjen

+0

Si puede, una buena forma de obtener el CSRF para el cliente es hacerlo en una etiqueta 'meta' (a la Rails) o algo similar de Express. –

3

Dado que está utilizando Backbone.js para su aplicación, supongo que es un SPA e inicialmente carga un archivo index.html, luego realice las demás solicitudes a través de las llamadas ajax. De ser así, puede agregar un pequeño fragmento de código JS a su archivo index.html para contener el token crsf para cualquier llamada futura ajax.

Por ejemplo:

index.html (utilizando un manillar para plantillas ...)

<!DOCTYPE html> 
<html> 
    <head> 
     ... 
     <script type="text/javascript"> 
      $(function() { 
       window.Backbone.csrf = "{{csrfToken}}"; 
      }); 
     </script> 
    </head> 
    <body> 
     ... 
    </body> 
</html> 

Cuando procesa el archivo index.html, darle el token csrf que el marco expreso genera aquí : req.session._csrf

Cuando usa Backbone.js, establece una variable global llamada Backbone. Todo lo que hace la función anterior es marcar una propiedad llamada csrf en el objeto global Backbone. Y cuando realiza una llamada ajax a POST datos, simplemente agregue la variable Backbone.csrf a los datos como _csrf que se están enviando a través de la llamada ajax.

31

Se puede hacer mediante la adición de meta etiqueta para CSRF token y luego pasan token CSRF con cada petición Ajax

servidor

Añadir CSRF middleware

app.use(express.csrf()); 
app.use(function (req, res, next) { 
    res.locals.token = req.session._csrf; 
    next(); 
}); 

Puede pasar un token CSRF a el lado del cliente mediante, digamos, una metaetiqueta. Por ej, en Jade

meta(name="csrf-token", content="#{token}") 

cliente

jQuery tiene una característica llamada ajaxPrefilter, lo que le permite proporcionar una devolución de llamada que se invoca cada petición Ajax. Luego configure un encabezado usando ajaxPrefilter.

var CSRF_HEADER = 'X-CSRF-Token'; 

var setCSRFToken = function (securityToken) { 
    jQuery.ajaxPrefilter(function (options, _, xhr) { 
    if (!xhr.crossDomain) { 
     xhr.setRequestHeader(CSRF_HEADER, securityToken); 
    } 
    }); 
}; 

setCSRFToken($('meta[name="csrf-token"]').attr('content')); 
+1

probablemente la mejor respuesta aquí – FRD

+1

esto debe aceptarse respuesta – user3658423

+3

No estoy de acuerdo. Es una buena respuesta, pero no soluciona la falta de procesamiento del lado del servidor. Menciona el uso de Jade para representar una metaetiqueta en la página. Pero el valor de esa etiqueta debe generarse desde el lado del servidor y, dado que el solicitante original dice que ninguna de sus vistas se genera en el lado del servidor, esto no funcionará. – Bill

1

En Servidor:

app.use(function (req, res) { 
    res.locals._csrf = req.csrfToken(); 
    res.locals.csrf_form_html = '<input type="hidden" name="_csrf" value="' + req.csrfToken() + '" >'; 
    req.next(); 
}); 

En Cliente: (plantilla trago)

var csrf = {{ _csrf|json|safe }}; 

$.ajaxSetup({ 
    headers: { 
    'X-CSRF-Token': csrf 
    } 
}); 

$.post("/create", data, function(result) { 
    console.log(result); 
}).fail(function(){ 
    console.log(arguments); 
}); 
+0

¿Cómo se valida el token en el lado del servidor? –

0

1. Añadir protección CSRF middleware:

app.use(csrf({cookie: true})); 

// csrf middleware 
app.use(function (req, res, next) { 
    res.cookie('X-CSRF-Token', req.csrfToken()); 
    // this line below is for using csrfToken value in normal forms (as a hidden input) 
    res.locals.csrfToken = req.csrfToken(); 
    next(); 
}); 

// routing setup goes here 

2. añadir una devolución beforeSend usando $.ajaxSetup: (añadir esto en alguna parte antes de que todas sus llamadas ajax)

$.ajaxSetup({ 
beforeSend: function (xhr, settings) { 
    function getCookie(name) { 
     var cookieValue = null; 
     if (document.cookie && document.cookie != '') { 
      var cookies = document.cookie.split(';'); 
      for (var i = 0; i < cookies.length; i++) { 
       var cookie = jQuery.trim(cookies[i]); 
       // Does this cookie string begin with the name we want? 
       if (cookie.substring(0, name.length + 1) == (name + '=')) { 
        cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 
        break; 
       } 
      } 
     } 
     return cookieValue; 
    } 

    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { 
     // Only send the token to relative URLs i.e. locally. 
     xhr.setRequestHeader("X-CSRF-Token", getCookie('X-CSRF-Token')); 
    } 
} 
}); 

3. Eso es todo! ahora puede enviar solicitudes ajax y no necesita agregar nada en los encabezados o como un parámetro de solicitud para pasar por csrf.

Cuestiones relacionadas