Flood detection
このコミットが含まれているのは:
コミット
8c9b0d9da0
|
@ -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
|
||||
|
|
読み込み中…
新しいイシューから参照