Flood protection

This commit is contained in:
Piero Toffanin 2021-05-16 11:50:22 -04:00
parent 4c5d828300
commit 1ac27aeb82
4 changed files with 73 additions and 3 deletions

View file

@ -5,8 +5,7 @@ from flask_swagger_ui import get_swaggerui_blueprint
from pkg_resources import resource_filename from pkg_resources import resource_filename
from .api_keys import Database from .api_keys import Database
from app.language import detect_languages, transliterate from app.language import detect_languages, transliterate
from app import flood
api_keys_db = None
def get_json_dict(request): def get_json_dict(request):
d = request.get_json() d = request.get_json()
@ -89,6 +88,9 @@ def create_app(args):
from .no_limiter import Limiter from .no_limiter import Limiter
limiter = Limiter() limiter = Limiter()
if args.req_flood_threshold > 0:
flood.setup(args.req_flood_threshold)
@app.errorhandler(400) @app.errorhandler(400)
def invalid_api(e): def invalid_api(e):
return jsonify({"error": str(e.description)}), 400 return jsonify({"error": str(e.description)}), 400
@ -99,8 +101,14 @@ def create_app(args):
@app.errorhandler(429) @app.errorhandler(429)
def slow_down_error(e): def slow_down_error(e):
flood.report(get_remote_address())
return jsonify({"error": "Slowdown: " + str(e.description)}), 429 return jsonify({"error": "Slowdown: " + str(e.description)}), 429
@app.errorhandler(403)
def denied(e):
return jsonify({"error": str(e.description)}), 403
@app.route("/") @app.route("/")
@limiter.exempt @limiter.exempt
def index(): def index():
@ -236,7 +244,18 @@ def create_app(args):
error: error:
type: string type: string
description: Reason for slow down description: Reason for slow down
403:
description: Banned
schema:
id: error-response
type: object
properties:
error:
type: string
description: Error message
""" """
if flood.is_banned(get_remote_address()):
abort(403, description="Too many request limits violations")
if request.is_json: if request.is_json:
json = get_json_dict(request) json = get_json_dict(request)
@ -369,7 +388,19 @@ def create_app(args):
error: error:
type: string type: string
description: Reason for slow down description: Reason for slow down
403:
description: Banned
schema:
id: error-response
type: object
properties:
error:
type: string
description: Error message
""" """
if flood.is_banned(get_remote_address()):
abort(403, description="Too many request limits violations")
if request.is_json: if request.is_json:
json = get_json_dict(request) json = get_json_dict(request)
q = json.get('q') q = json.get('q')

36
app/flood.py Normal file
View file

@ -0,0 +1,36 @@
import time
import atexit
from apscheduler.schedulers.background import BackgroundScheduler
banned = {}
active = False
threshold = -1
def clear_banned():
global banned
print(banned)
banned = {}
def setup(violations_threshold = 100):
global active
global threshold
active = True
threshold = violations_threshold
scheduler = BackgroundScheduler()
scheduler.add_job(func=clear_banned, trigger="interval", weeks=4)
scheduler.start()
# Shut down the scheduler when exiting the app
atexit.register(lambda: scheduler.shutdown())
def report(request_ip):
banned[request_ip] = banned.get(request_ip, 0)
banned[request_ip] += 1
def is_banned(request_ip):
# More than X offences?
return active and banned.get(request_ip, 0) >= threshold

View file

@ -14,6 +14,8 @@ def main():
help='Set the default maximum number of requests per minute per client (%(default)s)') help='Set the default maximum number of requests per minute per client (%(default)s)')
parser.add_argument('--daily-req-limit', default=-1, type=int, metavar="<number>", parser.add_argument('--daily-req-limit', default=-1, type=int, metavar="<number>",
help='Set the default maximum number of requests per day per client, in addition to req-limit. (%(default)s)') help='Set the default maximum number of requests per day per client, in addition to req-limit. (%(default)s)')
parser.add_argument('--req-flood-threshold', default=-1, type=int, metavar="<number>",
help='Set the maximum number of request limit offences per 4 weeks that a client can exceed before being banned. (%(default)s)')
parser.add_argument('--batch-limit', default=-1, type=int, metavar="<number of texts>", parser.add_argument('--batch-limit', default=-1, type=int, metavar="<number of texts>",
help='Set maximum number of texts to translate in a batch request (%(default)s)') help='Set maximum number of texts to translate in a batch request (%(default)s)')
parser.add_argument('--ga-id', type=str, default=None, metavar="<GA ID>", parser.add_argument('--ga-id', type=str, default=None, metavar="<GA ID>",

View file

@ -10,3 +10,4 @@ pycld2==0.41
morfessor==2.0.6 morfessor==2.0.6
polyglot==16.7.4 polyglot==16.7.4
appdirs==1.4.4 appdirs==1.4.4
APScheduler==3.7.0