Flood detection

このコミットが含まれているのは:
n9k 2022-02-20 22:07:32 +00:00
コミット 8c9b0d9da0
6個のファイルの変更39行の追加9行の削除

ファイルの表示

@ -42,6 +42,8 @@ def create_app(config_file):
'CHAT_NAME_MAX_LENGTH': config['chat']['max_name_length'],
'CHAT_NAME_MIN_CONTRAST': config['chat']['min_name_contrast'],
'CHAT_BACKGROUND_COLOUR': color_to_colour(config['chat']['background_color']),
'FLOOD_DURATION': config['flood']['duration'],
'FLOOD_THRESHOLD': config['flood']['threshold'],
'CAPTCHA_LIFETIME': config['captcha']['lifetime'],
'CAPTCHA_FONTS': config['captcha']['fonts'],
'CAPTCHA_ALPHABET': config['captcha']['alphabet'],

ファイルの表示

@ -35,6 +35,12 @@ def get_random_captcha_digest():
return digest
def get_random_captcha_digest_for(user):
if user['verified']:
return None
else:
return get_random_captcha_digest()
def get_captcha_image(digest):
try:
captcha = CAPTCHAS[digest]

ファイルの表示

@ -1,9 +1,9 @@
from quart import current_app, request, render_template, redirect, url_for, escape, Markup
from anonstream.captcha import get_random_captcha_digest
from anonstream.captcha import get_random_captcha_digest_for
from anonstream.chat import add_chat_message, Rejected
from anonstream.stream import get_stream_title
from anonstream.user import add_state, pop_state, try_change_appearance, verify, BadCaptcha
from anonstream.user import add_state, pop_state, try_change_appearance, verify, deverify, BadCaptcha
from anonstream.routes.wrappers import with_user_from, render_template_with_etag
from anonstream.helpers.chat import get_scrollback
from anonstream.helpers.user import get_default_name
@ -45,14 +45,13 @@ async def nojs_form(user):
state_id = request.args.get('state', type=int)
state = pop_state(user, state_id)
prefer_chat_form = request.args.get('landing') != 'appearance'
digest = None if user['verified'] else get_random_captcha_digest()
return await render_template(
'nojs_form.html',
user=user,
state=state,
prefer_chat_form=prefer_chat_form,
nonce=generate_nonce(),
digest=digest,
digest=get_random_captcha_digest_for(user),
default_name=get_default_name(user),
)
@ -86,7 +85,7 @@ async def nojs_submit_message(user):
else:
nonce = form.get('nonce', '')
try:
# if the comment is empty but the captcha was just solved,
# If the comment is empty but the captcha was just solved,
# be lenient: don't raise an exception and don't create a notice
add_chat_message(
user,
@ -98,6 +97,7 @@ async def nojs_submit_message(user):
notice, *_ = e.args
state_id = add_state(user, notice=notice)
else:
deverify(user)
state_id = None
return redirect(url_for(

ファイルの表示

@ -131,3 +131,19 @@ def verify(user, digest, answer):
verification_happened = True
return verification_happened
@with_timestamp
def deverify(timestamp, user):
if not user['verified']:
return
n_user_messages = 0
for message in reversed(MESSAGES):
message_sent_ago = timestamp - message['timestamp']
if message_sent_ago >= CONFIG['FLOOD_DURATION']:
break
elif message['token'] == user['token']:
n_user_messages += 1
if n_user_messages >= CONFIG['FLOOD_THRESHOLD']:
user['verified'] = False

ファイルの表示

@ -3,9 +3,9 @@ import asyncio
from quart import current_app, websocket
from anonstream.stream import get_stream_title, get_stream_uptime
from anonstream.captcha import get_random_captcha_digest
from anonstream.captcha import get_random_captcha_digest_for
from anonstream.chat import get_all_messages_for_websocket, add_chat_message, Rejected
from anonstream.user import get_all_users_for_websocket, see, verify, BadCaptcha
from anonstream.user import get_all_users_for_websocket, see, verify, deverify, BadCaptcha
from anonstream.utils.chat import generate_nonce
from anonstream.utils.websocket import parse_websocket_data, Malformed
@ -24,7 +24,7 @@ async def websocket_outbound(queue, user):
False: CONFIG['DEFAULT_ANON_NAME'],
},
'scrollback': CONFIG['MAX_CHAT_SCROLLBACK'],
'digest': None if user['verified'] else get_random_captcha_digest(),
'digest': get_random_captcha_digest_for(user),
}
await websocket.send_json(payload)
while True:
@ -51,7 +51,7 @@ async def websocket_inbound(queue, user):
payload = {
'type': 'captcha',
'notice': notice,
'digest': get_random_captcha_digest(),
'digest': get_random_captcha_digest_for(user),
}
else:
try:
@ -63,9 +63,11 @@ async def websocket_inbound(queue, user):
'notice': notice,
}
else:
deverify(user)
payload = {
'type': 'ack',
'nonce': nonce,
'next': generate_nonce(),
'digest': get_random_captcha_digest_for(user),
}
queue.put_nowait(payload)

ファイルの表示

@ -35,6 +35,10 @@ max_name_length = 24
min_name_contrast = 3.0
background_color = "#232327"
[flood]
duration = 20
threshold = 4
[thresholds]
user_notwatching = 8
user_tentative = 20