summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgumi <git@gumi.ca>2020-03-06 15:36:44 -0500
committergumi <git@gumi.ca>2020-03-06 16:03:34 -0500
commit86be43f1c0143495abe003654a4e415a154b11d4 (patch)
tree9ddfd0f8092369ba787ca8f0b49788d8684f4e1c
parent67ee81e912ab26930b3152ab3f35712cc68573e7 (diff)
downloadapi-86be43f1c0143495abe003654a4e415a154b11d4.tar.gz
api-86be43f1c0143495abe003654a4e415a154b11d4.tar.bz2
api-86be43f1c0143495abe003654a4e415a154b11d4.tar.xz
api-86be43f1c0143495abe003654a4e415a154b11d4.zip
prevent uuid bruteforcing
-rw-r--r--src/routers/vault/middlewares/account.js19
-rw-r--r--src/routers/vault/middlewares/evol/account.js33
-rw-r--r--src/routers/vault/middlewares/identity.js15
-rw-r--r--src/routers/vault/middlewares/legacy/account.js33
-rw-r--r--src/routers/vault/middlewares/session.js41
-rw-r--r--src/routers/vault/models/vault/login.js5
-rw-r--r--src/routers/vault/types/Session.js2
7 files changed, 140 insertions, 8 deletions
diff --git a/src/routers/vault/middlewares/account.js b/src/routers/vault/middlewares/account.js
index 9360728..42a63a4 100644
--- a/src/routers/vault/middlewares/account.js
+++ b/src/routers/vault/middlewares/account.js
@@ -44,6 +44,7 @@ const get_data = async (req, res, next) => {
// TODO: make this a method of Session
primaryIdentity: session.primaryIdentity,
allowNonPrimary: session.allowNonPrimary,
+ strictIPCheck: session.strictIPCheck,
vaultId: session.vault,
},
});
@@ -64,7 +65,7 @@ const update_account = async (req, res, next) => {
}
if (!req.body || !Reflect.has(req.body, "primary") || !Reflect.has(req.body, "allow") ||
- !Number.isInteger(req.body.primary)) {
+ !Reflect.has(req.body, "strict") || !Number.isInteger(req.body.primary)) {
res.status(400).json({
status: "error",
error: "invalid format",
@@ -94,6 +95,17 @@ const update_account = async (req, res, next) => {
return;
}
+ if (session.strictIPCheck && session.ip !== req.ip) {
+ // the ip is not the same
+ res.status(401).json({
+ status: "error",
+ error: "ip address mismatch",
+ });
+ req.app.locals.logger.warn(`Vault.account: ip address mismatch <${session.vault}@vault> [${req.ip}]`);
+ req.app.locals.cooldown(req, 3e5);
+ return;
+ }
+
const update_fields = {};
if (session.primaryIdentity !== req.body.primary) {
@@ -122,6 +134,10 @@ const update_account = async (req, res, next) => {
// update allow non-primary
update_fields.allowNonPrimary = !!req.body.allow;
}
+ if (session.strictIPCheck !== !!req.body.strict) {
+ // update allow non-primary
+ update_fields.strictIPCheck = !!req.body.strict;
+ }
// update SQL
if (Object.keys(update_fields).length) {
@@ -132,6 +148,7 @@ const update_account = async (req, res, next) => {
// now update our cache
session.allowNonPrimary = !!req.body.allow;
+ session.strictIPCheck = !!req.body.strict;
session.primaryIdentity = +req.body.primary;
for (const ident of session.identities) {
diff --git a/src/routers/vault/middlewares/evol/account.js b/src/routers/vault/middlewares/evol/account.js
index 50248b2..3a22158 100644
--- a/src/routers/vault/middlewares/evol/account.js
+++ b/src/routers/vault/middlewares/evol/account.js
@@ -42,6 +42,17 @@ const get_accounts = async (req, res, next) => {
return;
}
+ if (session.strictIPCheck && session.ip !== req.ip) {
+ // the ip is not the same
+ res.status(403).json({
+ status: "error",
+ error: "ip address mismatch",
+ });
+ req.app.locals.logger.warn(`Vault.evol.account: ip address mismatch <${session.vault}@vault> [${req.ip}]`);
+ req.app.locals.cooldown(req, 3e5);
+ return;
+ }
+
res.status(200).json({
status: "success",
accounts: session.gameAccounts,
@@ -96,6 +107,17 @@ const new_account = async (req, res, next) => {
return;
}
+ if (session.strictIPCheck && session.ip !== req.ip) {
+ // the ip is not the same
+ res.status(403).json({
+ status: "error",
+ error: "ip address mismatch",
+ });
+ req.app.locals.logger.warn(`Vault.evol.account: ip address mismatch <${session.vault}@vault> [${req.ip}]`);
+ req.app.locals.cooldown(req, 3e5);
+ return;
+ }
+
// this check is necessary because login.userid has no UNIQUE constraint
const existing = await req.app.locals.evol.login.findOne({
where: {userid: req.body.username}
@@ -190,6 +212,17 @@ const update_account = async (req, res, next) => {
return;
}
+ if (session.strictIPCheck && session.ip !== req.ip) {
+ // the ip is not the same
+ res.status(403).json({
+ status: "error",
+ error: "ip address mismatch",
+ });
+ req.app.locals.logger.warn(`Vault.evol.account: ip address mismatch <${session.vault}@vault> [${req.ip}]`);
+ req.app.locals.cooldown(req, 3e5);
+ return;
+ }
+
let account = null;
for (const acc of session.gameAccounts) {
if (acc.accountId === req.body.accountId) {
diff --git a/src/routers/vault/middlewares/identity.js b/src/routers/vault/middlewares/identity.js
index 8ee5b6b..158e5df 100644
--- a/src/routers/vault/middlewares/identity.js
+++ b/src/routers/vault/middlewares/identity.js
@@ -88,7 +88,14 @@ const add_identity = async (req, res, next) => {
status: "error",
error: "token has expired",
});
- req.app.locals.cooldown(req, 15e3);
+
+ // max 3 attempts per 15 minutes
+ if (req.app.locals.brute.consume(req, 3, 9e5)) {
+ req.app.locals.cooldown(req, 15e3);
+ } else {
+ req.app.locals.logger.warn(`Vault.identity: validation request flood [${req.ip}]`);
+ req.app.locals.cooldown(req, 3.6e6);
+ }
return;
}
@@ -217,7 +224,11 @@ const add_identity = async (req, res, next) => {
return;
}
- const uuid = uuidv4();
+ let uuid;
+ do { // avoid collisions
+ uuid = uuidv4();
+ } while (req.app.locals.session.get(uuid));
+
req.app.locals.identity_pending.set(uuid, {
ip: req.ip,
vault: session.vault,
diff --git a/src/routers/vault/middlewares/legacy/account.js b/src/routers/vault/middlewares/legacy/account.js
index fb507de..29da5a6 100644
--- a/src/routers/vault/middlewares/legacy/account.js
+++ b/src/routers/vault/middlewares/legacy/account.js
@@ -48,6 +48,17 @@ const get_accounts = async (req, res, next) => {
return;
}
+ if (session.strictIPCheck && session.ip !== req.ip) {
+ // the ip is not the same
+ res.status(403).json({
+ status: "error",
+ error: "ip address mismatch",
+ });
+ req.app.locals.logger.warn(`Vault.legacy.account: ip address mismatch <${session.vault}@vault> [${req.ip}]`);
+ req.app.locals.cooldown(req, 3e5);
+ return;
+ }
+
res.status(200).json({
status: "success",
accounts: session.legacyAccounts,
@@ -101,6 +112,17 @@ const claim_by_password = async (req, res, next) => {
return;
}
+ if (session.strictIPCheck && session.ip !== req.ip) {
+ // the ip is not the same
+ res.status(403).json({
+ status: "error",
+ error: "ip address mismatch",
+ });
+ req.app.locals.logger.warn(`Vault.legacy.account: ip address mismatch <${session.vault}@vault> [${req.ip}]`);
+ req.app.locals.cooldown(req, 3e5);
+ return;
+ }
+
const legacy = await req.app.locals.legacy.login.findOne({
where: {userid: req.body.username}
});
@@ -255,6 +277,17 @@ const migrate = async (req, res, next) => {
return;
}
+ if (session.strictIPCheck && session.ip !== req.ip) {
+ // the ip is not the same
+ res.status(403).json({
+ status: "error",
+ error: "ip address mismatch",
+ });
+ req.app.locals.logger.warn(`Vault.legacy.account: ip address mismatch <${session.vault}@vault> [${req.ip}]`);
+ req.app.locals.cooldown(req, 3e5);
+ return;
+ }
+
let legacy = null;
// check if we own it
diff --git a/src/routers/vault/middlewares/session.js b/src/routers/vault/middlewares/session.js
index be5787a..4451080 100644
--- a/src/routers/vault/middlewares/session.js
+++ b/src/routers/vault/middlewares/session.js
@@ -74,8 +74,29 @@ const auth_session = async (req, res, next) => {
identity: null,
}
});
- // don't log: this can get spammy
- req.app.locals.cooldown(req, 1e3);
+
+ // max 3 attempts per 15 minutes
+ if (req.app.locals.brute.consume(req, 3, 9e5)) {
+ req.app.locals.cooldown(req, 1e3);
+ } else {
+ req.app.locals.logger.warn(`Vault.session: authentication request flood [${req.ip}]`);
+ req.app.locals.cooldown(req, 3.6e6);
+ }
+ return;
+ }
+
+ if (session.strictIPCheck && session.ip !== req.ip) {
+ // not the same ip
+ res.status(403).json({
+ status: "error",
+ error: "ip address mismatch",
+ session: {
+ expires: 0,
+ identity: null,
+ }
+ });
+
+ req.app.locals.cooldown(req, 5e3);
return;
}
@@ -122,6 +143,7 @@ const auth_session = async (req, res, next) => {
session.identity = ident.id;
session.primaryIdentity = ident.id;
session.allowNonPrimary = user.allowNonPrimary;
+ session.strictIPCheck = user.strictIPCheck;
session.identities = [{
// TODO: make this a class!
email: ident.email,
@@ -215,7 +237,11 @@ const new_session = async (req, res, next) => {
if (Reflect.has(req.body, "confirm") && req.body.confirm === true) {
// account creation request
- const uuid = uuidv4();
+ let uuid;
+ do { // avoid collisions
+ uuid = uuidv4();
+ } while (req.app.locals.session.get(uuid));
+
const session = new Session(req.ip, req.body.email);
req.app.locals.session.set(uuid, session);
@@ -276,7 +302,7 @@ const new_session = async (req, res, next) => {
// auth flow
if (account.primaryIdentity === null || account.primaryIdentity === undefined) {
// the vault account has no primary identity (bug): let's fix this
- console.warn(`Vault.session: fixing account with no primary identity <${session.vault}@vault> [${req.ip}]`);
+ console.warn(`Vault.session: fixing account with no primary identity <${account.id}@vault> [${req.ip}]`);
account.primaryIdentity = identity.id;
await account.save();
} else if (identity.id !== account.primaryIdentity && !account.allowNonPrimary) {
@@ -290,11 +316,16 @@ const new_session = async (req, res, next) => {
// TODO: if account has WebAuthn do WebAuthn authentication flow
- const uuid = uuidv4();
+ let uuid;
+ do { // avoid collisions
+ uuid = uuidv4();
+ } while (req.app.locals.session.get(uuid));
+
const session = new Session(req.ip, req.body.email);
session.vault = account.id;
session.primaryIdentity = account.primaryIdentity;
session.allowNonPrimary = account.allowNonPrimary;
+ session.strictIPCheck = account.strictIPCheck;
session.identity = identity.id;
req.app.locals.session.set(uuid, session);
diff --git a/src/routers/vault/models/vault/login.js b/src/routers/vault/models/vault/login.js
index 1c9c51e..262ed65 100644
--- a/src/routers/vault/models/vault/login.js
+++ b/src/routers/vault/models/vault/login.js
@@ -17,6 +17,11 @@ module.exports = {
defaultValue: true,
allowNull: false,
},
+ strictIPCheck: {
+ type: Sequelize.BOOLEAN,
+ defaultValue: true,
+ allowNull: false,
+ },
creationDate: {
type: Sequelize.DATE,
allowNull: false,
diff --git a/src/routers/vault/types/Session.js b/src/routers/vault/types/Session.js
index 1809cac..ff7e20d 100644
--- a/src/routers/vault/types/Session.js
+++ b/src/routers/vault/types/Session.js
@@ -24,6 +24,8 @@ module.exports = class Session {
gameAccounts = [];
/** ip that was used to init the session */
ip;
+ /** refuse to authenticate a session with a different IP */
+ strictIPCheck = true;
constructor (ip, email) {
this.ip = ip;