192

I have this code:

res.sendfile( '../../temp/index.html' )

However, it throws this error:

Error: Forbidden
at SendStream.error (/Users/Oliver/Development/Personal/Reader/node_modules/express/node_modules/send/lib/send.js:145:16)
at SendStream.pipe (/Users/Oliver/Development/Personal/Reader/node_modules/express/node_modules/send/lib/send.js:307:39)
at ServerResponse.res.sendfile (/Users/Oliver/Development/Personal/Reader/node_modules/express/lib/response.js:339:8)
at exports.boot (/Users/Oliver/Development/Personal/Reader/server/config/routes.js:18:9)
at callbacks (/Users/Oliver/Development/Personal/Reader/node_modules/express/lib/router/index.js:161:37)
at param (/Users/Oliver/Development/Personal/Reader/node_modules/express/lib/router/index.js:135:11)
at pass (/Users/Oliver/Development/Personal/Reader/node_modules/express/lib/router/index.js:142:5)
at Router._dispatch (/Users/Oliver/Development/Personal/Reader/node_modules/express/lib/router/index.js:170:5)
at Object.router (/Users/Oliver/Development/Personal/Reader/node_modules/express/lib/router/index.js:33:10)
at next (/Users/Oliver/Development/Personal/Reader/node_modules/express/node_modules/connect/lib/proto.js:199:15)

Can anyone tell me why this might be?

4 Answers4

341

I believe it's because of the relative path; the "../" is considered malicious. Resolve the local path first, then call res.sendfile. You can resolve the path with path.resolve beforehand.

var path = require('path');
res.sendFile(path.resolve('temp/index.html'));
nwinkler
  • 52,665
  • 21
  • 154
  • 168
Joe
  • 41,484
  • 20
  • 104
  • 125
  • 35
    more details would be handy here for newbies like me – Adam Waite Aug 06 '13 at 10:49
  • 5
    Express considers relative paths in `sendfile` as bad. Unless you specify the `root` directory parameter, as seen here: https://github.com/visionmedia/express/issues/1465 – Joe Aug 06 '13 at 10:51
  • 2
    var path = require('path'); – Matt Harrison Mar 07 '14 at 12:53
  • 1
    yeah the final codE!! – SuperUberDuper May 21 '16 at 21:20
  • 3
    @MattHarrison ES6 update, for packages imports, `const` is preferred over `var` – Nino Filiu Mar 01 '19 at 14:37
  • The `resolve` will resolve the relative path and create an absolute path, that then will be treated as the 'root' - if the strings received by `resolve` doesn't form an absolute path, the path from the current directory is used. [Reference](https://nodejs.org/docs/latest/api/path.html#path_path_resolve_paths). –  Jan 26 '20 at 02:28
45

This answer gathers together the info from the other answers/comments.

It depends whether you want to include something relative to the process working directory (cwd) or the file directory. Both use the path.resolve function (put var path = require('path') at the top of the file.

  • relative to cwd: path.resolve('../../some/path/to/file.txt');
  • relative to file: path.resolve(__dirname+'../../some/path/to/file.txt');

From reading the link from @Joe's comment, it sounds like relative paths are a security risk if you accept user input for the path (e.g. sendfile('../.ssh/id_rsa') might be a hacker's first try).

derekdreery
  • 3,860
  • 4
  • 29
  • 38
  • 1
    AS a newbie I want to know how hacker scenario came here? – bharath muppa Sep 11 '15 at 15:37
  • 3
    If you accidentally allow the user to input the path of the file they want to download, they could download any file on your system (I gave the example of a ssh private key - which would give them the ability to pretend to be your PC (man-in-the-middle etc)). Having the .. restriction disallows this possibility as only files from the website can be accessed. – derekdreery Nov 23 '15 at 14:51
35

The Express documentation suggests doing it a different way, and in my opinion it makes more sense later than the current solution.

res.sendFile('index.html', {root: './temp'});

The root option seems to set ./ as the root directory of your project. So I cannot fully tell where you file is in relation to the project root, but if your temp folder is there, you can set ./temp as the root for the file you're sending.

tenor528
  • 1,120
  • 13
  • 20
  • 1
    This is true, but it uses sendFile (capital F, supported by Express v4.8.0 onwards) instead of the older sendfile the OP was using. Just saying... =] – RemyNL Feb 10 '16 at 11:50
  • Ahh... good catch. I didn't notice this small difference. I also wonder if the selected answer works not because it uses `.sendfile` but because it relies on something else completely (path). Thanks for pointing this out. – tenor528 Feb 10 '16 at 15:53
  • Simple and eficient. Thank you! This worked for me perfectly! – Emanuela Colta Jun 22 '17 at 18:18
4

Also, you can use path.join

const path = require("path");

router.get("/", (req, res) => {
  let indexPath = path.join(__dirname, "../public/index.html");
  res.sendFile(indexPath);
});

Lalit Vavdara
  • 406
  • 4
  • 9