Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to validate by extension? #114

Closed
alvarotrigo opened this issue Apr 7, 2015 · 17 comments
Closed

How to validate by extension? #114

alvarotrigo opened this issue Apr 7, 2015 · 17 comments

Comments

@alvarotrigo
Copy link

How could I use multer to validate the extension so it can only allow to upload PDF files?

@jpfluger
Copy link
Contributor

jpfluger commented May 1, 2015

It would be nice to have a filter by extension. multer currently does not have an option property for this.

But you should be able to customize the 'options.onFileUploadStart' function. Pass in something like::

onFileUploadStart: function(file, req, res) {
   return (file.ext.toLowerCase() === 'pdf')
}

Did that help?

@LinusU
Copy link
Member

LinusU commented Jul 18, 2015

This is now possible in 1.0.0.

If you want to abort the upload:

multer({
  fileFilter: function (req, file, cb) {
    if (path.extension(file.originalname) !== '.pdf') {
      return cb(new Error('Only pdfs are allowed'))
    }

    cb(null, true)
  }
})

If you want to skip any files that is not pdf:

multer({
  fileFilter: function (req, file, cb) {
    if (path.extension(file.originalname) !== '.pdf') {
      return cb(null, false)
    }

    cb(null, true)
  }
})

@LinusU LinusU closed this as completed Jul 18, 2015
@tyleralves
Copy link

EDIT: Nevermind, I did some googling, and see that you are considering a magic numbers implementation.

Is this robust enough to prevent abuse of file upload? Or could someone theoretically upload a malicious file that just has the extension .pdf? I have read a lot about the magic bytes method for validating file-types, but would prefer this if equivalent.

Thanks!

@ex-tag
Copy link

ex-tag commented Jul 10, 2016

A file (and therefore it's file extension) can be renamed. It's better to check files by mimetype and magic numbers as well. Not sure how to test magic numbers with Multer, but did ask for a field named "magicNumber" #155.

const path = require('path');

multer({
  fileFilter: function (req, file, cb) {

    var filetypes = /jpeg|jpg/;
    var mimetype = filetypes.test(file.mimetype);
    var extname = filetypes.test(path.extname(file.originalname).toLowerCase());

    if (mimetype && extname) {
      return cb(null, true);
    }
    cb("Error: File upload only supports the following filetypes - " + filetypes);
  }
});

@srkkhan
Copy link

srkkhan commented Aug 16, 2016

This worked for me:

fileFilter: function (req, file, cb) {
    if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
        return cb(new Error('Only image files are allowed!'));
    }
    cb(null, true);
  }

@mxmaxime
Copy link

mxmaxime commented Apr 14, 2017

To be sure about files types, we need to use magic number.

import fileType from 'file-type';

multer({
  fileFilter (req, file, cb) {
    // How can I get the buffer to read and test ?
  const infoFile = fileType(file.buffer);
  }
});

@slavafomin
Copy link

slavafomin commented Jul 2, 2017

@EmixMaxime

// How can I get the buffer to read and test ?

If you are using memory store, then file.buffer should be set. Otherwise, you can use fs.readFile() to actually read the file from file.path. However, I don't see any reason to use filesystem storage to later read the file back into memory again.

@jonataswalker
Copy link

jonataswalker commented Jul 8, 2017

@slavafomin

Where do you see file.path? I mean inside fileFilter.

@slavafomin
Copy link

@jonataswalker I'm not using fileFilter, but if the same instance of the file is passed to it, then you will be able to access file.buffer of file.path depending on the store you are using.

@pratham2003
Copy link

pratham2003 commented Aug 23, 2017

Sorry but this is kind of a newbie question and a bit off topic but how do I handle the error that is thrown? i.e. how do i return this error to the client/user from the server side? Current this results in an internal server error kind of response.

P.S. : I came across this : https://github.com/koajs/koa/wiki/Error-Handling
But again, how do I prevent it from returning any other kind of errors thrown by other things? by something like a SQL lib and only display my own custom errors that I want to be shown?

P.S. 2 : This works, but is it safe? I don't want to return any other error (SQL etc) that would disclose too much information.

async function handleFileError(ctx, next) {
  try {
    await next()
    if (ctx.status === 404) ctx.throw(404)
  } catch (err) {
    console.error(err.message)
    ctx.status = err.status || 500
    ctx.body = err.message;
  }
}

@slavafomin
Copy link

slavafomin commented Aug 23, 2017

@pratham2003 The general principle is that you have a central error handler, which captures all the errors of your application and translates them into the HTTP response. I believe all servers should support this one way or another. I know for sure it works in Express and Restify this way.

Then, in your error handler, you can decide what errors and what details should be displayed. It's very convenient to rely on environment here, because you can show all errors with stack traces in development/test and only show error messages in production. Also, you can filter by error type, just make sure to have different classes for different errors.

Also, in the future I would recommend you to ask such questions on StackOverflow, rather than writing an off-topic questions on GitHub.

@2pay
Copy link

2pay commented Dec 4, 2017

I am using this:

fileFilter: function(req, file, cb) {
    if (file.mimetype !== 'image/png' || file.mimetype !== 'image/gif' || file.mimetype !== 'image/jpeg') 
    {
        return cb(null, false);
    } else {
        cb(null, true);
    }
}

@syntheticgoo
Copy link

I am using this:

fileFilter: function(req, file, cb) {
    if (file.mimetype !== 'image/png' || file.mimetype !== 'image/gif' || file.mimetype !== 'image/jpeg') 
    {
        return cb(null, false);
    } else {
        cb(null, true);
    }
}

This should be (notice || replaced with &&):

fileFilter: function(req, file, cb) {
    if (file.mimetype !== 'image/png' && file.mimetype !== 'image/gif' && file.mimetype !== 'image/jpeg') 
    {
        return cb(null, false);
    } else {
        cb(null, true);
    }
}

or else it will always fail validation if not a png.

@SarathVavachan
Copy link

SarathVavachan commented Aug 23, 2020

Multer doesn't have File validation using Magic number.

What you could do instead is use NPM packages like read-chunk and file-type to detect the file type after it's uploaded. Since we are using readchunk to read only the beginning of the file as a Buffer, it will not utilize the memory much.

The following are the rules in this approach.

  1. File validation should happen after the file upload using Multer.
  2. You need to use an extra variable for the stored File Route.

First, get the stored file Route.
Define the variable in advance like

let storedFileRoute = "";

and store the file name to this variable when you process the file using Multer like

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "uploads/");
  },
  filename: (req, file, cb) => {
  ModifiedFileName = Date.now()+"-"+file.originalname.trim().toLowerCase().split(" ").join("-");
  storedFileRoute = "uploads/"+ModifiedFileName;
    cb(null, ModifiedFileName);
  },
});

So, We now have the saved file route. Let's read a portion of that as Buffer using a custom function and readChunk.

function isFileValid(){
readChunk(storedFileRoute, 0, 30).then((value,error)=>{
      if(!value || error) return res.sendStatus(500);
      fileType.fromBuffer(value).then((value,error)=>{
            if(!value || error) return res.sendStatus(500);
            console.log(value) // This is the MimeType from Magic Number.
       })
});
}

Now, check value against a list of mime-types that you want to support and keep or delete or move the file.
To make this function run, Add this as middleware in express after Multer.

app.use("/upload", upload.single("file"), isFileValid, (req,res,next)=> {
//Then your express stuff here...
})

Wrote the entire answer by hand, sorry if there are any syntax errors. Feel free to ask questions.

@FahmyChaabane
Copy link

This is now possible in 1.0.0.

If you want to abort the upload:

multer({
  fileFilter: function (req, file, cb) {
    if (path.extension(file.originalname) !== '.pdf') {
      return cb(new Error('Only pdfs are allowed'))
    }

    cb(null, true)
  }
})

If you want to skip any files that is not pdf:

multer({
  fileFilter: function (req, file, cb) {
    if (path.extension(file.originalname) !== '.pdf') {
      return cb(null, false)
    }

    cb(null, true)
  }
})

Just for learning purposes.. where that path attribute is coming from ?

@masonjwhite
Copy link

This is now possible in 1.0.0.
If you want to abort the upload:

multer({
  fileFilter: function (req, file, cb) {
    if (path.extension(file.originalname) !== '.pdf') {
      return cb(new Error('Only pdfs are allowed'))
    }

    cb(null, true)
  }
})

If you want to skip any files that is not pdf:

multer({
  fileFilter: function (req, file, cb) {
    if (path.extension(file.originalname) !== '.pdf') {
      return cb(null, false)
    }

    cb(null, true)
  }
})

Just for learning purposes.. where that path attribute is coming from ?

The path referenced here is a reference to the NodeJS core path module, as referenced here: https://nodejs.org/api/path.html

Hope this helps!

@iamalbinnj
Copy link

can any one tell me to pass failed messagge in cb()
eg - res.status(500).json({Status:"failed",Message:"Invalid file extension."})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests