Реализована система маскировки чувствительных данных в логах для предотвращения утечки паролей и токенов в файлы логов.
def mask_sensitive_data(data: dict) -> dict:def mask_csv_line(line: str, headers: list) -> str:def mask_csv_line_safe(line: str) -> str:data(dict): Исходный словарь с данными
line(str): CSV строка для маскировкиheaders(list): Список заголовков CSV файла
line(str): CSV строка для маскировки
dict: Копия словаря с замаскированными чувствительными полями (mask_sensitive_data)str: CSV строка с замаскированными чувствительными полями (mask_csv_line, mask_csv_line_safe)
Функция mask_csv_line() полагается на соответствие количества полей в строке количеству заголовков. Если это условие не выполняется, чувствительные данные могут попасть в логи.
mask_csv_line()- при несоответствии полей маскирует ВСЕ поля для безопасностиmask_csv_line_safe()- использует регулярные выражения для поиска потенциальных паролей и токенов независимо от структуры CSV
Проблема: Первоначальная версия mask_csv_line_safe() могла захватывать части соседних полей при использовании регулярных выражений.
Исправление: Функция теперь:
- Разделяет CSV строку на отдельные поля по разделителю
; - Анализирует каждое поле индивидуально
- Применяет регулярные выражения только к целым полям
- Исключает возможность захвата соседних полей
password- пароли пользователейoauth_token- OAuth токеныaccess_token- токены доступаtoken- общие токены
- Пароли: минимум 8 символов + заглавная буква + цифра + спецсимвол
- Исключает email адреса из маскировки
- Исправлено: Анализирует каждое поле отдельно, не захватывает соседние поля
- Глубокая копия: Создается полная копия исходных данных
- Рекурсивный поиск: Функция проходит по всем уровням вложенности
- Маскировка: Чувствительные поля заменяются на
***MASKED*** - Безопасность: Исходные данные остаются неизменными
# БЫЛО (небезопасно):
logger.debug(f"POST DATA: {user}")
# СТАЛО (безопасно):
logger.debug(f"POST DATA: {mask_sensitive_data(user)}")# БЫЛО (небезопасно):
logger.debug(f'Обработка строки #{line_number} {element}')
# СТАЛО (безопасно):
logger.debug(f'Обработка строки #{line_number} {mask_sensitive_data(element)}')# БЫЛО (небезопасно):
logger.debug(f"PATCH DATA: {patch_data}")
# СТАЛО (безопасно):
logger.debug(f"PATCH DATA: {mask_sensitive_data(patch_data)}")# БЫЛО (небезопасно):
logger.debug(f'Чтение строки из файла - {line}')
logger.error(f'Ошибка! Строка {line} - количество полей не соответствует')
# СТАЛО (безопасно - используем mask_csv_line_safe):
logger.debug(f'Чтение строки из файла - {mask_csv_line_safe(line)}')
logger.error(f'Ошибка! Строка {mask_csv_line_safe(line)} - количество полей не соответствует')
# Альтернативно (с заголовками, но менее безопасно):
logger.debug(f'Чтение строки из файла - {mask_csv_line(line, headers)}')
logger.error(f'Ошибка! Строка {mask_csv_line(line, headers)} - количество полей не соответствует')user_data = {
"name": "John Doe",
"password": "SecretPassword123!",
"email": "john@example.com",
"oauth_token": "ya29.a0AfH6SMC...",
"contacts": [
{
"type": "phone",
"value": "+1234567890"
}
]
}masked_data = {
"name": "John Doe",
"password": "***MASKED***",
"email": "john@example.com",
"oauth_token": "***MASKED***",
"contacts": [
{
"type": "phone",
"value": "+1234567890"
}
]
}user1;SecretPass123!;John;Doe;john@example.com;abc123
user1;***MASKED***;John;Doe;john@example.com;***MASKED***
user1;SecretPass123!;John;Doe;john@example.com
user1;***MASKED***;John;Doe;john@example.com
Ключевое отличие: Функция теперь анализирует каждое поле отдельно, не захватывая соседние поля.
^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};:"\\|,.<>\/?])[A-Za-z0-9!@#$%^&*()_+\-=\[\]{};:"\\|,.<>\/?]{8,}$- Минимум 8 символов
- Минимум одна заглавная буква
- Минимум одна цифра
- Минимум один спецсимвол
Password123! → ***MASKED*** (валидный пароль)
password123 → password123 (нет заглавной буквы)
PASSWORD123 → PASSWORD123 (нет спецсимвола)
PasswordABC → PasswordABC (нет цифр)
Pass123! → ***MASKED*** (короткий но валидный)
- Пароли пользователей
- OAuth токены
- Токены доступа
- Любые поля с названием "token"
- Имена пользователей
- Email адреса
- Номера телефонов
- Другие нечувствительные данные
Для тестирования функции создан файл test_password_masking.py:
python test_password_masking.pyТест проверяет:
- Маскировку различных типов чувствительных данных
- Рекурсивную обработку вложенных структур
- Корректность работы с массивами
- Сохранение нечувствительных данных
add_modify_users.py- функцияmask_sensitive_data()и интеграцияtest_password_masking.py- тесты для проверки функцииPASSWORD_MASKING_README.md- данная документация
Функция автоматически применяется во всех местах логирования данных пользователей:
- При создании пользователей - маскирует пароли в POST запросах
- При обработке CSV - маскирует пароли в данных из файла
- При обновлении пользователей - маскирует пароли в PATCH запросах
Теперь в логах вместо реальных паролей будет отображаться:
POST DATA: {'name': {'first': 'John', 'last': 'Doe'}, 'password': '***MASKED***', 'nickname': 'johndoe'}
Это обеспечивает:
- Безопасность логов
- Соответствие требованиям GDPR
- Защиту от утечек данных
- Возможность отладки без раскрытия чувствительной информации