router
- localhost:3000
- localhost:3000/favicon.ico
- localhost:3000/css/style.css
- localhost:3000/page localhost:3000/dir/page localhost:3000/dir/page?param=test
маршрутизатор должен обработать запрос главной страницы, запретить файлы в корне директории, обрабатывать файлы в каталоге, обрабатывать запрос каталога/страницы
app.get('*', (req, res) => { const requestedPath = req.path; const queryParameters = req.query; // Получаем все query parameters let { dir, base } = path.parse(requestedPath); if (dir === '/' && base === '') { // Обработка основного запроса каталога requestHome = 'index.html'; console.log(`Main page requested: ${requestHome}`); res.send(`Main page requested: ${requestHome}`); } else if (dir === '/' && base.includes('.')) { // Обработка запроса к запрещенным файлам res.send(`Forbidden request: ${dir}, ${base}`); console.log(`Forbidden request: ${dir}, ${base}`); } else if (dir && base.includes('.')) { // Обработка запроса к файлам внутри каталога res.send(`Requested directory: ${dir}, path: ${base}`); console.log(`Requested directory: ${dir}, path: ${base}`); } else if (dir && base) { // Обработка запроса к каталогам res.send(`Requested path: ${requestedPath}`); console.log(`Requested path ${requestedPath}`); // Обработка дополнительного параметра, если он присутствует if (queryParameters.myParameter) { const myParamValue = queryParameters.myParameter; // Делайте что-то с myParamValue console.log(`Value of myParameter: ${myParamValue}`); } } else { // Обработка ошибочного запроса res.send(`Error requested path: ${requestedPath}`); console.error(`Error requested path: ${requestedPath}`); } }); const host = configObject.host; // host const port = configObject.port; // port app.listen(port, host, () => { console.log(`Сервер запущен на http://${host}:${port}`)); });
усложняем с определением параметров для разных директорий
const directoryMap = { "/catalog/": ["pages"], "/login": ["user"] }; app.get('*', (req, res) => { const requestedPath = req.path; const queryParameters = req.query; let { dir, base } = path.parse(requestedPath); if (dir === '/' && base === '') { // Обработка основного запроса каталога requestHome = 'index.html'; console.log(`Main page requested: ${requestHome}`); res.send(`Main page requested: ${requestHome}`); } else if (dir === '/' && base.includes('.')) { // Обработка запроса к запрещенным файлам res.send(`Forbidden request: ${dir}, ${base}`); console.log(`Forbidden request: ${dir}, ${base}`); } else if (dir && base.includes('.')) { // Обработка запроса к файлам внутри каталога res.send(`Requested directory: ${dir}, path: ${base}`); console.log(`Requested directory: ${dir}, path: ${base}`); // Проверка наличия ожидаемых параметров if (directoryMap[dir]) { const expectedParameters = directoryMap[dir]; const missingParameters = expectedParameters.filter(param => !(param in queryParameters)); if (missingParameters.length > 0) { console.log(`Missing parameters for ${dir}: ${missingParameters.join(', ')}`); res.send(`Missing parameters for ${dir}: ${missingParameters.join(', ')}`); return; } } // Обработка дополнительных параметров, если они присутствуют const parameterValues = Object.keys(queryParameters) .filter(key => expectedParameters.includes(key)) .map(key => queryParameters[key]); if (parameterValues.length !== 0) { console.log(`Values of expected parameters: ${parameterValues.join(', ')}`); } } else if (dir && base) { // Обработка запроса к каталогам res.send(`Requested directory: ${dir}, path: ${base}`); console.log(`Requested directory: ${dir}, path: ${base}`); // Проверка наличия ожидаемых параметров if (directoryMap[dir]) { const expectedParameters = directoryMap[dir]; const missingParameters = expectedParameters.filter(param => !(param in queryParameters)); if (missingParameters.length > 0) { console.log(`Missing parameters for ${dir}: ${missingParameters.join(', ')}`); res.send(`Missing parameters for ${dir}: ${missingParameters.join(', ')}`); return; } } // Обработка дополнительных параметров, если они присутствуют const parameterValues = Object.keys(queryParameters) .filter(key => expectedParameters.includes(key)) .map(key => queryParameters[key]); if (parameterValues.length !== 0) { console.log(`Values of expected parameters: ${parameterValues.join(', ')}`); } } else { // Обработка ошибочного запроса res.send(`Error requested path: ${requestedPath}`); console.error(`Error requested path: ${requestedPath}`); } });
const express = require("express"); const expressValidator = require("express-validator"); const fs = require("fs"); const app = express(); app.get("/", (req, res) => { // Главный URL const page = db.findOne({ path: "/", name: "index" }); // Отправляем ответ res.send(page); }); app.get("/:path", (req, res) => { // Путь const path = req.params.path; // Проверяем, является ли path главной страницей if (path === "/") { // Главная страница const page = db.findOne({ path: "/", name: "index" }); // Отправляем ответ res.send(page); } else { // Путь к странице или файлу const parts = path.split("/"); const lastPart = parts.pop(); // Проверяем, есть ли у файла расширение const extension = lastPart.split(".").pop(); if (extension) { // Путь к файлу // Ищем файл в указанной директории const file = fs.readFileSync(`${parts.join("/")}/${lastPart}`); // Отправляем ответ res.send(file); } else { // Путь к странице // Ищем страницу с именем `lastPart` в базе данных // Фильтруем входные данные req.body.lastPart = expressValidator.trim(req.body.lastPart); req.body.lastPart = expressValidator.escape(req.body.lastPart); const page = db.findOne({ path: "", name: lastPart }); // Проверяем, существует ли такая страница if (!page) { // Не существует // Возвращаем ошибку 404 res.status(404).send("Страница не найдена"); } } } // Отправляем ответ res.send(page); }); app.listen(3000);
- Фильтрация входных данных. Необходимо фильтровать входные данные, которые поступают от пользователей. Это поможет предотвратить атаки типа SQL-инъекции. Например, можно использовать функцию
sanitize()
из модуляexpress-validator
. - Использование HTTPS. HTTPS обеспечивает шифрование соединения между клиентом и сервером. Это помогает защитить данные пользователей от перехвата.
- Серверная аутентификация. Необходимо использовать серверную аутентификацию для проверки подлинности пользователей. Это поможет предотвратить несанкционированный доступ к данным.
- Ограничение доступа к ресурсам. Необходимо ограничить доступ к ресурсам в соответствии с ролями пользователей. Это поможет защитить конфиденциальные данные от несанкционированного доступа.
Вот некоторые примеры того, что мы можем проверить в свойстве req.body:
Допустимость данных. Мы можем проверить, что данные имеют допустимый формат. Например, мы можем проверить, что данные являются числовыми, строковыми или другими допустимыми типами данных.
Длина данных. Мы можем проверить, что данные имеют допустимую длину. Например, мы можем проверить, что данные не превышают определенной длины.
Наличие недопустимых символов. Мы можем проверить, что данные не содержат недопустимых символов. Например, мы можем проверить, что данные не содержат символов, которые могут использоваться для атак типа SQL-инъекции.
Вот несколько конкретных примеров того, как мы можем проверить данные в свойстве req.body:
//JavaScript if (req.body) { // Проверяем, что данные являются числовыми if (typeof req.body.number !== "number") { // Данные не являются числовыми res.status(400).send("Данные должны быть числовыми"); } }
//JavaScript if (req.body) { // Проверяем, что данные не превышают 100 символов if (req.body.text.length > 100) { // Данные превышают 100 символов res.status(400).send("Данные не должны превышать 100 символов"); } }
Используйте код с осторожностью. Подробнее…
Проверка наличия недопустимых символов
//JavaScript if (req.body) { // Проверяем, что данные не содержат символы `;` if (req.body.text.includes(";")) { // Данные содержат символ `;` res.status(400).send("Данные не должны содержать символ `;`"); } }
Конкретные проверки, которые мы должны выполнить, зависят от того, какие данные мы ожидаем от пользователей.
В результате код может выглядеть так:
const express = require("express"); const expressValidator = require("express-validator"); const fs = require("fs"); const app = express(); app.get("/", (req, res) => { // Главный URL const page = db.findOne({ path: "/", name: "index" }); // Отправляем ответ res.send(page); }); app.get("/:path", (req, res) => { // Путь const path = req.params.path; // Проверяем, является ли path главной страницей if (path === "/") { // Главная страница const page = db.findOne({ path: "/", name: "index" }); // Отправляем ответ res.send(page); } else { // Путь к странице или файлу const parts = path.split("/"); const lastPart = parts.pop(); // Проверяем, есть ли у файла расширение const extension = lastPart.split(".").pop(); if (extension) { // Путь к файлу // Ищем файл в указанной директории const file = fs.readFileSync(`${parts.join("/")}/${lastPart}`); // Отправляем файл res.send(file); } else { // Путь к странице // Фильтруем входные данные req.body.lastPart = expressValidator.trim(req.body.lastPart); req.body.lastPart = expressValidator.escape(req.body.lastPart); // Проверяем, существует ли такая страница const page = db.findOne({ path: "", name: lastPart }); // Проверяем, является ли страница допустимой if (!page) { // Не существует // Возвращаем ошибку 404 res.status(404).send("Страница не найдена"); } // Отправляем страницу res.send(page); } } // Проверяем, есть ли другие данные в req.body if (req.body) { // Проверяем, что данные являются числовыми if (typeof req.body.number !== "number") { // Данные не являются числовыми res.status(400).send("Данные должны быть числовыми"); } // Проверяем, что данные не превышают 100 символов if (req.body.text.length > 100) { // Данные превышают 100 символов res.status(400).send("Данные не должны превышать 100 символов"); } // Проверяем, что данные не содержат символы `;` if (req.body.text.includes(";")) { // Данные содержат символ `;` res.status(400).send("Данные не должны содержать символ `;`"); } } }); app.listen(3000);
В этом коде, мы сделали следующие изменения:
- Мы добавили проверку допустимости данных в свойство
req.body
. Это поможет предотвратить атаки типа SQL-инъекции. - Мы добавили проверку длины данных в свойство
req.body
. Это поможет предотвратить атаки типа переполнения буфера. - Мы добавили проверку наличия недопустимых символов в свойство
req.body
. Это поможет предотвратить атаки типа XSS.
Этот код более безопасный и надежный, чем предыдущий код. Он гарантирует, что мы проверим все данные, которые поступают от пользователей.
Добавим проверку разрешенных полей:
const allowedFields = ["number", "text"]; app.get("/", (req, res) => { // Главный URL const page = db.findOne({ path: "/", name: "index" }); // Отправляем ответ res.send(page); }); app.get("/:path", (req, res) => { // Путь const path = req.params.path; // Проверяем, является ли path главной страницей if (path === "/") { // Главная страница const page = db.findOne({ path: "/", name: "index" }); // Отправляем ответ res.send(page); } else { // Путь к странице или файлу const parts = path.split("/"); const lastPart = parts.pop(); // Проверяем, есть ли у файла расширение const extension = lastPart.split(".").pop(); if (extension) { // Путь к файлу // Ищем файл в указанной директории const file = fs.readFileSync(`${parts.join("/")}/${lastPart}`); // Отправляем файл res.send(file); } else { // Путь к странице // Проверяем, что все поля в req.body разрешены for (const field of Object.keys(req.body)) { if (!allowedFields.includes(field)) { // Поле не разрешено res.status(400).send("Поле `${field}` не разрешено"); return; } } // Поля разрешены // Выполняем остальные проверки req.body.lastPart = expressValidator.trim(req.body.lastPart); req.body.lastPart = expressValidator.escape(req.body.lastPart); // Проверяем, существует ли такая страница const page = db.findOne({ path: "", name: lastPart }); // Проверяем, является ли страница допустимой if (!page) { // Не существует // Возвращаем ошибку 404 res.status(404).send("Страница не найдена"); } // Отправляем страницу res.send(page); } } // Проверяем, есть ли другие данные в req.body if (req.body) { // Проверяем, что данные являются числовыми if (typeof req.body.number !== "number") { // Данные не являются числовыми res.status(400).send("Данные должны быть числовыми"); } // Проверяем, что данные не превышают 100 символов if (req.body.text.length > 100) { // Данные превышают 100 символов res.status(400).send("Данные не должны превышать 100 символов"); } // Проверяем, что данные не содержат символы `;` if (req.body.text.includes(";")) { // Данные содержат символ `;` res.status(400).send("Данные не должны содержать символ `;`"); } } }); app.listen(3000);
В этом коде, мы создаем массив allowedFields, в котором перечислены разрешенные поля. Затем, в методе get, мы проверяем, что все поля в свойстве req.body находятся в этом массиве. Если поле не разрешено, мы возвращаем ошибку 400.
Этот подход также является безопасным и надежным. Он гарантирует, что мы проверим все данные, которые поступают от пользователей.
Однако, этот подход может быть менее гибким, чем предыдущий подход. Например, нам может потребоваться добавить новое поле в свойство req.body. В этом случае, нам также потребуется добавить это поле в массив allowedFields.
В конечном итоге, выбор подхода зависит от конкретных требований вашего приложения
Разрешенные поля в URL представлены в виде параметров запроса. Например, если мы хотим разрешить два поля, number и text, то мы можем добавить следующие параметры запроса в URL:
/?number=123&text=Hello, world!
В этом случае, данные, отправленные в свойстве req.body, будут содержать только поля number и text. Все остальные поля будут игнорироваться.
Вот пример того, как мы можем проверить, что все поля в URL разрешены:
const allowedFields = ["number", "text"]; app.get("/", (req, res) => { // Главный URL const page = db.findOne({ path: "/", name: "index" }); // Отправляем ответ res.send(page); }); app.get("/:path", (req, res) => { // Путь const path = req.params.path; // Проверяем, является ли path главной страницей if (path === "/") { // Главная страница const page = db.findOne({ path: "/", name: "index" }); // Отправляем ответ res.send(page); } else { // Путь к странице или файлу const parts = path.split("/"); const lastPart = parts.pop(); // Проверяем, есть ли у файла расширение const extension = lastPart.split(".").pop(); if (extension) { // Путь к файлу // Ищем файл в указанной директории const file = fs.readFileSync(`${parts.join("/")}/${lastPart}`); // Отправляем файл res.send(file); } else { // Путь к странице // Проверяем, что все поля в URL разрешены const allowedFieldsInUrl = new Set(); for (const key of req.query.keys()) { allowedFieldsInUrl.add(key); } for (const field of allowedFields) { if (!allowedFieldsInUrl.has(field)) { // Поле не разрешено res.status(400).send("Поле `${field}` не разрешено"); return; } } // Поля разрешены // Выполняем остальные проверки req.body.lastPart = expressValidator.trim(req.body.lastPart); req.body.lastPart = expressValidator.escape(req.body.lastPart); // Проверяем, существует ли такая страница const page = db.findOne({ path: "", name: lastPart }); // Проверяем, является ли страница допустимой if (!page) { // Не существует // Возвращаем ошибку 404 res.status(404).send("Страница не найдена"); } // Отправляем страницу res.send(page); } } // Проверяем, есть ли другие данные в req.body if (req.body) { // Проверяем, что данные являются числовыми if (typeof req.body.number !== "number") { // Данные не являются числовыми res.status(400).send("Данные должны быть числовыми"); } // Проверяем, что данные не превышают 100 символов if (req.body.text.length > 100) { // Данные превышают 100 символов res.status(400).send("Данные не должны превышать 100 символов"); } // Проверяем, что данные не содержат символы `;` if (req.body.text.includes(";")) { // Данные содержат символ `;` res.status(400).send("Данные не должны содержать символ `;`"); } } }); app.listen(3000);
В этом коде, мы создаем множество allowedFieldsInUrl, в котором хранятся все поля, которые были отправлены в URL. Затем, мы проверяем, что все поля в этом множестве также находятся в массиве allowedFields. Если поле не разрешено, мы возвращаем ошибку 400.