1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-07 19:32:34 +01:00

🔒 register proxy ip check

This commit is contained in:
Flam3rboy 2021-06-27 23:14:13 +02:00
parent 624a4d5694
commit ea676ddd03
8 changed files with 10565 additions and 144 deletions

10499
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,7 @@
},
"homepage": "https://github.com/fosscord/fosscord-api#readme",
"dependencies": {
"@fosscord/server-util": "^1.3.16",
"@fosscord/server-util": "^1.3.20",
"@types/jest": "^26.0.22",
"@types/json-schema": "^7.0.7",
"ajv": "^8.4.0",
@ -68,6 +68,7 @@
"@types/node-fetch": "^2.5.7",
"@zerollup/ts-transform-paths": "^1.7.18",
"0x": "^4.10.2",
"ipdata": "^2.2.0",
"jest": "^26.6.3",
"node-fetch": "^2.6.1",
"ts-node": "^9.1.1",

View File

@ -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 }));

View File

@ -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(":", "_");
}

View File

@ -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) => {};
}

View File

@ -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({

View File

@ -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";

87
src/util/ipAddress.ts Normal file
View File

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