Безопасное стирание пароля в памяти (Python)

Как вы храните пароль, введенный пользователем в память, и надежно стираете его после того, как он больше не нужен?

Чтобы уточнить, в настоящее время мы имеем следующий код:

username = raw_input('User name: ')
password = getpass.getpass()
mail = imaplib.IMAP4(MAIL_HOST)
mail.login(username, password)

После вызова метода login, что нам нужно сделать, чтобы заполнить область памяти, содержащую пароль с искаженными символами, чтобы кто-то не смог восстановить пароль, выполнив дамп ядра?

Есть аналогичный вопрос, однако он находится в Java, и решение использует массивы символов: Как безопасно хранить хэширование пароля в памяти при создании аккаунтов?

Можно ли это сделать в Python?

Ответ 1

Python не обладает таким низким уровнем контроля над памятью. Примите это и продолжайте. лучший, который вы можете сделать, это del password после вызова mail.login, чтобы ссылки на объект строки пароля не сохранялись. Любое решение, которое претендует на возможность сделать больше, это лишь дает вам ложное чувство безопасности.

Строковые объекты Python неизменяемы; нет прямого способа изменить содержимое строки после ее создания. Даже если вы каким-то образом перезаписали содержимое строки, на которую ссылается password (что технически возможно с помощью глупых трюков ctypes), все равно будут другие копии пароля, которые были созданы в различных строковых операциях:

  • с помощью модуля getpass, когда он удаляет завершающую новую строку с введенного пароля
  • модулем imaplib, когда он цитирует пароль, а затем создает полную команду IMAP перед передачей ее в сокет

Вам нужно было бы получить ссылки на все эти строки и перезаписать их память.

Ответ 2

На самом деле - это способ безопасного удаления строк в Python; используйте функцию memset C согласно Пометить данные как чувствительные в python

Отредактировано, чтобы добавить, спустя долгое время после того, как сообщение было сделано: здесь более глубокое погружение в интернирование строки. Существуют некоторые обстоятельства (в основном связанные с неконстантными строками), когда интернирование не происходит, что делает очистку строкового значения несколько более явной, основываясь на подсчете ссылок CPython GC. (Хотя это еще не "очистка"/"очистка").

Ответ 3

Если вам не нужно, чтобы почтовый объект сохранялся, как только вы закончите с ним, я думаю, что лучше всего выполнить работу по рассылке в подпроцессе (см. subprocess.) Таким образом, когда подпроцесс умирает, так же ваш пароль.

Ответ 4

Это можно сделать с помощью numpy chararray:

import numpy as np

username = raw_input('User name: ')
mail = imaplib.IMAP4(MAIL_HOST)
x = np.chararray((20,))
x[:] = list("{:<20}".format(raw_input('Password: ')))
mail.login(username, x.tobytes().strip())
x[:] = ''

Вам нужно будет определить максимальный размер пароля, но это должно удалить данные, когда они будут перезаписаны.

Ответ 5

Правильным решением является использование bytearray()..., который является изменяемым, и вы можете безопасно удалять ключи и чувствительный материал из оперативной памяти.

Тем не менее, есть некоторые библиотеки, в частности, библиотека "cryptography" python, которая не позволяет использовать "bytearray". Это проблематично... в некоторой степени эти криптографические библиотеки должны обеспечивать использование только изменяемых типов для ключевого материала.

Существует SecureString, представляющий собой пип-модуль, который позволяет полностью удалить ключ из памяти... (я немного изменил его рефакторинг и назвал его SecureBytes). Я написал несколько модульных тестов, которые показывают, что ключ полностью удален.

Но есть большая оговорка: если у кого-то пароль "type", то слово "type" будет стерто со всего python... в том числе в определениях функций и атрибутах объектов.

Другими словами... мутирование неизменяемых типов - ужасная идея, и если вы не будете очень осторожны, вы можете немедленно завершить работу любой работающей программы.

Правильное решение... никогда не используйте неизменяемые типы для ключевого материала, паролей и т.д. Любой, кто создает криптографическую библиотеку или подпрограмму, такую как "getpass", должен работать с "bytearray" вместо строк python.

Ответ 6

Здесь: следующее заменяет байты адреса памяти переменной нулями, а затем разыменовывает указатель на ячейку памяти.

Протестировано на системах на основе Debian.

import sys 
import ctypes

def nuke(var_to_nuke):
    strlen = len(var_to_nuke)
    offset = sys.getsizeof(var_to_nuke) - strlen - 1
    ctypes.memset(id(var_to_nuke) + offset, 0, strlen)
    del var_to_nuke               # derefrencing the pointer.

Ответ 7

ИЗМЕНИТЬ: удалил плохой совет...

Вы также можете использовать массивы, например, пример java, но просто перезаписать его должно быть достаточно.

http://docs.python.org/library/array.html

Ответ 8

Сохраните пароль в списке, и если вы просто установите список в нуль, память массива, хранящегося в списке, будет автоматически освобождена.