mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-07 11:22:35 +01:00
🔒 register proxy ip check
This commit is contained in:
parent
624a4d5694
commit
ea676ddd03
10499
package-lock.json
generated
10499
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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 }));
|
||||
|
@ -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(":", "_");
|
||||
}
|
@ -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) => {};
|
||||
}
|
||||
|
@ -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({
|
||||
|
@ -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
87
src/util/ipAddress.ts
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user