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 0 - Stats + sensor configs
|
||||||
DB 1 - Users
|
DB 1 - Users
|
||||||
DB 2 - Analyzer queue
|
DB 2 - Analyzer queue
|
||||||
|
DB 3 - Flask Cache
|
||||||
```
|
```
|
||||||
|
|
||||||
### DB 1
|
### DB 1
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
# -*-coding:UTF-8 -*
|
# -*-coding:UTF-8 -*
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
import redis
|
import redis
|
||||||
import bcrypt
|
import bcrypt
|
||||||
|
import random
|
||||||
|
|
||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
|
|
||||||
|
@ -44,6 +46,9 @@ class User(UserMixin):
|
||||||
if self.user_is_anonymous():
|
if self.user_is_anonymous():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
rand_sleep = random.randint(1,300)/1000
|
||||||
|
time.sleep(rand_sleep)
|
||||||
|
|
||||||
password = password.encode()
|
password = password.encode()
|
||||||
hashed_password = self.r_serv_db.hget('user:all', self.id).encode()
|
hashed_password = self.r_serv_db.hget('user:all', self.id).encode()
|
||||||
if bcrypt.checkpw(password, hashed_password):
|
if bcrypt.checkpw(password, hashed_password):
|
||||||
|
|
|
@ -87,6 +87,12 @@ redis_server_analyzer = redis.StrictRedis(
|
||||||
db=2,
|
db=2,
|
||||||
decode_responses=True)
|
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:
|
with open(json_type_description_path, 'r') as f:
|
||||||
json_type = json.loads(f.read())
|
json_type = json.loads(f.read())
|
||||||
json_type_description = {}
|
json_type_description = {}
|
||||||
|
@ -267,7 +273,6 @@ def _handle_client_error(e):
|
||||||
@app.route('/login', methods=['POST', 'GET'])
|
@app.route('/login', methods=['POST', 'GET'])
|
||||||
def login():
|
def login():
|
||||||
|
|
||||||
'''
|
|
||||||
current_ip = request.remote_addr
|
current_ip = request.remote_addr
|
||||||
login_failed_ip = r_cache.get('failed_login_ip:{}'.format(current_ip))
|
login_failed_ip = r_cache.get('failed_login_ip:{}'.format(current_ip))
|
||||||
|
|
||||||
|
@ -277,7 +282,6 @@ def login():
|
||||||
if login_failed_ip >= 5:
|
if login_failed_ip >= 5:
|
||||||
error = 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_ip:{}'.format(current_ip)))
|
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)
|
return render_template("login.html", error=error)
|
||||||
'''
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
|
@ -286,7 +290,7 @@ def login():
|
||||||
|
|
||||||
if username is not None:
|
if username is not None:
|
||||||
user = User.get(username)
|
user = User.get(username)
|
||||||
'''
|
|
||||||
login_failed_user_id = r_cache.get('failed_login_user_id:{}'.format(username))
|
login_failed_user_id = r_cache.get('failed_login_user_id:{}'.format(username))
|
||||||
# brute force by user_id
|
# brute force by user_id
|
||||||
if login_failed_user_id:
|
if login_failed_user_id:
|
||||||
|
@ -294,7 +298,6 @@ def login():
|
||||||
if login_failed_user_id >= 5:
|
if login_failed_user_id >= 5:
|
||||||
error = 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_user_id:{}'.format(username)))
|
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)
|
return render_template("login.html", error=error)
|
||||||
'''
|
|
||||||
|
|
||||||
if user and user.check_password(password):
|
if user and user.check_password(password):
|
||||||
#if not check_user_role_integrity(user.get_id()):
|
#if not check_user_role_integrity(user.get_id()):
|
||||||
|
@ -309,11 +312,10 @@ def login():
|
||||||
else:
|
else:
|
||||||
# set brute force protection
|
# set brute force protection
|
||||||
#logger.warning("Login failed, ip={}, username={}".format(current_ip, username))
|
#logger.warning("Login failed, ip={}, username={}".format(current_ip, username))
|
||||||
#r_cache.incr('failed_login_ip:{}'.format(current_ip))
|
r_cache.incr('failed_login_ip:{}'.format(current_ip))
|
||||||
#r_cache.expire('failed_login_ip:{}'.format(current_ip), 300)
|
r_cache.expire('failed_login_ip:{}'.format(current_ip), 300)
|
||||||
#r_cache.incr('failed_login_user_id:{}'.format(username))
|
r_cache.incr('failed_login_user_id:{}'.format(username))
|
||||||
#r_cache.expire('failed_login_user_id:{}'.format(username), 300)
|
r_cache.expire('failed_login_user_id:{}'.format(username), 300)
|
||||||
#
|
|
||||||
|
|
||||||
error = 'Password Incorrect'
|
error = 'Password Incorrect'
|
||||||
return render_template("login.html", error=error)
|
return render_template("login.html", error=error)
|
||||||
|
|
|
@ -8,9 +8,11 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
import json
|
import json
|
||||||
import redis
|
import redis
|
||||||
|
import random
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response
|
from flask import Flask, render_template, jsonify, request, Blueprint, redirect, url_for, Response
|
||||||
|
@ -42,6 +44,12 @@ r_serv_db = redis.StrictRedis(
|
||||||
db=1,
|
db=1,
|
||||||
decode_responses=True)
|
decode_responses=True)
|
||||||
|
|
||||||
|
r_cache = redis.StrictRedis(
|
||||||
|
host=host_redis_metadata,
|
||||||
|
port=port_redis_metadata,
|
||||||
|
db=3,
|
||||||
|
decode_responses=True)
|
||||||
|
|
||||||
# ============ AUTH FUNCTIONS ============
|
# ============ AUTH FUNCTIONS ============
|
||||||
|
|
||||||
def check_token_format(strg, search=re.compile(r'[^a-zA-Z0-9_-]').search):
|
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):
|
if not check_token_format(token):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
rand_sleep = random.randint(1,300)/1000
|
||||||
|
time.sleep(rand_sleep)
|
||||||
if r_serv_db.hexists('user:tokens', token):
|
if r_serv_db.hexists('user:tokens', token):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
@ -104,7 +114,6 @@ def authErrors(user_role):
|
||||||
data = None
|
data = None
|
||||||
# verify token format
|
# verify token format
|
||||||
|
|
||||||
'''
|
|
||||||
# brute force protection
|
# brute force protection
|
||||||
current_ip = request.remote_addr
|
current_ip = request.remote_addr
|
||||||
login_failed_ip = r_cache.get('failed_login_ip_api:{}'.format(current_ip))
|
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)
|
login_failed_ip = int(login_failed_ip)
|
||||||
if login_failed_ip >= 5:
|
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)
|
return ({'status': 'error', 'reason': 'Max Connection Attempts reached, Please wait {}s'.format(r_cache.ttl('failed_login_ip_api:{}'.format(current_ip)))}, 401)
|
||||||
'''
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
authenticated = False
|
authenticated = False
|
||||||
|
@ -125,8 +133,8 @@ def authErrors(user_role):
|
||||||
data = ({'status': 'error', 'reason': 'Access Forbidden'}, 403)
|
data = ({'status': 'error', 'reason': 'Access Forbidden'}, 403)
|
||||||
|
|
||||||
if not authenticated:
|
if not authenticated:
|
||||||
#r_cache.incr('failed_login_ip_api:{}'.format(current_ip))
|
r_cache.incr('failed_login_ip_api:{}'.format(current_ip))
|
||||||
#r_cache.expire('failed_login_ip_api:{}'.format(current_ip), 300)
|
r_cache.expire('failed_login_ip_api:{}'.format(current_ip), 300)
|
||||||
data = ({'status': 'error', 'reason': 'Authentication failed'}, 401)
|
data = ({'status': 'error', 'reason': 'Authentication failed'}, 401)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
|
@ -45,11 +45,7 @@ def one():
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def check_email(email):
|
def check_email(email):
|
||||||
result = email_regex.match(email)
|
return email_regex.match(email)
|
||||||
if result:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_user_metadata(user_id):
|
def get_user_metadata(user_id):
|
||||||
user_metadata = {}
|
user_metadata = {}
|
||||||
|
|
Loading…
Reference in New Issue