Как безопасно загружать файлы в Node с помощью Express?

Есть много статей, руководств и вопросов о загрузке файлов в node, но в основном они предназначены для начинающих, и ни одна из них полностью не объясняет, как защитить загрузку файлов для производства. Я очень старался найти полный ответ о том, как это сделать, но безуспешно.

Ниже приводится объяснение моих выводов.

  1. Ограничить размер файла при загрузке:

    app.use(express.limit('4mb'));
    
  2. Ограничьте загрузку файлов только определенными маршрутами: я не могу заставить это работать, но вот что я пробовал:

    Заменять:

    app.use(express.bodyParser());
    

    с

    app.use(express.json());
    app.use(express.urlencoded());
    

    и добавьте составное промежуточное ПО к каждому маршруту загрузки:

    app.post('/upload', express.multipart(), uploadController.uploadPhoto);
    

    Эта часть не работает, но загрузка работает нормально, если я оставлю express.bodyParser(). Так что я делаю неправильно?

  3. Проверка типа загружаемого файла перед сохранением загрузки на диск:

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

    Однако это будет работать только с изображениями, поэтому это не полное решение.

    Как я могу реализовать это? Любой пример кода?

Есть ли что-то еще, чего мне не хватает, чтобы загрузка была безопасной?


person Marwan Roushdy    schedule 22.01.2013    source источник
comment
3 был бы путь, предполагая, что 2 не работает. Если да, то это будет идеальным решением.   -  person Juzer Ali    schedule 22.01.2013
comment
Из того, что я прочитал, 2 должно работать, я что-то не так делаю? Также у вас есть примеры того, как сделать 3? Я пытался это сделать и не преуспел. Большая часть кода, который я нашел, не работала с новой версией Express.   -  person Marwan Roushdy    schedule 22.01.2013
comment
Не могли бы вы создать наименьший возможный проект, используя 2, и поделиться кодом, возможно, на github. Я посмотрю на это. Если не получится, я соберу для вас 3 и поделюсь.   -  person Juzer Ali    schedule 23.01.2013
comment
В итоге я использовал плагин под названием alleup, который сначала загружает файлы в tmp, а затем изменяет их размер, а затем удаляет их из tmp, поэтому он не проверяет тип файла перед его сохранением. Как вы думаете, это достаточно безопасно?   -  person Marwan Roushdy    schedule 23.01.2013


Ответы (2)


Подход 2 действительно работает. Проблема у меня была в том, что

app.use(passport.session());

мешало ему работать. Таким образом, если вы используете passport.js для аутентификации, это может быть проблемой. Если вы используете этот подход, просто убедитесь, что добавили безопасность на фактический маршрут.


Я закончил тем, что использовал этот плагин

https://github.com/tih-ra/alleup

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

person Marwan Roushdy    schedule 23.01.2013
comment
Я думал, что ваш первоначальный вопрос был о защите путей для загрузки файлов. Скажем, например, кто-то публикует составную форму с файлом, прикрепленным к пути /restricted-path, что бы вы тогда сделали? - person Juzer Ali; 23.01.2013
comment
ну, плагин использует грозный для обработки загрузки файлов, и для этого не следует использовать экспресс-мультипарт, поэтому загрузки не могут быть отправлены только на любые маршруты. У меня есть еще одна проблема, хотя паспорт.сессия() останавливается от запуска файловых событий formidable-from-triggering-file-even" title="in node js, почему сессия с паспортом останавливается из-за запуска файла даже"> stackoverflow.com/questions/14479343/, поэтому мне нужно либо решить эту проблему, либо вычислить как сделать номер 2. - person Marwan Roushdy; 23.01.2013
comment
Итак, я понял, что app.use(passport.session()) делает номер 2 неработающим. Я думаю, что это связано с тем, что pass.session() нужно вызывать после multipart, а не перед ним. Я до сих пор не уверен, как это сделать на маршруте по маршрутным базам - person Marwan Roushdy; 23.01.2013

Я использую multiparty для загрузки (и потоковой передачи) файлов.

var form = new multiparty.Form();

To 1:

form.on('progress', function (bytesReceived) {
  if (262144000 < bytesReceived) {
   abortConnection('filesizeexeeded');
  }
});

реализовать собственную функцию прерывания соединения; например.:

function abortConnection(reason) {
  res.writeHead(413, { 'Connection': 'close' });
  return res.end(reason);
}

предупреждение: браузер, скорее всего, повторит попытку загрузки (до 4 раз). Я использую соединение через веб-сокет, чтобы отменить загрузку на стороне клиента.

Кому 2: (использовать многопартийность)

К 3: я создал gist, который показывает, как проверять mime-тип на лету, используя mmmagic.

Если вы используете паспорт в сочетании с многопартийностью, вам может пригодиться следующее:

https://github.com/jaredhanson/passport/pull/106#issuecomment-14188999

person chmanie    schedule 20.01.2014
comment
Выглядит хорошо, но как вы получаете файловый поток, когда используете мультипати? У меня была эта проблема давно, и я до сих пор не могу найти ответ. - person Light Flow; 01.12.2016