mirror of https://github.com/D4-project/d4-core
fix: [Flask auth] add brute force and side-channel protection
parent
f6b6137937
commit
96cfebd0ea
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = {}
|
||||
|
|
Loading…
Reference in New Issue