change Blacklist/Whitelist tables to not use ip.toLong to avoid weird results on IPv6

This commit is contained in:
HF 2020-01-08 00:32:55 +01:00
parent 62337771a2
commit 759bffbf1f
9 changed files with 33 additions and 71 deletions

5
package-lock.json generated
View File

@ -6718,11 +6718,6 @@
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
}, },
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"ip-address": { "ip-address": {
"version": "6.2.0", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-6.2.0.tgz", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-6.2.0.tgz",

View File

@ -41,7 +41,6 @@
"global": "^4.3.2", "global": "^4.3.2",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"http-proxy-agent": "^3.0.0", "http-proxy-agent": "^3.0.0",
"ip": "^1.1.5",
"ip-address": "^6.2.0", "ip-address": "^6.2.0",
"isomorphic-fetch": "^2.2.1", "isomorphic-fetch": "^2.2.1",
"keycode": "^2.1.9", "keycode": "^2.1.9",

View File

@ -2,10 +2,10 @@
* @flow * @flow
* */ * */
import IP from 'ip'; import fetch from '../utils/proxiedFetch';
import fetch from '../utils/proxiedFetch.js';
import redis from '../data/redis'; import redis from '../data/redis';
import { getIPv6Subnet } from '../utils/ip';
import { Blacklist, Whitelist } from '../data/models'; import { Blacklist, Whitelist } from '../data/models';
import logger from './logger'; import logger from './logger';
@ -108,11 +108,10 @@ async function getCombined(ip: string): Promise<boolean> {
* @return true if blacklisted * @return true if blacklisted
*/ */
async function isBlacklisted(ip: string): Promise<boolean> { async function isBlacklisted(ip: string): Promise<boolean> {
const numIp = IP.toLong(ip);
const count = await Blacklist const count = await Blacklist
.count({ .count({
where: { where: {
numIp, ip,
}, },
}); });
return count !== 0; return count !== 0;
@ -124,11 +123,10 @@ async function isBlacklisted(ip: string): Promise<boolean> {
* @return true if whitelisted * @return true if whitelisted
*/ */
async function isWhitelisted(ip: string): Promise<boolean> { async function isWhitelisted(ip: string): Promise<boolean> {
const numIp = IP.toLong(ip);
const count = await Whitelist const count = await Whitelist
.count({ .count({
where: { where: {
numIp, ip,
}, },
}); });
return count !== 0; return count !== 0;
@ -137,7 +135,7 @@ async function isWhitelisted(ip: string): Promise<boolean> {
/* /*
* dummy function to include if you don't want any proxycheck * dummy function to include if you don't want any proxycheck
*/ */
async function dummy(ip: string): Promise<boolean> { async function dummy(): Promise<boolean> {
return false; return false;
} }
@ -149,7 +147,8 @@ async function dummy(ip: string): Promise<boolean> {
*/ */
async function withoutCache(f, ip) { async function withoutCache(f, ip) {
if (!ip) return true; if (!ip) return true;
return !(await isWhitelisted(ip)) && (await isBlacklisted(ip) || await f(ip)); const ipKey = getIPv6Subnet(ip);
return !(await isWhitelisted(ipKey)) && (await isBlacklisted(ipKey) || await f(ip));
} }
/* /*
@ -160,11 +159,12 @@ async function withoutCache(f, ip) {
* @return true if proxy or blacklisted, false if not or whitelisted * @return true if proxy or blacklisted, false if not or whitelisted
*/ */
let lock = 4; let lock = 4;
const checking = new Array(); const checking = [];
async function withCache(f, ip) { async function withCache(f, ip) {
if (!ip) return true; if (!ip) return true;
// get from cache, if there // get from cache, if there
const key = `isprox:${ip}`; const ipKey = getIPv6Subnet(ip);
const key = `isprox:${ipKey}`;
const cache = await redis.getAsync(key); const cache = await redis.getAsync(key);
if (cache) { if (cache) {
const str = cache.toString('utf8'); const str = cache.toString('utf8');
@ -176,20 +176,20 @@ async function withCache(f, ip) {
// else make asynchronous ipcheck and assume no proxy in the meantime // else make asynchronous ipcheck and assume no proxy in the meantime
// use lock to just check three at a time // use lock to just check three at a time
// do not check ip that currently gets checked // do not check ip that currently gets checked
if (checking.indexOf(ip) == -1 && lock > 0) { if (checking.indexOf(ipKey) == -1 && lock > 0) {
lock -= 1; lock -= 1;
checking.push(ip); checking.push(ipKey);
withoutCache(f, ip) withoutCache(f, ip)
.then((result) => { .then((result) => {
const value = result ? 'y' : 'n'; const value = result ? 'y' : 'n';
redis.setAsync(key, value, 'EX', 3 * 24 * 3600); // cache for three days redis.setAsync(key, value, 'EX', 3 * 24 * 3600); // cache for three days
const pos = checking.indexOf(ip); const pos = checking.indexOf(ipKey);
if (~pos) checking.splice(pos, 1); if (~pos) checking.splice(pos, 1);
lock += 1; lock += 1;
}) })
.catch((error) => { .catch((error) => {
logger.error('PROXYCHECK withCache %s', error.message || error); logger.error('PROXYCHECK withCache %s', error.message || error);
const pos = checking.indexOf(ip); const pos = checking.indexOf(ipKey);
if (~pos) checking.splice(pos, 1); if (~pos) checking.splice(pos, 1);
lock += 1; lock += 1;
}); });

View File

@ -6,33 +6,17 @@
*/ */
import DataType from 'sequelize'; import DataType from 'sequelize';
import nodeIp from 'ip';
import Model from '../sequelize'; import Model from '../sequelize';
const Blacklist = Model.define('Blacklist', { const Blacklist = Model.define('Blacklist', {
numIp: { ip: {
type: DataType.INTEGER.UNSIGNED, type: DataType.CHAR(39),
allowNull: false, allowNull: false,
primaryKey: true, primaryKey: true,
}, },
}, {
getterMethods: {
ip(): string {
return nodeIp.fromLong(this.numIp);
},
},
setterMethods: {
ip(value: string): number {
this.setDataValue('numIp', nodeIp.toLong(value));
},
},
}); });
export default Blacklist; export default Blacklist;

View File

@ -7,33 +7,17 @@
*/ */
import DataType from 'sequelize'; import DataType from 'sequelize';
import nodeIp from 'ip';
import Model from '../sequelize'; import Model from '../sequelize';
const Whitelist = Model.define('Whitelist', { const Whitelist = Model.define('Whitelist', {
numIp: { ip: {
type: DataType.INTEGER.UNSIGNED, type: DataType.CHAR(39),
allowNull: false, allowNull: false,
primaryKey: true, primaryKey: true,
}, },
}, {
getterMethods: {
ip(): string {
return nodeIp.fromLong(this.numIp);
},
},
setterMethods: {
ip(value: string): number {
this.setDataValue('numIp', nodeIp.toLong(value));
},
},
}); });
export default Whitelist; export default Whitelist;

View File

@ -4,7 +4,6 @@
* @flow * @flow
*/ */
import nodeIp from 'ip';
import express from 'express'; import express from 'express';
import expressLimiter from 'express-limiter'; import expressLimiter from 'express-limiter';
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
@ -12,7 +11,7 @@ import bodyParser from 'body-parser';
import sharp from 'sharp'; import sharp from 'sharp';
import multer from 'multer'; import multer from 'multer';
import { getIPFromRequest } from '../utils/ip'; import { getIPFromRequest, getIPv6Subnet } from '../utils/ip';
import { getIdFromObject } from '../core/utils'; import { getIdFromObject } from '../core/utils';
import redis from '../data/redis'; import redis from '../data/redis';
import session from '../core/session'; import session from '../core/session';
@ -64,17 +63,20 @@ router.use(passport.session());
router.use(async (req, res, next) => { router.use(async (req, res, next) => {
const ip = await getIPFromRequest(req); const ip = await getIPFromRequest(req);
if (!req.user) { if (!req.user) {
logger.info(`${ip} tried to access admintools without login`); logger.info(`ADMINTOOLS: ${ip} tried to access admintools without login`);
res.status(403).send('You are not logged in'); res.status(403).send('You are not logged in');
return; return;
} }
if (!req.user.isAdmin()) { if (!req.user.isAdmin()) {
logger.info( logger.info(
`${ip} / ${req.user.id} tried to access admintools but isn't Admin`, `ADMINTOOLS: ${ip}/${req.user.id} wrongfully tried to access admintools`,
); );
res.status(403).send('You are not allowed to access this page'); res.status(403).send('You are not allowed to access this page');
return; return;
} }
logger.info(
`ADMINTOOLS: ${req.user.id} / ${req.user.regUser.name} is using admintools`,
);
next(); next();
}); });
@ -86,31 +88,32 @@ router.use(async (req, res, next) => {
* @return true if successful * @return true if successful
*/ */
async function executeAction(action: string, ip: string): boolean { async function executeAction(action: string, ip: string): boolean {
const numIp = nodeIp.toLong(ip); const ipKey = getIPv6Subnet(ip);
const key = `isprox:${ip}`; const key = `isprox:${ipKey}`;
logger.info(`ADMINTOOLS: ${action} ${ip}`);
switch (action) { switch (action) {
case 'ban': case 'ban':
await Blacklist.findOrCreate({ await Blacklist.findOrCreate({
where: { numIp }, where: { ip: ipKey },
}); });
await redis.setAsync(key, 'y', 'EX', 24 * 3600); await redis.setAsync(key, 'y', 'EX', 24 * 3600);
break; break;
case 'unban': case 'unban':
await Blacklist.destroy({ await Blacklist.destroy({
where: { numIp }, where: { ip: ipKey },
}); });
await redis.del(key); await redis.del(key);
break; break;
case 'whitelist': case 'whitelist':
await Whitelist.findOrCreate({ await Whitelist.findOrCreate({
where: { numIp }, where: { ip: ipKey },
}); });
await redis.setAsync(key, 'n', 'EX', 24 * 3600); await redis.setAsync(key, 'n', 'EX', 24 * 3600);
break; break;
case 'unwhitelist': case 'unwhitelist':
await Whitelist.destroy({ await Whitelist.destroy({
where: { numIp }, where: { ip: ipKey },
}); });
await redis.del(key); await redis.del(key);
break; break;

View File

@ -4,7 +4,6 @@
*/ */
import type { Request, Response } from 'express'; import type { Request, Response } from 'express';
import nodeIp from 'ip';
import draw from '../../core/draw'; import draw from '../../core/draw';
import { blacklistDetector, cheapDetector, strongDetector } from '../../core/isProxy'; import { blacklistDetector, cheapDetector, strongDetector } from '../../core/isProxy';
@ -83,9 +82,8 @@ async function checkHuman(req: Request, res: Response, next) {
try { try {
const { token } = req.body; const { token } = req.body;
const numIp = nodeIp.toLong(ip);
const key = `human:${ip}:${ip}`; const key = `human:${ip}`;
const ttl: number = await redis.ttlAsync(key); const ttl: number = await redis.ttlAsync(key);
if (ttl > 0) { if (ttl > 0) {

View File

@ -4,7 +4,6 @@
* @flow * @flow
*/ */
import nodeIp from 'ip';
import express from 'express'; import express from 'express';
import expressLimiter from 'express-limiter'; import expressLimiter from 'express-limiter';
import bodyParser from 'body-parser'; import bodyParser from 'body-parser';

View File

@ -17,7 +17,7 @@ function randomProxyURL() {
return rand; return rand;
} }
export function fetch(url, options = {}) { function fetch(url, options = {}) {
if (proxylist.length === 0) { if (proxylist.length === 0) {
return isoFetch(url, options); return isoFetch(url, options);
} }