From 5226044c13817688a5ca3461743844dca4ed3d2b Mon Sep 17 00:00:00 2001 From: Markus Heiser Date: Wed, 19 Apr 2023 18:59:23 +0200 Subject: [PATCH] [mod] limiter: add random token to the limiter URL By adding a random component in the limiter URL a bot can no longer send a ping by request a static URL. Related: https://github.com/searxng/searxng/pull/2357#issuecomment-1518525094 Signed-off-by: Markus Heiser --- searx/plugins/limiter.py | 25 ++++++++++++++++++++++++- searx/templates/simple/base.html | 2 +- searx/webapp.py | 8 +++++--- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/searx/plugins/limiter.py b/searx/plugins/limiter.py index c7d74248b..69bd576d4 100644 --- a/searx/plugins/limiter.py +++ b/searx/plugins/limiter.py @@ -14,6 +14,8 @@ Enable the plugin in ``settings.yml``: """ import re +import string +import random from flask import request from searx import redisdb @@ -54,6 +56,27 @@ def ping(): redis_client.set(secret_hash(ping_key), 1, ex=600) +def get_token(): + redis_client = redisdb.client() + if not redis_client: + # This function is also called when limiter is inactive / no redis DB + # (see render function in webapp.py) + return '12345678' + token = redis_client.get(TOKEN_KEY) + if token: + token = token.decode('UTF-8') + else: + token = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(8)) + redis_client.set(TOKEN_KEY, token, ex=600) + return token + + +def token_is_valid(token): + valid = token == get_token() + logger.debug("token is valid --> %s", valid) + return valid + + def is_accepted_request() -> bool: # pylint: disable=too-many-return-statements redis_client = redisdb.client() @@ -83,7 +106,7 @@ def is_accepted_request() -> bool: c_burst = incr_sliding_window(redis_client, 'IP limit, burst' + x_forwarded_for, 20) c_10min = incr_sliding_window(redis_client, 'IP limit, 10 minutes' + x_forwarded_for, 600) if c_burst > c_burst_max or c_10min > c_10min_max: - logger.debug("BLOCK %s: to many request", x_forwarded_for) + logger.debug("BLOCK %s: too many request", x_forwarded_for) return False if len(request.headers.get('Accept-Language', '').strip()) == '': diff --git a/searx/templates/simple/base.html b/searx/templates/simple/base.html index dfe4ea265..9f7cdbb8e 100644 --- a/searx/templates/simple/base.html +++ b/searx/templates/simple/base.html @@ -18,7 +18,7 @@ {% endif %} {% if get_setting('server.limiter') %} - + {% endif %} {% block styles %}{% endblock %} diff --git a/searx/webapp.py b/searx/webapp.py index 67265e542..815bfcabd 100755 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -416,6 +416,7 @@ def render(template_name: str, **kwargs): kwargs['endpoint'] = 'results' if 'q' in kwargs else request.endpoint kwargs['cookies'] = request.cookies kwargs['errors'] = request.errors + kwargs['limiter_token'] = limiter.get_token() # values from the preferences kwargs['preferences'] = request.preferences @@ -642,9 +643,10 @@ def health(): return Response('OK', mimetype='text/plain') -@app.route('/limiter.css', methods=['GET', 'POST']) -def limiter_css(): - limiter.ping() +@app.route('/limiter.css', methods=['GET', 'POST']) +def limiter_css(token=None): + if limiter.token_is_valid(token): + limiter.ping() return Response('', mimetype='text/css')