fix: [Flask auth] add brute force and side-channel protection

pull/24/head
Terrtia 2019-09-03 10:43:52 +02:00
parent f6b6137937
commit 96cfebd0ea
No known key found for this signature in database
GPG Key ID: 1E1B1F50D84613D0
5 changed files with 30 additions and 18 deletions

View File

@ -13,6 +13,7 @@ sensor registrations, management of decoding protocols and dispatching to adequa
DB 0 - Stats + sensor configs
DB 1 - Users
DB 2 - Analyzer queue
DB 3 - Flask Cache
```
### DB 1

View File

@ -2,8 +2,10 @@
# -*-coding:UTF-8 -*
import os
import time
import redis
import bcrypt
import random
from flask_login import UserMixin
@ -44,6 +46,9 @@ class User(UserMixin):
if self.user_is_anonymous():
return False
rand_sleep = random.randint(1,300)/1000
time.sleep(rand_sleep)
password = password.encode()
hashed_password = self.r_serv_db.hget('user:all', self.id).encode()
if bcrypt.checkpw(password, hashed_password):

View File

@ -87,6 +87,12 @@ redis_server_analyzer = redis.StrictRedis(
db=2,
decode_responses=True)
r_cache = redis.StrictRedis(
host=host_redis_metadata,
port=port_redis_metadata,
db=3,
decode_responses=True)
with open(json_type_description_path, 'r') as f:
json_type = json.loads(f.read())
json_type_description = {}
@ -267,7 +273,6 @@ def _handle_client_error(e):
@app.route('/login', methods=['POST', 'GET'])
def login():
'''
current_ip = request.remote_addr
login_failed_ip = r_cache.get('failed_login_ip:{}'.format(current_ip))
@ -277,7 +282,6 @@ def login():
if login_failed_ip >= 5:
error = 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_ip:{}'.format(current_ip)))
return render_template("login.html", error=error)
'''
if request.method == 'POST':
username = request.form.get('username')
@ -286,7 +290,7 @@ def login():
if username is not None:
user = User.get(username)
'''
login_failed_user_id = r_cache.get('failed_login_user_id:{}'.format(username))
# brute force by user_id
if login_failed_user_id:
@ -294,7 +298,6 @@ def login():
if login_failed_user_id >= 5:
error = 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_user_id:{}'.format(username)))
return render_template("login.html", error=error)
'''
if user and user.check_password(password):
#if not check_user_role_integrity(user.get_id()):
@ -309,11 +312,10 @@ def login():
else:
# set brute force protection
#logger.warning("Login failed, ip={}, username={}".format(current_ip, username))
#r_cache.incr('failed_login_ip:{}'.format(current_ip))
#r_cache.expire('failed_login_ip:{}'.format(current_ip), 300)
#r_cache.incr('failed_login_user_id:{}'.format(username))
#r_cache.expire('failed_login_user_id:{}'.format(username), 300)
#
r_cache.incr('failed_login_ip:{}'.format(current_ip))
r_cache.expire('failed_login_ip:{}'.format(current_ip), 300)
r_cache.incr('failed_login_user_id:{}'.format(username))
r_cache.expire('failed_login_user_id:{}'.format(username), 300)
error = 'Password Incorrect'
return render_template("login.html", error=error)

View File

@ -8,9 +8,11 @@
import os
import re
import sys
import time
import uuid
import json
import redis
import random
import datetime
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response
@ -42,6 +44,12 @@ r_serv_db = redis.StrictRedis(
db=1,
decode_responses=True)
r_cache = redis.StrictRedis(
host=host_redis_metadata,
port=port_redis_metadata,
db=3,
decode_responses=True)
# ============ AUTH FUNCTIONS ============
def check_token_format(strg, search=re.compile(r'[^a-zA-Z0-9_-]').search):
@ -54,6 +62,8 @@ def verify_token(token):
if not check_token_format(token):
return False
rand_sleep = random.randint(1,300)/1000
time.sleep(rand_sleep)
if r_serv_db.hexists('user:tokens', token):
return True
else:
@ -104,7 +114,6 @@ def authErrors(user_role):
data = None
# verify token format
'''
# brute force protection
current_ip = request.remote_addr
login_failed_ip = r_cache.get('failed_login_ip_api:{}'.format(current_ip))
@ -113,7 +122,6 @@ def authErrors(user_role):
login_failed_ip = int(login_failed_ip)
if login_failed_ip >= 5:
return ({'status': 'error', 'reason': 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_ip_api:{}'.format(current_ip)))}, 401)
'''
try:
authenticated = False
@ -125,8 +133,8 @@ def authErrors(user_role):
data = ({'status': 'error', 'reason': 'Access Forbidden'}, 403)
if not authenticated:
#r_cache.incr('failed_login_ip_api:{}'.format(current_ip))
#r_cache.expire('failed_login_ip_api:{}'.format(current_ip), 300)
r_cache.incr('failed_login_ip_api:{}'.format(current_ip))
r_cache.expire('failed_login_ip_api:{}'.format(current_ip), 300)
data = ({'status': 'error', 'reason': 'Authentication failed'}, 401)
except Exception as e:
print(e)

View File

@ -45,11 +45,7 @@ def one():
return 1
def check_email(email):
result = email_regex.match(email)
if result:
return True
else:
return False
return email_regex.match(email)
def get_user_metadata(user_id):
user_metadata = {}