diff --git a/controllers/forms.js b/controllers/forms.js index ff9209e3..784f50ea 100644 --- a/controllers/forms.js +++ b/controllers/forms.js @@ -43,6 +43,7 @@ const express = require('express') , registerAccount = require(__dirname+'/../models/forms/register.js') , checkPermsMiddleware = require(__dirname+'/../helpers/checks/haspermsmiddleware.js') , checkPerms = require(__dirname+'/../helpers/checks/hasperms.js') + , spamCheck = require(__dirname+'/../helpers/checks/spamcheck.js') , paramConverter = require(__dirname+'/../helpers/paramconverter.js') , banCheck = require(__dirname+'/../helpers/checks/bancheck.js') , verifyCaptcha = require(__dirname+'/../helpers/captcha/captchaverify.js') @@ -245,6 +246,16 @@ router.post('/board/:board/post', Boards.exists, banCheck, postFiles, paramConve }); } + const flood = await spamCheck(req, res); + if (flood) { + deleteTempFiles(req).catch(e => console.error); + return res.status(429).render('message', { + 'title': 'Too many requests', + 'message': 'Flood detected', + 'redirect': `/${req.params.board}${req.body.thread ? '/thread/' + req.body.thread + '.html' : ''}` + }); + } + try { await makePost(req, res, next); } catch (err) { diff --git a/helpers/checks/spamcheck.js b/helpers/checks/spamcheck.js new file mode 100644 index 00000000..7458ce53 --- /dev/null +++ b/helpers/checks/spamcheck.js @@ -0,0 +1,66 @@ +'use strict'; + +const Mongo = require(__dirname+'/../../db/db.js') + , Posts = require(__dirname+'/../../db/posts.js') + , msTime = require(__dirname+'/../mstime.js') + , hasPerms = require(__dirname+'/hasperms.js') + +module.exports = async (req, res) => { + + if (hasPerms(req, res)) { + return false; + } + + const ip = req.headers['x-real-ip'] || req.connection.remoteAddress; + const now = Date.now(); + const last120id = Mongo.ObjectId.createFromTime(Math.floor((now - (msTime.minute*2))/1000)); + const last30id = Mongo.ObjectId.createFromTime(Math.floor((now - (msTime.minute*0.5))/1000)); + const last15id = Mongo.ObjectId.createFromTime(Math.floor((now - (msTime.minute*0.25))/1000)); + const ors = []; + const contentOr = []; + if (res.locals.numFiles > 0) { + contentOr.push({ + 'files': { + '$elemMatch': { + 'hash': { //any file hash will match, doesnt need to be all + '$in': req.files.file.map(f => f.sha256) + } + } + } + }); + } + if (req.body.message) { + contentOr.push({ + 'nomarkup': req.body.message + }) + } + //matching content from any IP in the past 30 seconds + ors.push({ + '_id': { + '$gt': last30id + }, + '$or': contentOr + }); + //matching content from same IP in last 2 minutes + ors.push({ + '_id': { + '$gt': last120id + }, + 'ip': ip, + '$or': contentOr + }); + //any posts from same IP in past 15 seconds + ors.push({ + '_id': { + '$gt': last15id + }, + 'ip': ip + }) + + let flood = await Posts.db.find({ + '$or': ors + }).toArray(); + + return flood.length > 0; + +}