19. Node.js Lessons. Safe Way to a FS File and Path

 

fantastic-wooden-pier-frisco-night

This article will deal with how to create a web server in Node.js, which will return a file to a user from a public directory. You may wonder: why do we need Node.js here? Why can’t we use another server? You question surely does make sense. Yes, for returning files other servers are generally more effective. From the other side, Node.js works pretty well, too. Second, before returning a file it can also perform some intellectual activities: for example, refer to a database, check out whether a user is permitted to access the file and give the file to him, if it’s permitted.

Please, submit coding from our repository, we won’t write anything today. But before you start, you will need to install the mime module:

npm install mime

That’s how our server.js looks like:


http.create is very simple here.


It will check whether you are permitted to access the file and give it to you, if the answer is positive. To check the access right, we will use the following function (which is Fake function as it is). This function will perform url parse and if there is a parameter named secret equal to ‘o_O’, it means you’ve got an access:


In practice, a check of this kind will be done using cookies, databases, etc.
The key function we are interested in is sendFileSafe:


This is the very function that upon receiving the path from a user has to send a respective file from the public directory considering the directory path. The key aspect that should be contained here is safety. Whatever path is delivered by a user, he has to receive a file within the public directory. For example, this request:

http://localhost:3000/index.html?secret=o_O

should return an index.html file. The picture is taken here from a directory


And if we hadn’t indicated secret:

http://localhost:3000/index.html

it will have shown an error with the code 403.

And if I had indicated it the following way:

http://localhost:3000/server.js

it will have shown an error, too. The principle is the same for any attempt to get away from this directory.

So, we look for the function sendFileSafe, in order to get an example of a safe work with the path from a visitor. This function contains a number of steps.


The first step is when we handle the path with decodeURIComponent, since the “HTTP” standard means coding of many symbols. Once this kind of url is received, we have to decode it in return using this call:

decodeURIComponent

Thus, if url has been coded incorrectly, an error will occur, and we will need to catch and handle it. Code 400 means that your url is incorrect and the request is invalid. Well, you can get back the code 404, too.
Once the request is decoded, it is high time to check it. There is a specialized ‘zero byte’ that must not be contained in the url string:


If it is there, it means someone has maliciously brought it here because some built-in Node.js functions will work incorrectly with this byte. Respectively, if you’ve got it, you need to get back Bad Request, and your request is incorrect.

Now it’s time to get a full path to the file on a disk. To do so, we will use our path module.


This built-in module contains a set of various functions to work with paths. For example join unites paths, normalize eliminates different strange things from a path: such as “.”, “:”, “//”, etc., which means it gives a path a correct form. If an url delivered by a user looked like this:

//  /deep/nodejs.jpg

after join with ROOT that represents this directory:


it would look differently, for example:


Our next task is to make sure this path is within the public directory. Right now, when we already have a correct and precise path, it is quite simple. You only need to make sure this prefix is contained in the beginning:


Which means the path starts with ROOT. Let’s check it out. If it’s not, you will see File not Found:


If your access is permitted, let us check what the path includes. If there is nothing, fs.stat will bring back err:


Even if there is no error, you still need to make sure whether it is a file. If it isn’t a file, it is an error. If it is a file, it means everything is checked and you need to send the file. This can be done using the built-in call sendFile:


sendFile is a function contained in the file, but a little bit below:


For reading a file it uses the call fs.readFile, and upon being read it will be outputted through res.end:


Pay your attention to the following: any mistake in this callback is barely possible


at least because we’ve already checked the file does exist, it is a file and it can be delivered. But you never know what may happen. For example, an error can occur while the file is being read on a disk. Anyway, we have to handle this possible error. Just to read the file and send it won’t be enough, since various files should contain various Content-Type titles.

For example, an HTML file needs to be of a text/html type, a file with a JPEG picture – of a image/jpeg type, etc. The needed file type gets defined according to its extension using the mime module.


In order to make it work, do not forget to install it. And, eventually, this article deals on how to work with a path from a visitor correctly, in order to do all needed checks, decoding, etc. All these things are extremely important, but if it comes to the file return, this code is incorrect:


because readFile computes the whole file and then sends it to content. Just imagine what will happen, if a file is very big. And what if it’s even bigger than the memory left? It will crash! So, in order to send a file you need to give a command to a specialized server or use the streams that we will talk about in our next article.

tumblr_nvdx0jpq7F1rf2fjgo1_1280
The article materials were borrowed from the following screencast.

We are looking forward to meeting you on our website soshace.com

About the author

Stay Informed

It's important to keep up
with industry - subscribe!

Stay Informed

Looks good!
Please enter the correct name.
Please enter the correct email.
Looks good!

19. Уроки Node.js. Безопасный Путь к Файлу в fs и path.

 

fantastic-wooden-pier-frisco-night
В этой статье мы рассмотрим, как при помощи Node.js создать веб-сервер, который будет возвращать файл пользователю из директории public. Может возникнуть вопрос: зачем здесь Node.js? почему бы не сделать это на другом сервере? Вопрос совершенно уместен. Да, для отдачи файлов, как правило, другие сервера будут более эффективны. С другой стороны, Node.js, во-первых, тоже работает весьма неплохо, а во-вторых, перед отдачей файла может совершить какие-то интеллектуальные действия, например, обратиться к базе данных, проверить, имеет ли пользователь право на доступ к данному файлу, и только если имеет, тогда уже отдавать.

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

npm install mime

Так выглядит наш server.js:


http.create здесь очень прост.


Он будет проверять, есть ли доступ к данному файлу, и если есть, то уже отдавать. Для проверки доступа мы будем использовать следующую функцию (которая является по своей сути является Fake function), которая будет выполнять url parse , и если есть параметр secret, который будет равен ‘o_O’, то считается, что доступ есть:


В реальной жизни такая проверка, конечно же, будет производиться при помощи cookies, базы данных и т.д.
Основная функция, которая нас интересует, это sendFileSafe:


Именно эта функция должна, получив путь от user, отослать соответствующий файл с директории public, учитывая путь директории. Важнейший аспект, который в ней должен быть заложен, это безопасность. Какой бы путь не передал пользователь, он ни в коем случае не должен получить файл вне директории public. Например, обращение:

http://localhost:3000/index.html?secret=o_O

должно возвращать файл index.html,  картинка здесь взята из директории


А если бы мы не указали secret:

http://localhost:3000/index.html

то оно должно было бы выдать ошибку с кодом 403.

А если бы я попробовали бы указать вот так:

http://localhost:3000/server.js

то тоже была бы ошибка. И так для любых попыток выйти за пределы этой директории.

Итак, смотрим функцию sendFileSafe, чтобы получить пример безопасной работы с путем от посетителя. Эта функция состоит из нескольких шагов.


На первом шаге мы пропускаем путь через decodeURIComponent, ведь по стандарту “http” многие символы кодируются. Получив такой “url”, мы обязаны его декодировать обратно, при помощи вот такого вызова:

decodeURIComponent

При этом, если “url” закодирован заведомо неверно, то возникнет ошибка, которую необходимо поймать и обработать. Код 400 как раз означает, что “url” некорректен и запрос неверен. Можно, конечно, вернуть и 404.
Когда мы раскодировали запрос, время его проверить. Есть специальный нулевой байт, который в строке “url” присутствовать не должен:


Если он есть, то это означает, что кто-то его злонамеренно передал, потому что некоторые встроенные функции Node.js будут работать таким байтом некорректно. Соответственно, если такой байт есть, то мы тоже возвращаем «Bad Request», запрос некорректен.

Теперь настало время получить полный путь к файлу на диске. Для этого мы будем использовать модуль path.


Этот встроенный модуль содержит набор самых разных функций для работы с путями. Например, join объединяет пути, normalize – удаляет из пути всякие странные вещи типа “.”, “:”, “//” и  т.д., то есть, делает путь более корректным. Если “url”, который передал пользователь, выглядел так:

//  /deep/nodejs.jpg

то после join с ROOT, который представляет собой вот эту директорию:


он будет выглядеть уже по-другому, например:


Наша следующая задача – это убедиться, что этот путь действительно находится внутри директории public. Сейчас, когда у нас уже есть абсолютно точный, корректный путь, это сделать очень просто. Достаточно всего лишь проверить, что вначале находится вот такой префикс:


то есть, что путь начинается с ROOT. Проверяем. Если это не так, то “File not Found”:


Если путь разрешен, то проверим, что по нему находиться. Если ничего нет, то fs.stat вернет ошибку err:


Если даже ошибки нет, то нужно проверить, файл ли это. В том случае, если это не файл, то это ошибка. Ну а если файл, то все проверено, и надо его отослать. Это делает вложенный вызов sendFile:


sendFile – функция которая есть в этом же файле, но немного ниже:


Она для чтения файла использует вызов fs.readFile, и когда он будет прочитан, то выводит его через res.end:


Обращаем ваше внимание вот на что:  ошибка в этом callback очень маловероятна


хотя бы потому, что мы уже проверили, файл есть, это действительно файл и его можно отдать. Но, тем не менее, мало ли что. Например, может возникнуть ошибка при чтении с диска. Так или иначе как-то обработать ошибку надо. Далее, мало просто считать содержимое файла и отправить его, ведь различные файлы должны снабжаться различными заголовками “Content-Tyрe”.

Например, HTML  файл должен иметь тип text/html, файл с картинкой JPEG – image/jpeg и т.д. Нужный тип файла определяется по расширению с использованием модуля ‘mime’.


Для того чтобы все работало, не забудьте его установить. И, наконец, последнее, эта статья была сосредоточена, в первую очередь, на том, чтобы корректно работать с path от посетителя, чтобы сделать все необходимые проверки, декодирование и т.д. Все это очень важно, но что касается отдачи файла, то этот код неверен:


потому что readFile полностью просчитывает файл, потом отсылает его в content. А представьте, что будет, если файл очень большой. А если он превышает количество свободной памяти? Тогда вообще все упадет! Поэтому, для того чтобы отсылать файл, нужно либо дать команду специализированному серверу, либо использовать потоки, которые мы рассмотрим в следующей статье.

tumblr_nvdx0jpq7F1rf2fjgo1_1280
Материалы для статьи взяты из следующего скринкаста.

We are looking forward to meeting you on our website soshace.com

About the author

Stay Informed

It's important to keep up
with industry - subscribe!

Stay Informed

Looks good!
Please enter the correct name.
Please enter the correct email.
Looks good!

Related articles

13.02.2020

JavaScript find() and filter() Array Iteration Methods Made Easy

While this article will be more focused on making you understand these two array iteration methods, it will also create a fun, interesting and easy ...

Dockerization of Node.JS Applications on Amazon Elastic Containers

This article is about creating a critical task-based highly available and scalable NodeJS application on the Amazon Cloud with an architecture that ...

How I Got Started with Angular and TypeScript

I share my experience on how to learn the most basic parts of Angular and ...

No comments yet

Sign in

Forgot password?

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy

Password recovery

You can also try to

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy