summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Server.ts3
-rw-r--r--src/middlewares/GlobalRateLimit.ts49
-rw-r--r--src/middlewares/RateLimit.ts47
-rw-r--r--src/routes/auth/register.ts19
-rw-r--r--src/routes/guilds/#guild_id/bans.ts2
-rw-r--r--src/util/ipAddress.ts87
6 files changed, 109 insertions, 98 deletions
diff --git a/src/Server.ts b/src/Server.ts

index f979dab2..93bbfe54 100644 --- a/src/Server.ts +++ b/src/Server.ts
@@ -2,7 +2,7 @@ import "missing-native-js-functions"; import fs from "fs/promises"; import { Connection } from "mongoose"; import { Server, ServerOptions } from "lambert-server"; -import { Authentication, CORS, GlobalRateLimit } from "./middlewares/"; +import { Authentication, CORS } from "./middlewares/"; import { Config, db } from "@fosscord/server-util"; import i18next from "i18next"; import i18nextMiddleware, { I18next } from "i18next-http-middleware"; @@ -65,7 +65,6 @@ export class FosscordServer extends Server { console.log("[DB] connected"); await Config.init(); - this.app.use(GlobalRateLimit); this.app.use(CORS); this.app.use(Authentication); this.app.use(BodyParser({ inflate: true, limit: 1024 * 1024 * 2 })); diff --git a/src/middlewares/GlobalRateLimit.ts b/src/middlewares/GlobalRateLimit.ts deleted file mode 100644
index 7260d1a2..00000000 --- a/src/middlewares/GlobalRateLimit.ts +++ /dev/null
@@ -1,49 +0,0 @@ -import { NextFunction, Request, Response } from "express"; -import { Config } from "@fosscord/server-util"; - -// TODO: use mongodb ttl index -// TODO: increment count on serverside - -export async function GlobalRateLimit(req: Request, res: Response, next: NextFunction) { - return next(); - // TODO: use new db mongoose models - /* - if (!Config.get().limits.rate.ip.enabled) return next(); - - const ip = getIpAdress(req); - let limit = (await db.data.ratelimit.global[ip].get()) || { start: Date.now(), count: 0 }; - if (limit.start < Date.now() - Config.get().limits.rate.ip.timespan) { - limit.start = Date.now(); - limit.count = 0; - } - - if (limit.count > Config.get().limits.rate.ip.count) { - const timespan = Date.now() - limit.start; - - return res - .set("Retry-After", `${timespan.toFixed(0)}`) - .set("X-RateLimit-Global", "true") - .status(429) - .json({ - message: "You are being rate limited.", - retry_after: timespan, - global: true, - }); - } - - res.once("close", async () => { - if (res.statusCode >= 400) { - limit.count++; - await db.data.ratelimit.global[ip].set(limit); - } - }); - - return next(); - */ -} - -export function getIpAdress(req: Request): string { - const { forwadedFor } = Config.get().security; - const ip = forwadedFor ? <string>req.headers[forwadedFor] : req.ip; - return ip.replaceAll(".", "_").replaceAll(":", "_"); -} diff --git a/src/middlewares/RateLimit.ts b/src/middlewares/RateLimit.ts
index 09d109e1..24f4013f 100644 --- a/src/middlewares/RateLimit.ts +++ b/src/middlewares/RateLimit.ts
@@ -1,45 +1,8 @@ -import { NextFunction, Request, Response } from "express"; +import { db, MongooseCache } from "@fosscord/server-util"; +import { NextFunction } from "express"; -import { getIpAdress } from "./GlobalRateLimit"; +const Cache = new MongooseCache(db.collection("ratelimit"), [], { onlyEvents: false }); -export function RateLimit({ count = 10, timespan = 1000 * 5, name = "/" }) { - return async (req: Request, res: Response, next: NextFunction) => { - return next(); - // TODO: use new db mongoose models - /* - - let id = req.user_id || getIpAdress(req); - - const limit: { count: number; start: number } = (await db.data.ratelimit.routes[name][id].get()) || { - count: 0, - start: Date.now(), - }; - - if (limit.start < Date.now() - timespan) { - limit.start = Date.now(); - limit.count = 0; - } - - if (limit.count > count) { - const wait = Date.now() - limit.start; - - return res - .set("Retry-After", `${wait.toFixed(0)}`) - .set("X-RateLimit-Limit", `${count}`) - .set("X-RateLimit-Remaining", "0") - .set("X-RateLimit-Reset", `${limit.start + wait}`) - .set("X-RateLimit-Reset-After", `${wait}`) - .set("X-RateLimit-Bucket", name) - .set("X-RateLimit-Global", "false") - .status(429) - .json({ - message: "You are being rate limited.", - retry_after: wait, - global: false, - }); - } - - return next(); - */ - }; +export default function RateLimit({}) { + return async (req: Request, res: Response, next: NextFunction) => {}; } diff --git a/src/routes/auth/register.ts b/src/routes/auth/register.ts
index 49a3bd6c..f39206f2 100644 --- a/src/routes/auth/register.ts +++ b/src/routes/auth/register.ts
@@ -4,6 +4,8 @@ import bcrypt from "bcrypt"; import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf"; import "missing-native-js-functions"; import { generateToken } from "./login"; +import { getIpAdress, IPAnalysis, isProxy } from "../../util/ipAddress"; +import { HTTPError } from "lambert-server"; const router: Router = Router(); @@ -34,7 +36,19 @@ router.post( gift_code_sku_id, // ? what is this captcha_key } = req.body; - console.log("register", req.body.email, req.body.username, req.headers["cf-connecting-ip"]); + + // get register Config + const { register, security } = Config.get(); + const ip = getIpAdress(req); + + if (register.blockProxies) { + if (isProxy(await IPAnalysis(ip))) { + console.log(`proxy ${ip} blocked from registration`); + throw new HTTPError("Your IP is blocked from registration"); + } + } + + console.log("register", req.body.email, req.body.username, ip); // TODO: automatically join invite // TODO: gift_code_sku_id? // TODO: check password strength @@ -51,9 +65,6 @@ router.post( // discriminator will be randomly generated let discriminator = ""; - // get register Config - const { register, security } = Config.get(); - // check if registration is allowed if (!register.allowNewRegistration) { throw FieldErrors({ diff --git a/src/routes/guilds/#guild_id/bans.ts b/src/routes/guilds/#guild_id/bans.ts
index 87d2e7f8..cf6a059b 100644 --- a/src/routes/guilds/#guild_id/bans.ts +++ b/src/routes/guilds/#guild_id/bans.ts
@@ -1,7 +1,7 @@ import { Request, Response, Router } from "express"; import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "@fosscord/server-util"; import { HTTPError } from "lambert-server"; -import { getIpAdress } from "../../../middlewares/GlobalRateLimit"; +import { getIpAdress } from "../../../util/ipAddress"; import { BanCreateSchema } from "../../../schema/Ban"; import { emitEvent } from "../../../util/Event"; import { check } from "../../../util/instanceOf"; diff --git a/src/util/ipAddress.ts b/src/util/ipAddress.ts new file mode 100644
index 00000000..4467caef --- /dev/null +++ b/src/util/ipAddress.ts
@@ -0,0 +1,87 @@ +import { Request } from "express"; +import { Config } from "../../../server-util/dist"; +// use ipdata package instead of simple fetch because of integrated caching +import IPData, { LookupResponse } from "ipdata"; + +var ipdata: IPData; +const cacheConfig = { + max: 1000, // max size + maxAge: 1000 * 60 * 60 * 24 // max age in ms (i.e. one day) +}; + +const exampleData = { + ip: "", + is_eu: true, + city: "", + region: "", + region_code: "", + country_name: "", + country_code: "", + continent_name: "", + continent_code: "", + latitude: 0, + longitude: 0, + postal: "", + calling_code: "", + flag: "", + emoji_flag: "", + emoji_unicode: "", + asn: { + asn: "", + name: "", + domain: "", + route: "", + type: "isp" + }, + languages: [ + { + name: "", + native: "" + } + ], + currency: { + name: "", + code: "", + symbol: "", + native: "", + plural: "" + }, + time_zone: { + name: "", + abbr: "", + offset: "", + is_dst: true, + current_time: "" + }, + threat: { + is_tor: false, + is_proxy: false, + is_anonymous: false, + is_known_attacker: false, + is_known_abuser: false, + is_threat: false, + is_bogon: false + }, + count: 0, + status: 200 +}; + +export async function IPAnalysis(ip: string): Promise<LookupResponse> { + const { ipdataApiKey } = Config.get().security; + if (!ipdataApiKey) return { ...exampleData, ip }; + if (!ipdata) ipdata = new IPData(ipdataApiKey, cacheConfig); + + return await ipdata.lookup(ip); +} + +export function isProxy(data: LookupResponse) { + if (data.asn.type !== "isp") return true; + if (Object.values(data.threat).some((x) => x)) return true; + + return false; +} + +export function getIpAdress(req: Request): string { + // @ts-ignore + return req.headers[Config.get().security.forwadedFor] || req.socket.remoteAddress; +}