Ад заванесення да URL: Безпечнае зберагачча і выдача файлоў корыстувачаў у Express
Выявіце, дзе програмы Express павінны зберагаць заванесеныя файлы, як express.static супрацоўвае з папкамі для стварэння URL-адрэс, і якія захоўніцы не дазволяюць заванесенным файлам корыстувачаў стаць вадой для безпекі.
Пользователь выбірае фота для профіля, натыкаецца на кнопку аплодавання, і за секунду яна з’являецца на його сторанцы. За гэтым простым взаімадзеянням стояць тры рэальныя дыячанні з дизайну: дзе файл фізычна знаходзится, як сервер зноў яго знаходзіць і як браузер яго адображае. Шырокі практычны кялік разбірае ўсе тры аспекты для прыемлівання Express, пачынаючы з выбору месца для зберагання і заканчваючы выкладкай файлоў па URL, а таксама наводзіць заходы прытармоўкі, якія не дазволяюць аплодаванням стаць атакуючай плошчынай.
Збераганне аплодаваных файлоў на дыску
Перш чым файл можа быць зноў выданы, яго трэба запісаць дзе-небудзь. У простейшай наладзе гэта папка ў сабецвятым файловым системе сервера, якая зазвычай называецца uploads і розташоўваецца ў корані проекта, лякальна ад кода выканання, а не ўнутрь яго:
project-root/
uploads/
avatars/
user-42.png
user-91.png
documents/
invoice-108.pdf
src/
app.js
Групаванне файлаў па ўпынку, такім чынам як avatars/ і documents/, дае рэзультаты ўжо за короткі час. Гэта дапамагае падтрымваць спісы каталогаў у кантролі, калі ўжо ёсць вялікі ўзбор файлаў, упрымнелівае застосаванне правіл чысткі і рэзервнага копіювання па кожной категорыі, а таксама, як вы скора пазнаеце, стварае URL-адрэсы, якія лёгка ўможліваўць працаваць з імі. Одна плоская папка, якая мае ў сабе всі відэкі файлаў, становіць проблему ще задоўга перш чым стане прычынай проблем з выдаткамі.
У большасці прыемлянняў база дадзенаў храніць толькі апыл да файла, напрыклад, вялікі шлях avatars/user-42.png, а не сам файл і не цэлы URL. Храненне вялікага шляху значыць, што пазней можна змяніць домэн або перайсці на іншы спосаб хранення дадзенаў без неабходнасці перапісваць кожную строчку.
Локальны дыск або зовнішняя система хранення дадзенаў
Храненне файлаў на сервере прыемляння
Запіс файлоў безпасова на диск сервера, як показана вышэй, не трывае дадатковай інфраструктуры. Гэта падходзіць для локальнага розвіцця, протатэў, внутрашняго абладнання і прыкладнаў, дзе об’ём завантажэння є малым і прыбліжна вядомым.
Пераказ файлоў у службу зберагачвання
У прыкладнаўх системах для практычнага викорыстання завантажэння часта перадаюць у спецыяльны хранілнік об’ектаў, напрыклад Amazon S3, Google Cloud Storage аб адпоўнага службы, і такім чынам не включаюць диск сервера прыкладнаў у процес.
Чаму важлівы выбор
- Дискі можу быць малага розмеру і короткачасныя. Большасць платформ для хостынгу перабудоввае аб заменяе екземпляры праз кожнае розгортанне, таму файл, які існуе толькі на локальным дыску, можа з’явіцца ўжо пасля наступнага розгортання.
Нічыга з гэтага не робіць локальны хранэлік неправым. Для проектаў для навучэння і сапраўдна маленькіх прыемлівае, гэта ўсвядомлены першы крок, а ідэі, указаны нижчэй (упорядкованыя шляхі, прагнозавальныя URL-адрэсы, акуратная перакананне), застаюцца незменнымі, якщо пазней вы перайдзеце на зовнішні хранэлік. Зберагчы код для хранэння ў маленькам модулі з функцыямі на кшталт save і getUrl, вы значна спроставаеце гэты переход.
Выкарыстоўванне express.static для обслужэння папкі
Пасля збережэння файла браузер усё раве патрэбуе спосаба, які бы ўзяў яго. Express прыносіць з сабой мідлвэр для статычных файлоў саме для гэтай меты:
const express = require("express");
const app = express();
app.use("/uploads", express.static("uploads"));
Этае рэчыце падключае каталог uploads пад прыфікс URL /uploads. Для кожнага запиту, паштака якога пачынаецца з гэтага прыфікса, Express шукае файл у адпаведным месцы ўнутрь папкі, і, якщо ён існуе, передае яго праз стрім з адпаведным типам кантэнту і заголовкамі кешавання. Вам не трэба пісаць адзін обробнік на кожны файл.
Гэта іншы падход, чым звычная маршрут API. Уместо запуску логіки, якая завантажае даны і стварае адпаведны адказ, вы публікуеце цэлую папку, якая падбіраецца выключна за паштакой. З гэтага выліваюцца два практычныя зауважэння:
- Аргумент папкі выраховываецца стосоваўна да каталогу, з якога запускаецца процес Node, а не стосоваўна файла, які містыць код. Іспользованне абсалютнага шляху, напрыклад таког, які ствараецца за дапамою
path.join(__dirname, „..“, „uploads“), запобегае неспадзянанням, калі прыстрой запускаецца з іншага рабочага каталогу. - Усе, што размешчаецца ў той папцы, становіцься доступным для всіх. З’ўязуйце толькі той каталог, які мае быць відкрытым.
Як зберажаны файл стае URL
Калі налагоджана статычная выдача, шлях на дыску і шлях у URL адпаведаюць адна другой:
File on disk: uploads/avatars/user-42.png
Accessible at: https://yourapp.com/uploads/avatars/user-42.png
З-за такога одназначнага адпаведання фронтэнд можа безпасабільна вказваць на файл, напрыклад у тэгу з імажынай, без якога-лібо спецыяльнага канцаўка API для завантажэння байтаў:
<img src="https://yourapp.com/uploads/avatars/user-42.png" alt="User avatar" />
Осё гэта адбываецца там, дзе структура папак з першага раздзела выконвае свою функцыю. Які бы лейяут вы ні выбралі на дыску, ён адразу ж стае тым лейяутам, які кліенты бачаюць у URL-адрэсах, таму апранутая, аднообразная структура дае вам апранутыя, аднообразныя лінкі. Це таксама значыць, што URL можна стварыць на адной з абсалютных дорог, якія зберагаюцца ў базе дадзеных, чым домэн і прыфікс залишаюцца ў адной значэнні налаштавання.
Меры безпекі для завантажэнняў ад корыстнікаў
Завантажэнняя є папулярным цілём для зловжывання, таму фраза «завантажэнне работае» — гэта толькі пачатковая точка. Кожна з наступных мераў закрывае адпаведную прычыну проблем.
Пераканайцеся, што файл насправды ёсьць тым, кім являецца
Расшырэнне імені файлу — гэта проста тэкст, выбраны кліентам, і ён нічога не падтверджвае. Пераканаліце рэальны тип кантэнта, жадаўна чытаючы першыя байты файлу, а не павержаючыся заявленым типам MIME, і прыймайце толькі тое, што патрэбна функцыя. Напрыклад, канцэнтр паверхневага аватара должен прымаць толькі зображэнняя, а нічога іншаго. Фарматы такія як SVG і HTML выказуюцься ўвагай, таму што прыгледчыкі можаць выкананыць скрыпты, ўбудованыя ў іх.
Абмежыце размах
Установіце максымальны размах для кожнага аплодавання. Без ліміту адна занадта вялікая запроса, чырвонай прычыны або навмесна, можа запаліць дыск або перапрацаваць весь пропускную здатнась. Застосавіце ліміт пад час парсавання запроса, каб сервер зупыніўся ў часе, а не спачатку прыняў усю інфармацыю.
Створыце сабе імені файлаў
Ніколы не запісвайце файл пад іменем, яе задаў кліент. этае імя можа мяць незвычныя симвалы або последованасці для праходжэння па шляху, такія як ../../config.js, якія маюць на мете абйсці каталог загрузкі. Створыце новае імя на сервере, напрыклад, адбытковы ідэнтыфікатор плюс пераканальваная расширэння. Як дадзенны эфект, гэта таксама запобегае таму, каб два корыстнікі, якія загружаюць photo.jpg, перазапісвалі адна другую.
Зберагачыце каталог загрузкі адсакраваным
Зберагачыце загружаныя файлы ў каталозе, які чыста аддзелены ад кансультатывага коду і налашчэнняў, і пераканайцеся, што статычны мідлвэр выканае доступ толькі да гэтага каталогу. Памылковая монтавка корневага каталогу проекта прыведзе да таго, што файлы сяродовішча і кансультатывы код буду доступныя для кожнага, хто здогадаецца па шляху.
Падумайце пра тое, які источнік суплявае файлы
Адміністрацыя кантэнту, які дае корыстнік, з таго ж домэна, што і галоўны прыемлік, можа стварыць рызык выканання атак типу cross-site scripting, адколі зловучы файл будзе выкананы пад адрэсам вашага сайту. З гэтай прычыны багато практычных систем выдаюць атласаваны кантэнт з зовнішняй памяці або з окрамжнага сабдомэна. Адправка заголовка X-Content-Type-Options: nosniff і, для завантажэнняў, якія не павінны адображацца ў тэксте, заголовка Content-Disposition: attachment, ўсё больш зменшае гэты рызык.
Частыя запытанні
Чы гэты справа кожнага проекту — викорыстоўваць S3 або падобную службу?
Не обавязкова. Для маленькіх або навчальных проектаў локальная дыскета ёсць простейшым і аблікованым рашэнням. Зовнішняя память становіць значэнне тады, калі вы запускаеце прыемлік на платформе з тымчасовай системай файлаў або калі трэба запускаць калькі экземпляраў, якія павінны бачыць тыя ж файлы.
Чы express.static патрабуе дадатковага пакета?
Ня, гэта ўжо ўбудована ў Express. Парсінг паднятых дакументаў з формы типу multipart зазвычай выклікае патрэбу ў бібліятэцы, такой як Multer, але выдача файлаў, якія вучынены ўжо збережанымі, такой патрэбы не выклікае.
Чы можна застаўляць первісную назву файла?
Безпечней яе не застаўляць. Назва, створаная серверам, запобегае наяўнасці странных симвалаў, збітам між ідэнтычнымі назвамі, а таксама хакерскім трюкам, якія можна выкарыстоўваць за дапамогою спецыяльна створанай назвы. Якщо корыстувальнікам трэба бачыць первісную назву, лепш зберагаць яе як метаданыя ў базе данных.
Чы можна обмежыць доступ да паднятых файлаў?
Так, але не толькі за дапамогою express.static, якая дае файл кожнаму, хто знае його URL. Для прыватных файлаў патрэбна спецыяльная маршрутка, якая пераканаецца ў аутентыкацыі і автарызацыі перш чым прадаст файл, альбо, у разе внешняй зберагальніцы, короткавечныя лінкі, створаныя толькі для дазволеных корыстувальнікаў.
Заключанне
Функцыя калічэння файлоў падтрымлець адпаведна на адповесць на чатыро запытанні. Дзе знаходзіцца файл: на дыску сервера, таму што гэта проста, чы ў хранілішчы об’ектаў, таму што ён застаецца пасля апдэйтаў і магчыма ўскорення? Як прыстранне будзе яго знаходзіць пазней: за дапамогою арганізаванага структурування папак і абсалютнага шляху, які зберагаецца ў базе дадзенаў? Як браузер будзе яго запрашаць: через express.static, прысвоўанага чыстаму прэфіксу URL, чы ўрочыстай маршруту для прыватных файлоў? І што будзе яго заханяць: перакананыя типы, ліміты розмеру, імены, створаныя серверам, і строго адмежаванае, жадаўна окремае месца раздачы. Як толькі гэтыя пытанні рашаны, рэшта функцый ёст кароткая налада.