惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
Security Latest
Security Latest
S
Secure Thoughts
H
Heimdal Security Blog
The Last Watchdog
The Last Watchdog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
S
Security Affairs
PCI Perspectives
PCI Perspectives
C
CERT Recently Published Vulnerability Notes
C
CXSECURITY Database RSS Feed - CXSecurity.com
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
有赞技术团队
有赞技术团队
爱范儿
爱范儿
腾讯CDC
P
Privacy & Cybersecurity Law Blog
量子位
T
Threat Research - Cisco Blogs
V
V2EX
S
Schneier on Security
P
Proofpoint News Feed
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
Forbes - Security
Forbes - Security
The Hacker News
The Hacker News
月光博客
月光博客
C
Cybersecurity and Infrastructure Security Agency CISA
Schneier on Security
Schneier on Security
L
LINUX DO - 最新话题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Visual Studio Blog
S
SegmentFault 最新的问题
G
GRAHAM CLULEY
人人都是产品经理
人人都是产品经理
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 叶小钗
罗磊的独立博客
WordPress大学
WordPress大学
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
Cisco Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tenable Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
W
WeLiveSecurity
V
Vulnerabilities – Threatpost
Last Week in AI
Last Week in AI
Google DeepMind News
Google DeepMind News
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Менеджер паролей на python
Cheef44 · 2026-05-18 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение6 мин

Охват и читатели0

Предисловие

Это не полноценный гайд по созданию менеджера паролей и не рекомендация использовать мой подход в production. Скорее, это разбор моего опыта разработки локального менеджера паролей на Python и PySide6: какие решения я выбрал, с какими проблемами столкнулся и что понял по ходу работы.

Проект некоммерческий

Начало начал

В 2024 году я начал разрабатывать свой мессенджер на QT, но через примерно год я встал в тупик и решил на примере не сложного проекта разобраться в QT и попробовать нормально построить архитектуру. Основная цель проекта была не в создании идеального менеджера паролей, а в тренировке архитектуры и работы с PySide6.

Технологии

В качестве языка я выбрал Python, так как на нем пишу уже 7 лет и считаю что почти все можно написать на нем, надо только уметь.

В качестве интерфейса был выбран QT через библиотеку pyside6. С помощью QT можно сделать красивый интерфейс и пример этом — телеграм.

Я выбрал гибридную схему: данные шифруются симметричным ключом, а сам симметричный ключ шифруется RSA. Такой подход удобнее, чем шифровать все данные напрямую RSA, потому что асимметричное шифрование плохо подходит для больших объемов данных.

Однако в моей первой реализации есть спорные места. Например, используется RSA-1024, что сейчас уже нельзя считать хорошим выбором. В дальнейшем я бы заменил это минимум на RSA-2048/3072 или пересмотрел схему в сторону более современной криптографии.

    #Метод генерации синхронного ключа
    def gen_sync_key(self):
        with open(self.sync_key_path, 'wb') as key:
            key.write(get_random_bytes(32))
    
    #Метод генерации асинхронного ключа
    def gen_async_key(self):
        keys = RSA.generate(1024)
        with open(self.async_public_key_path, 'wb') as key_pub:
            key_pub.write(keys.public_key().export_key())
        with open(self.async_private_key_path, 'wb') as key_private:
            key_private.write(keys.export_key(format='PEM', passphrase=self.password, protection='PBKDF2WithHMAC-SHA512AndAES256-CBC', prot_params={'iteration_count':131072}))
    
    #Метод шифрования синхронного ключа с помощью асинхронного шифрования
    def synchronous_key_encryption(self):
        with open(self.sync_key_path, 'rb') as sync_key:
            sync_key = sync_key.read()
        with open(self.async_public_key_path, 'rb') as async_key:
            async_key = RSA.import_key(async_key.read(), self.password)
        
        encrypt = PKCS1_OAEP.new(async_key)
        encrypt = encrypt.encrypt(sync_key)
        with open(self.sync_key_path, 'wb') as sync_key:
            sync_key.write(encrypt)

Все остальное шифрование строится так:

открыть зашифрованный синхронный ключ --> расшифровать приватным ключом RSA --> зашифровать данные.

Так как все данные хранятся локально, в качестве СУБД был использован sqlite3. Все данные для входа пользователя хранятся в виде хэшэй с солью. Записи паролей состоят из несколько полей: имя записи, имя сайта/сервиса, логин, почта, пароль. В первой версии я шифровал только почту и пароль. Сейчас это кажется спорным решением: название сервиса и логин тоже могут раскрывать чувствительную информацию о пользователе. Поэтому в следующих версиях логичнее шифровать всю запись целиком, кроме технических идентификаторов.

#Генерация соли и добавление соли к данным пользователя
    def sault_func(self, user_name:str, user_password:str, sault:bytes=get_random_bytes(32)):
        user_name = bytes(user_name, encoding="utf-8")
        user_password = bytes(user_password, encoding="utf-8")
        sault_user_name = sault[:len(sault)//2]+user_name+sault[len(sault)//2:]
        sault_user_password = sault[:len(sault)//2]+user_password+sault[len(sault)//2:]
        
        return {"sault": sault, "sault_user_name": sault_user_name, "sault_user_password": sault_user_password}
      

Помимо этого ключи RSA имеют пароль который пользователь задает при входе в систему. Одно из самых спорных решений — временное хранение пользовательского пароля через keyring. С точки зрения удобства это помогало не спрашивать пароль повторно, но с точки зрения модели угроз решение выглядит сомнительно. Сейчас я бы лучше хранил не сам пароль, а производный ключ, полученный через KDF, и очищал бы его из памяти после завершения работы.

class CachePassword:
    def __init__(self, password=None, name_service=None, time_del_pwd=0):
        self.PASSWORD = password
        self.NAME_SERVICE = name_service
        self.time_del_pwd = time_del_pwd
        self.user_name = socket.gethostname()
    
    #Метод помещения данных в кэш
    def set_password(self):
        keyring.set_password(self.NAME_SERVICE, self.user_name, self.PASSWORD)
    
    #Метод выдачи данных из кэша
    def get_password(self):
        return keyring.get_password(self.NAME_SERVICE, self.user_name)
    
    #Метод таймера удаления данных из кэша
    def del_timer(self):
        timer = threading.Timer(self.time_del_pwd, self.del_password)
        timer.daemon = True
        timer.start()
    
    #Метод удаления данных из кэша
    def del_password(self):
        keyring.delete_password(self.NAME_SERVICE, self.user_name)

Функционал

Из базовых функций уже понятно что: шифрование, реализация регистрации и входа в систему, удобное расположение записей

Интерфейс программы

Интерфейс программы

Поговорим про хоть какое-то удобство.

Под кнопкой «+» у нас скрывается добавление записей и функция генерации пароля. Генератор не претендует на идеальность, но позволяет создавать пароли, которые обычно лучше ручных пользовательских вариантов.

Интерфейс добавления записи

Интерфейс добавления записи

#Класс генирации паролей
class GenerationPassword:
    def __init__(self, len_password:int):
        self.len_password = len_password
        self.chars_number = ''.join(string.printable.split())
    
    #Метод валидации пароля
    def validation(self, user_password):
        if not bool(re.search("[a-z]", user_password)):
            return False
        if not bool(re.search("[A-Z]", user_password)):
            return False
        if not bool(re.search("[0-9]", user_password)):
            return False
        if not bool(re.search("["+ re.escape(string.punctuation)+ "]", user_password)):
            return False
        
        return True
    
    #Метод генирации пароля
    def generation_password(self):
        while True:
            password = ''.join([secrets.choice(self.chars_number) for _ in range(self.len_password)])
            if self.validation(user_password=password):
                return password

Под стрелкой направленной вниз — импорт паролей из csv файла. Можно импортировать пароли из браузера:

Импорт из csv

Импорт из csv

Соответственно стрелка вверх — экспорт в csv. Можно использовать, если хотите перенести все в браузер или хотите перенести пароли на новую систему.

Экспорт в csv

Экспорт в csv

Также есть поиск по ключевым словам и функция удаления записи.

Есть также функция ответственная за проверку вводимого пароля при регистрации. Она легкая, но от нее больше и не требуется. Была мысль освободить пользователя от обязательности ввода «безопасного» пароля, но было решено добавить. Если кто‑то решит что это лишнее для него, может смело вырезать.

#Проверка валидации данных
    def validation(self):
        if not self.user_data or self.user_name in self.user_password:
            return False
        if not len(self.user_password) >= 8:
            return False
        if bool(re.search("[ ]", self.user_password)):
            return False
        if bool(re.search("[а-яА-ёЁ]", self.user_data)):
            return False
        if not bool(re.search("[a-z]", self.user_password)):
            return False
        if not bool(re.search("[A-Z]", self.user_password)):
            return False
        if not bool(re.search("[0-9]", self.user_password)):
            return False
        if not bool(re.search("[!-_#@%$*?/|&)}{<>+='(,)^:;№%*.]", self.user_password)):
            return False
        
        return True

Также есть функция удаления записи)

Сложности с которыми столкнулся

Большинство сложностей были касаемо работы с интерфейсом, но это все мелочи которые никак не влияют на функционал.

Самая большая проблема была — лагает интерфейс, если слишком много записей. Выглядит сложно, но решилась просто. Проблема была в том что каждый раз когда рендерился интерфес (каждое прокручивание или расширение окна), все данные расшифровывались заново. Решил я это тем, что просто сохранял таблицу в переменную на время работы программы.

Заключение

Все это начиналось как обкадка интерфейса для переписывания мессенджера на новой архитектуре и с уверенной работой с QT, но с этого проекта у меня появилась идея написания ПО которое будет безопасным и открытым.

В планах еще много что написать, так как многие сервисы в россии нормально не работают, а их альтернативами пользоваться никто не хочет. Исходный код проекта открыт. Ссылку на репозиторий оставлю в конце статьи: буду рад технической критике, замечаниям по архитектуре и предложениям по безопасности. Пока деньги не собираю, поэтому просто подписывайтесь на тгк, github и заходите на мой сайт, если конечно этого хотите.

Если есть что‑то что вам интересно, но я про это не рассказал, то задавайте вопросы.