Skip to content

Commit

Permalink
Throttle failed logins by target userid as well as IP
Browse files Browse the repository at this point in the history
  • Loading branch information
mia-pi-git committed Apr 6, 2024
1 parent 9e57116 commit 3fcde7d
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 4 deletions.
4 changes: 2 additions & 2 deletions src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ export const actions: {[k: string]: QueryHandler} = {
}
let teams = [];
try {
teams = await tables.teams.selectAll<any>(
teams = await tables.teams.selectAll(
SQL`teamid, team, format, title as name`
)`WHERE ownerid = ${this.user.id}`;
} catch (e) {
Expand Down Expand Up @@ -717,7 +717,7 @@ export const actions: {[k: string]: QueryHandler} = {
throw new ActionError("Invalid team ID");
}
try {
const data = await tables.teams.selectOne<any>(
const data = await tables.teams.selectOne(
SQL`ownerid, team, private as privacy`
)`WHERE teamid = ${teamid}`;
if (!data || data.ownerid !== this.user.id) {
Expand Down
8 changes: 8 additions & 0 deletions src/schemas/ntbb-loginattempts.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE `ntbb_loginattempts` (
`count` int(11) NOT NULL,
`userid` varchar(63) COLLATE utf8mb4_bin NOT NULL,
`time` int(11) NOT NULL,
PRIMARY KEY (`userid`),
KEY `count` (`count`),
KEY `time` (`time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
6 changes: 6 additions & 0 deletions src/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ export const loginthrottle = psdb.getTable<{
lastuserid: string;
}>('loginthrottle', 'ip');

export const loginattempts = psdb.getTable<{
count: number;
time: number;
userid: number;
}>('loginattempts', 'userid');

export const usermodlog = psdb.getTable<{
entryid: number;
userid: string;
Expand Down
30 changes: 28 additions & 2 deletions src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import * as gal from 'google-auth-library';
import {SQL} from './database';
import {ActionError, ActionContext} from './server';
import {toID, time, signAsync} from './utils';
import {ladder, loginthrottle, sessions, users, usermodlog} from './tables';
import {
ladder, loginthrottle, loginattempts, sessions, users, usermodlog,
} from './tables';

const SID_DURATION = 2 * 7 * 24 * 60 * 60;
const LOGINTIME_INTERVAL = 24 * 60 * 60;
Expand Down Expand Up @@ -251,7 +253,7 @@ export class Session {
}
const userstate = await users.get(userid);
if (userstate) {
if (userstate.banstate >= 100 || ((userstate as any).password && userstate.nonce)) {
if (userstate.banstate >= 100 || ((userstate).password && userstate.nonce)) {

Check failure on line 256 in src/user.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Property 'password' does not exist on type '{ userid: string; usernum: number; username: string; nonce: string | null; passwordhash: string | null; email: string | null; registertime: number; group: number; banstate: number; ip: string; avatar: number; logintime: number; loginip: string | null; }'.
return ';;Your username is no longer available.';
}
if (userstate.email?.endsWith('@')) {
Expand Down Expand Up @@ -356,6 +358,22 @@ export class Session {
async passwordVerify(name: string, pass: string) {
const ip = this.context.getIp();
const userid = toID(name);
let attempts = await loginattempts.get(userid);
if (attempts) {
// too many attempts, no valid login session from that ip on that userid
if (attempts.count >= 500 && !(await sessions.selectOne()`WHERE ip = ${ip} AND userid = ${userid}`)) {
attempts.count++;
await attempts.update(userid, {time: time(), count: attempts.count});

Check failure on line 366 in src/user.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Property 'update' does not exist on type '{ count: number; time: number; userid: number; }'.
throw new ActionError(
`Too many unrecognized login attempts have been made against this account. Please try again later.`
);
} else if (attempts.time + 24 * 60 * 60 < time()) {
attempts = {

Check failure on line 371 in src/user.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Property 'userid' is missing in type '{ time: number; count: number; }' but required in type '{ count: number; time: number; userid: number; }'.
time: time(),
count: 0,
};
}
}
let throttleTable = await loginthrottle.get(
ip, ['count', 'time']
) as {count: number; time: number} || null;
Expand Down Expand Up @@ -410,6 +428,14 @@ export class Session {
ip, count: 1, lastuserid: userid, time: time(),
});
}
if (attempts) {
attempts.count++;
await attempts.update(userid, {

Check failure on line 433 in src/user.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Property 'update' does not exist on type '{ count: number; time: number; userid: number; }'.
count: attempts.count, time: time(),
});
} else {
await attempts.insert({userid, ip, time: time()});

Check failure on line 437 in src/user.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'attempts' is possibly 'undefined'.
}
return false;
}
// i don't know how often this is actually necessary. so let's make this configurable.
Expand Down

0 comments on commit 3fcde7d

Please sign in to comment.