feat(security): implement AES-GCM for password encryption
Replaces the `pyeasyencrypt` library with a more robust and standard encryption implementation using `cryptography.hazmat`. This commit introduces AES-256-GCM for encrypting and decrypting user account passwords. The `add_account` endpoint now properly encrypts passwords before database insertion. Error handling has been added to the `get_user_accounts` endpoint to manage decryption failures for legacy passwords, which will be returned as "DECRYPTION_FAILED". BREAKING CHANGE: The password encryption algorithm has been changed. All previously stored passwords are now invalid and cannot be decrypted.
This commit is contained in:
parent
4352004ed3
commit
79a2f6e944
@ -3,7 +3,7 @@ import mysql.connector
|
||||
from dotenv import load_dotenv
|
||||
from flask import jsonify, request
|
||||
from ktvmanager.lib.checker import single_account_check
|
||||
from ktvmanager.lib.encryption import decrypt_password
|
||||
from ktvmanager.lib.encryption import encrypt_password, decrypt_password
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@ -42,7 +42,12 @@ def get_user_accounts(user_id):
|
||||
query = "SELECT * FROM userAccounts WHERE userID = %s"
|
||||
accounts = _execute_query(query, (user_id,))
|
||||
for account in accounts:
|
||||
try:
|
||||
account['password'] = decrypt_password(account['password'])
|
||||
except Exception as e:
|
||||
# Log the error to the console for debugging
|
||||
print(f"Password decryption failed for account ID {account.get('id', 'N/A')}: {e}")
|
||||
account['password'] = "DECRYPTION_FAILED"
|
||||
return jsonify(accounts)
|
||||
|
||||
def get_stream_names():
|
||||
@ -64,8 +69,9 @@ def single_check():
|
||||
|
||||
def add_account():
|
||||
data = request.get_json()
|
||||
encrypted_password = encrypt_password(data['password'])
|
||||
query = "INSERT INTO userAccounts (username, stream, streamURL, expiaryDate, password, userID) VALUES (%s, %s, %s, %s, %s, %s)"
|
||||
params = (data['username'], data['stream'], data['streamURL'], data['expiaryDate'], data['password'], data['userID'])
|
||||
params = (data['username'], data['stream'], data['streamURL'], data['expiaryDate'], encrypted_password, data['userID'])
|
||||
result = _execute_query(query, params)
|
||||
return jsonify(result)
|
||||
|
||||
|
@ -1,17 +1,30 @@
|
||||
from pyeasyencrypt.pyeasyencrypt import encrypt_string, decrypt_string
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
import hashlib
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
|
||||
SECRET = "BBLBTV-DNS-PASSWORDS"
|
||||
KEY = hashlib.sha256(SECRET.encode()).digest()
|
||||
ALGORITHM = "aes-256-gcm"
|
||||
IV_LENGTH = 16
|
||||
AUTH_TAG_LENGTH = 16
|
||||
|
||||
def encrypt_password(clear_string):
|
||||
password = os.getenv("ENCRYPTKEY")
|
||||
encrypted_string = encrypt_string(clear_string, password)
|
||||
return encrypted_string
|
||||
iv = os.urandom(IV_LENGTH)
|
||||
aesgcm = AESGCM(KEY)
|
||||
|
||||
ciphertext_and_tag = aesgcm.encrypt(iv, clear_string.encode(), None)
|
||||
ciphertext = ciphertext_and_tag[:-AUTH_TAG_LENGTH]
|
||||
tag = ciphertext_and_tag[-AUTH_TAG_LENGTH:]
|
||||
|
||||
return (iv + tag + ciphertext).hex()
|
||||
|
||||
def decrypt_password(encrypted_string):
|
||||
password = os.getenv("ENCRYPTKEY")
|
||||
decrypted_string = decrypt_string(encrypted_string, password)
|
||||
return decrypted_string
|
||||
data = bytes.fromhex(encrypted_string)
|
||||
|
||||
iv = data[:IV_LENGTH]
|
||||
tag = data[IV_LENGTH:IV_LENGTH + AUTH_TAG_LENGTH]
|
||||
ciphertext = data[IV_LENGTH + AUTH_TAG_LENGTH:]
|
||||
|
||||
aesgcm = AESGCM(KEY)
|
||||
decrypted_bytes = aesgcm.decrypt(iv, ciphertext + tag, None)
|
||||
return decrypted_bytes.decode()
|
||||
|
Loading…
x
Reference in New Issue
Block a user