Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to securely upload files in Node with Express?

There are many articles, tutorials, and questions about file uploads in node but mostly they are for beginners and none of them fully explains how to secure file uploads for production. I have tried very hard to find a complete answer on how to do it, but it was not successful.

Below is an explanation of my findings.

  1. Limit file size on uploads:

    app.use(express.limit('4mb'));
    
  2. Limit file uploads to only certain routes: I can't get this to actually work but here is what I have tried:

    Replace:

    app.use(express.bodyParser());
    

    with

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

    and add the multipart middleware to every upload route:

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

    This part doesn't work, but the upload works fine if I leave express.bodyParser(). So what am I doing wrong?

  3. Checking uploaded file type before saving upload to disk:

    I couldn't figure this part out, but a suggestion was to write a custom middleware that uses formidable to parse file uploads and trying to resize the file before it is saved (assuming that it is an image) using a library like image magic. The suggestion was that this would make the image safe and ensure that it is actually an image (because the process would fail if it is not an image).

    This would only work with images though, so it is not a complete solution.

    How can I implement this? Any example code?

Is there anything else that I am missing for uploads to be safe?

like image 673
Marwan Roushdy Avatar asked Jan 22 '13 14:01

Marwan Roushdy


2 Answers

Approach 2 actually works. The problem I had was that

app.use(passport.session());

was stopping it from working. So, if you are using passport.js for authentication this might be the issue. If you use this approach just make sure to add the security on the actual route.


I ended up using this plugin

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

which works great with image uploads and automatically resizes the files to multiple versions and uploads them to amazon s3. Using this plugin would be inline with using approach 3, but the files are uploaded to the tmp folder first and then deleted.

like image 152
Marwan Roushdy Avatar answered Sep 30 '22 15:09

Marwan Roushdy


I am using multiparty for uploading (and streaming) files.

var form = new multiparty.Form();

To 1:

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

implement your own abort Connection function; e.g.:

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

warning: the browser will most probably retry the upload (up to 4 times). I am using a websocket connection to cancel the upload on the client side.

To 2: (use multiparty)

To 3: I created a gist that shows how to check the mime-type on the fly using mmmagic.

If you are using passport in combination with multiparty you might find this useful:

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

like image 35
chmanie Avatar answered Sep 30 '22 15:09

chmanie