josephernest / talktalktalk

TalkTalkTalk is an easy-installable small chat room, with chat history.
251 stars 29 forks source link

Basic flood detection #8

Open josephernest opened 7 years ago

josephernest commented 7 years ago

Rough idea:

On connect of a new client ws :

 bytesent[ws] = 0

We should do this each time a message is posted by a user ws :

 bytesent[ws] += len(message)

Then we should measure if the increase is more than, say, 1kB per minute. If so deconnect, and store the IP of the potential spammer.

If user comes back and second flood, add to IP ban list...

Something else: how to get IP from ws here with websocket: https://github.com/josephernest/talktalktalk/blob/master/talktalktalk.py#L75 ?

29d commented 7 years ago

You can get the IP address from request headers.

ip = request.headers.get('X-REAL-IP') or request.headers.get('HOST')

The correct header will depend on the webserver or proxy you are running behind.

How are you going to calculate the kB per minute with only the length sent? Check the length every minute or X seconds? What about something like:

from collections import deque

# on ws connect:
rate_limit[ws] = deque(maxlen=10)

# on ws message:
rate_limit[ws].append(time.time())
if len(rate_limit[ws]) = rate_limit[ws].maxlen:
    if time.time() - rate_limit[ws][0] < 1:
        break

Rate limit user messages to 10 messages a second. Drop the excess messages. Alternatively you could disconnect a user instead.

josephernest commented 7 years ago

I agree with that solution @29d. Would you do a pull request? If so, I'll merge it.

josephernest commented 7 years ago

Thanks @29d . I added your flood control method (to be merged in a few minutes).

About IP, I tested on 2 servers (Server1: Apache proxy forwarding to Python ; Server2: a VPS with direct access). Here are the results:

request.headers.get('X-REAL-IP')             #  'None' with Server1 ; 'None' with Server2
request.headers.get('HOST')                  #  localhost:9000 with Server1 ; IP the server itself with Server2 
request.environ.get('HTTP_X_FORWARDED_FOR')  #  correct client IP with Server1 ; 'None' with Server2