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;
+}
|