2012-06-26 7 views
20

Próbuję zaimplementować ochronę CSRF w aplikacji zbudowanej przy użyciu pliku node.js przy użyciu frameworku express.js. Aplikacja wykonuje obfite wykorzystanie wywołań postów Ajax na serwerze. Rozumiem, że framework connect zapewnia oprogramowanie warstwy pośredniej CSRF, ale nie jestem pewien, jak go wdrożyć w zakresie postów postów Ajax po stronie klienta.Jak zaimplementować ochronę CSRF w wywołaniach Ajax za pomocą express.js (szukając pełnego przykładu)?

Jest kilka rzeczy na ten temat w innych pytaniach zamieszczonych tutaj w stackoverflow, ale muszę jeszcze znaleźć dość kompletny przykład, jak zaimplementować go zarówno po stronie klienta, jak i serwera.

Czy ktoś ma przykład pracy, którym zależy na tym, jak go wdrożyć? Większość przykładów, które widziałem, zakłada, że ​​renderujesz formularz po stronie serwera, a następnie przesyłasz go (wraz z osadzonym polem formularza csrf_token) po stronie klienta. W mojej aplikacji cała treść jest renderowana po stronie klienta (w tym szablony) za pośrednictwem Backbone.js. Wszystko, co robi serwer, to dostarczanie wartości w formacie JSON, które są wykorzystywane przez różne modele w Backbone.js po stronie klienta. Według mojego rozumowania będę musiał najpierw pobrać csrf_token przez ajax, zanim będzie można go użyć. Obawiam się jednak, że może to być problematyczne z punktu widzenia bezpieczeństwa. Czy to jest ważny problem?

Odpowiedz

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}); 
}); 

W somelayout.jade

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

CSRF middleware tylko generuje CSRF żeton raz na sesję, więc prawdopodobnie nie zmieni się przez cały czas trwania wizyty użytkownika .

Ponadto nie sprawdza tokena w żądaniach GET i HEAD. Tak długo, jak token znajduje się w żądaniu (nagłówek, treść lub zapytanie), jesteś dobry. To prawie wszystko.

+3

Dzięki za szybką odpowiedź. W mojej aplikacji wszystkie treści po stronie klienta są wykonywane za pośrednictwem ajax. Rzeczywiste renderowanie treści (w tym szablonów) wykonywane jest po stronie klienta. Wszystko, co robi serwer, to dostarczanie zmiennych danych do strony klienta w formacie JSON. Oznaczałoby to, że muszę odzyskać token CSRF za pośrednictwem ajax, aby renderować go na stronie, aby następnie mógł zostać odesłany z powrotem do żądania ajax ajax. Obawiam się, że może to być problematyczne z punktu widzenia bezpieczeństwa, czy jest to ważny problem? – Benjen

+0

Jeśli możesz, dobrym sposobem na uzyskanie CSRF dla klienta jest renderowanie go wewnątrz znacznika 'meta' (a la Rails) lub czegoś podobnego z Express. –

3

Ponieważ korzystasz z aplikacji Backbone.js, zakładam, że jest to SPA i najpierw ładujesz plik index.html, a następnie inne żądania są wysyłane za pośrednictwem połączeń ajax. Jeśli tak, możesz dodać mały fragment kodu JS do swojego pliku index.html, aby przechowywać token crsf dla przyszłych połączeń ajax.

Na przykład:

index.html (używając Kierownica dla szablonów ...)

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

Podczas renderowania pliku index.html, nadać mu csrf żeton że express ramy wygenerowany tutaj : req.session._csrf

Podczas korzystania z Backbone.js ustawia globalną zmienną o nazwie Backbone. Wszystko, co robi poprzednia funkcja, ustawia właściwość o nazwie csrf na globalny obiekt Backbone. A gdy wykonasz wywołanie ajax do danych , po prostu dodaj zmienną Backbone.csrf do danych jako _csrf, która jest wysyłana za pośrednictwem wywołania ajax.

31

Można to zrobić poprzez dodanie meta znacznik do CSRF tokena, a następnie przekazać CSRF żeton z każdego żądania Ajax

Server

Dodaj CSRF middleware

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

można przekazać token CSRF do po stronie klienta za pośrednictwem, powiedzmy, metatagu. Dla ex, w Jade

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

Klienta

jQuery posiada funkcję o nazwie ajaxPrefilter, która pozwala zapewnić zwrotnego powoływać wszelkie żądania Ajax. Następnie ustaw nagłówek za pomocą 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

prawdopodobnie najlepsza odpowiedź tutaj – FRD

+1

to powinno być zaakceptowane odpowiedź – user3658423

+3

Nie zgadzam się. To dobra odpowiedź, ale nie rozwiązuje problemu braku renderowania po stronie serwera. Wspomina o użyciu Jade do renderowania metatagu na stronie. Ale wartość tego tagu musi zostać wygenerowana po stronie serwera, a ponieważ oryginalny pytający mówi, że żaden z jego widoków nie jest generowany po stronie serwera, to nie zadziała. – Bill

1

W Serwer:

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(); 
}); 

klient: (szablon haust)

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

W jaki sposób sprawdzasz token po stronie serwera? –

0

1. Dodaj ochrona 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. Dodaj beforeSend zwrotnego używając $.ajaxSetup: (dodaj to gdzieś przed wszystkimi swoimi ajax połączeń)

$.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. to jest to! teraz możesz wysyłać zapytania ajaxowe i nie musisz dodawać niczego w nagłówkach lub jako parametr żądania, aby przejść przez csrf.

Powiązane problemy