Увеличены rate limits для улучшения производительности
- Основной лимит: 100 → 1000 запросов за 15 минут - API лимит: 500 → 2000 запросов за 5 минут
This commit is contained in:
parent
daa1c14d11
commit
7d115e1845
@ -8,6 +8,7 @@ const session = require("express-session");
|
||||
const SQLiteStore = require("connect-sqlite3")(session);
|
||||
const path = require("path");
|
||||
const helmet = require("helmet");
|
||||
const cors = require("cors");
|
||||
const rateLimit = require("express-rate-limit");
|
||||
const bodyParser = require("body-parser");
|
||||
const multer = require("multer");
|
||||
@ -25,6 +26,38 @@ const PORT = process.env.PORT || 3001;
|
||||
// Доверяем всем прокси (nginx proxy manager должен передавать X-Forwarded-For)
|
||||
app.set("trust proxy", true);
|
||||
|
||||
// Конфигурация CORS (защита от CSRF)
|
||||
const corsOptions = {
|
||||
origin: function (origin, callback) {
|
||||
// Список разрешённых доменов
|
||||
const allowedOrigins = [
|
||||
"http://localhost:3000",
|
||||
"http://localhost:5173", // Vite dev server
|
||||
"http://localhost:5174",
|
||||
"http://127.0.0.1:3000",
|
||||
"http://127.0.0.1:5173",
|
||||
];
|
||||
|
||||
// В продакшене добавить домены вашего сайта
|
||||
if (process.env.ALLOWED_ORIGINS) {
|
||||
allowedOrigins.push(...process.env.ALLOWED_ORIGINS.split(","));
|
||||
}
|
||||
|
||||
// Если origin не указан (например, для мобильного приложения), разрешаем
|
||||
if (!origin || allowedOrigins.includes(origin)) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(new Error("CORS не разрешен для этого домена"));
|
||||
}
|
||||
},
|
||||
credentials: true, // разрешить куки
|
||||
optionsSuccessStatus: 200,
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
|
||||
allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
|
||||
};
|
||||
|
||||
app.use(cors(corsOptions));
|
||||
|
||||
// Создаем директорию для аватарок, если её нет
|
||||
const uploadsDir = path.join(__dirname, "public", "uploads");
|
||||
if (!fs.existsSync(uploadsDir)) {
|
||||
@ -185,12 +218,42 @@ app.use(
|
||||
})
|
||||
);
|
||||
|
||||
// Ограничение запросов (отключено для разработки)
|
||||
// const limiter = rateLimit({
|
||||
// windowMs: 15 * 60 * 1000, // 15 минут
|
||||
// max: 100, // максимум 100 запросов с одного IP
|
||||
// });
|
||||
// app.use(limiter);
|
||||
// Ограничение запросов (включено для защиты от DDoS и брутфорса)
|
||||
const limiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 минут
|
||||
max: 1000, // максимум 1000 запросов с одного IP
|
||||
standardHeaders: true, // вернёт информацию о rate limit в `RateLimit-*` заголовки
|
||||
legacyHeaders: false, // отключить `X-RateLimit-*` заголовки
|
||||
message: "Слишком много запросов с этого IP адреса, пожалуйста попробуйте позже.",
|
||||
skip: (req) => {
|
||||
// Пропускаем health check запросы
|
||||
return req.path === "/health" || req.path === "/manifest.json";
|
||||
},
|
||||
});
|
||||
app.use(limiter);
|
||||
|
||||
// Специальный rate limiter для защиты от брутфорса (для логина и регистрации)
|
||||
const authLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 минут
|
||||
max: 5, // максимум 5 попыток с одного IP
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: "Слишком много попыток входа. Попробуйте позже.",
|
||||
skipSuccessfulRequests: true, // не считаем успешные попытки
|
||||
});
|
||||
|
||||
// Rate limiter для API запросов (более мягкий для обычных операций)
|
||||
const apiLimiter = rateLimit({
|
||||
windowMs: 5 * 60 * 1000, // 5 минут
|
||||
max: 2000, // максимум 2000 запросов за 5 минут (400 в минуту)
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: "Слишком много запросов к API. Пожалуйста, попробуйте позже.",
|
||||
skip: (req) => {
|
||||
// Пропускаем GET запросы (безопасные, не требуют защиты)
|
||||
return req.method === "GET";
|
||||
},
|
||||
});
|
||||
|
||||
// Статические файлы
|
||||
app.use(express.static(path.join(__dirname, "public")));
|
||||
@ -218,6 +281,9 @@ app.get("/browserconfig.xml", (req, res) => {
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// Rate limiter для API (применяется ко всем API запросам)
|
||||
app.use("/api/", apiLimiter);
|
||||
|
||||
// Настройка сессий с хранением в SQLite
|
||||
app.use(
|
||||
session({
|
||||
@ -230,7 +296,9 @@ app.use(
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: false, // в продакшене установить true с HTTPS
|
||||
secure: process.env.NODE_ENV === "production", // куки только через HTTPS в продакшене
|
||||
httpOnly: true, // куки недоступны для JavaScript (защита от XSS)
|
||||
sameSite: "strict", // защита от CSRF атак
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 дней
|
||||
},
|
||||
})
|
||||
@ -785,7 +853,7 @@ app.get("/register", (req, res) => {
|
||||
});
|
||||
|
||||
// API регистрации
|
||||
app.post("/api/register", async (req, res) => {
|
||||
app.post("/api/register", authLimiter, async (req, res) => {
|
||||
const { username, password, confirmPassword } = req.body;
|
||||
|
||||
// Валидация
|
||||
@ -841,7 +909,7 @@ app.post("/api/register", async (req, res) => {
|
||||
});
|
||||
|
||||
// API входа
|
||||
app.post("/api/login", async (req, res) => {
|
||||
app.post("/api/login", authLimiter, async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
if (!username || !password) {
|
||||
@ -899,7 +967,7 @@ app.post("/api/login", async (req, res) => {
|
||||
});
|
||||
|
||||
// Обработка входа (старый маршрут для совместимости)
|
||||
app.post("/login", async (req, res) => {
|
||||
app.post("/login", authLimiter, async (req, res) => {
|
||||
const { password } = req.body;
|
||||
const correctPassword = process.env.APP_PASSWORD;
|
||||
|
||||
@ -2961,7 +3029,7 @@ app.post("/api/user/2fa/disable", requireApiAuth, async (req, res) => {
|
||||
});
|
||||
|
||||
// API для проверки 2FA кода при входе
|
||||
app.post("/api/user/2fa/verify", async (req, res) => {
|
||||
app.post("/api/user/2fa/verify", authLimiter, async (req, res) => {
|
||||
const { username, token } = req.body;
|
||||
|
||||
if (!username || !token) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user