2013-09-12 2 views
12

Я реализовал CSRF (запрос межсайтовое подлог) защиты в экспрессе так:Как проверить конечные точки, защищенные CSRF в Node.js/выразить

... 
app.use(express.csrf()); 
app.use(function (req, res, next) { 
    res.cookie('XSRF-TOKEN', req.csrfToken()); 
    next(); 
}); 
... 

Это прекрасно работает. Angularjs использовал токен csrf во всех запросах, сделанных через службу $ http. Запросы, которые я делаю через свое угловое приложение, отлично работают.

Моя проблема заключается в проверке этих конечных точек api. Я использую mocha для запуска своих автоматических тестов и модуля запроса для тестирования конечных точек api. Когда я делаю запрос к конечной точке, использующей csrf (POST, PUT, DELETE и т. Д.) С помощью модуля запроса, он терпит неудачу, хотя он правильно использует файлы cookie и т. Д.

Есть ли у кого-нибудь еще решение? Кому нужна дополнительная информация?

Пример испытания:

function testLogin(done) { 
    request({ 
    method: 'POST', 
    url: baseUrl + '/api/login', 
    json: { 
     email: '[email protected]', 
     password: 'mypassword' 
    } 
    }, function (err, res, body) { 
    // do stuff to validate returned data 
    // the server spits back a 'FORBIDDEN' string, 
    // which obviously will not pass my validation 
    // criteria 
    done(); 
    }); 
} 
+0

Можете ли вы поместить образец тестов, которые вы пытались сделать? – jjperezaguinaga

ответ

9

Фокус в том, что вам нужно обернуть ваш тест POST внутри GET и проанализировать нужный токен CSRF из файла cookie. Во-первых, это предполагает, что вы создаете Угловое-совместимый CSRF печенье, как это:

.use(express.csrf()) 
.use(function (req, res, next) { 
    res.cookie('XSRF-TOKEN', req.session._csrf); 
    res.locals.csrftoken = req.session._csrf; 
    next(); 
}) 

Затем ваш тест может выглядеть следующим образом:

describe('Authenticated Jade tests', function() { 
    this.timeout(5000); 

    before(function (done) { 
    [Set up an authenticated user here] 
    }); 

    var validPaths = ['/help', '/products']; 

    async.each(validPaths, function (path, callback) { 
    it('should confirm that ' + path + ' serves HTML and is only available when logged in', function (done) { 
     request.get('https://127.0.0.1:' + process.env.PORT + path, function (err, res, body) { 
     expect(res.statusCode).to.be(302); 
     expect(res.headers.location).to.be('/login'); 
     expect(body).to.be('Moved Temporarily. Redirecting to /login'); 

     var csrftoken = unescape(/XSRF-TOKEN=(.*?);/.exec(res.headers['set-cookie'])[1]); 
     var authAttributes = { _csrf: csrftoken, email: userAttributes.email, password: 'password' }; 

     request.post('https://127.0.0.1:' + process.env.PORT + '/login', { body: authAttributes, json: true }, function (err, res) { 
      expect(res.statusCode).to.be(303); 

      request.get('https://127.0.0.1:' + process.env.PORT + path, function (err, res, body) { 
      expect(res.statusCode).to.be(200); 
      expect(body.toString().substr(-14)).to.be('</body></html>'); 

      request.get('https://127.0.0.1:' + process.env.PORT + '/bye', function() { 
       done(); 
      }); 
      }); 
     }); 
     }); 
    }); 

    callback(); 
    }); 
}); 

Идея заключается в том, чтобы фактически войти в систему и использовать после лексемы CSRF вы получаете от печенья. Обратите внимание, что вам необходимо следующее в верхней части тестового мокко файла:

var request = require('request').defaults({jar: true, followRedirect: false}); 
+0

Похоже, это будет тот, кто это сделает. Я надеялся, что мне не придется вложить такие запросы, поэтому я могу просто отключить csrf при запуске тестов. – tytho

+0

Это, по общему признанию, немного боль, но вы не можете реалистично провести сквозной тест, если не оставить CSRF. – dankohn

+0

Это правда ... Я также подумал об использовании другой структуры тестирования, такой как phantomjs, где я могу проверить больше взаимодействия с приложением, а не только с конечными точками, но, полагаю, было бы хорошо сделать то и другое. Спасибо за вашу помощь! – tytho

1

, что я сделать, это выставить CSRF токен только в непроизводственной:

if (process.env.NODE_ENV !== 'production') { 
    app.use('/csrf', function (req, res, next) { 
    res.json({ 
     csrf: req.csrfToken() 
    }) 
    }) 
} 

тогда это будет первый тест и сохранить его как глобальный. вам придется использовать агента в своих тестах, чтобы вы последовательно использовали один и тот же сеанс.

+0

Как я мог бы использовать токен csrf в моем запросе выше? Если я добавлю jar: true к объекту запроса, он использует файлы cookie, и я вижу, как используется токен и cookie, но по какой-то причине он его не принимает. Итак, как я могу использовать токен в запросе, чтобы заставить его принять его? – tytho

+0

вы должны отправить его. т.е. 'x-csrf-token: csrf' или добавить его в тело запроса. да, это раздражает, но это именно то, что должны делать пользователи. убедитесь, что сеанс не изменяется при каждом запросе. –

+0

Я выполнил поиск объектов запроса и ответа на стороне тестирования, а серверная сторона и токен совпадают в последовательных запросах. Это так, как только я использую переднюю часть сайта, попадая в те же конечные точки. Файл cookie существует, и он добавляется в тело запроса по каждому запросу после первого. – tytho

1

@dankohn «s отличный ответ был наиболее полезным. С тех пор ситуация немного изменилась, и в отношении как supertest, так и модуля csurf. Поэтому в дополнение к этому ответу я обнаружил, что в ПОЧТУ необходимо передать следующее:

it('should ...', function(done) { 
    request(app) 
     .get('/...') 
     .expect(200) 
     .end(function(err, res) { 
     var csrfToken = unescape(/XSRF-TOKEN=(.*?);/.exec(res.headers['set-cookie'])[1]); 
     assert(csrfToken); 
     request(app) 
      .post('/...') 
      .set({cookie: res.headers['set-cookie']}) 
      .send({ 
      _csrf: csrfToken, 
      ... 
      }) 
      .expect(200) 
      .end(done); 
     }); 
    }); 
Смежные вопросы