ユーザーが入力したパスワードをメモリに保存し、不要になったパスワードを安全に消去するにはどうすればよいですか?
詳細に説明すると、現在、次のコードがあります。
username = raw_input('User name: ')
password = getpass.getpass()
mail = imaplib.IMAP4(MAIL_Host)
mail.login(username, password)
login
メソッドを呼び出した後、誰かがコアダンプを実行してパスワードを回復できないように、パスワードを含むメモリ領域を文字化けした文字で埋めるために何をする必要がありますか?
同様の質問がありますが、Javaであり、ソリューションは文字配列を使用します: アカウントを作成するときに、パスワードハッシュをメモリに安全に保存するにはどうすればよいですか?
これはPythonで実行できますか?
Pythonには、メモリに対する制御レベルがそれほど低くありません。それを受け入れて、次に進みます。 best実行できるのは、del password
を呼び出した後にmail.login
を実行して、パスワード文字列オブジェクトへの参照が残らないようにすることです。それ以上のことができると主張する解決策は、あなたに誤った安心感を与えるだけです。
Python文字列オブジェクトは不変です。文字列の作成後に文字列の内容を直接変更する方法はありません。 たとえpassword
によって参照される文字列の内容をなんとかして上書きすることができたとしても(これは愚かなctypesトリックで技術的に可能です) 、さまざまな文字列操作で作成されたパスワードの他のコピーがまだあります。
どういうわけか、これらすべての文字列への参照を取得し、それらのメモリも上書きする必要があります。
実際には、Pythonで文字列を安全に消去する方法があります。 Pythonで機密としてデータをマークする のようにmemsetC関数を使用します
投稿が行われてからずっと後に、追加するように編集されました: 文字列のインターンについて詳しく説明します 。 CPython参照カウントGCに基づいて、インターンが発生せず、文字列値のクリーンアップが少し明確になる状況がいくつかあります(主に非定数文字列が関係します)。 (まだ「スクラブ」/「サニタイズ」クリーンアップではありませんが。)
メールオブジェクトを使い終わったら永続化する必要がない場合は、サブプロセスでメール送信作業を実行するのが最善の策だと思います( subprocess モジュールを参照)。サブプロセスが停止すると、パスワードも停止します。
正しい解決策は、可変であるbytearray()...を使用することです。これにより、RAMからキーと機密情報を安全にクリアできます。
ただし、いくつかのライブラリ、特に「bytearray」の使用を妨げるpython "cryptography"ライブラリ)があります。これには問題があります...ある程度、これらの暗号ライブラリはのみ可変タイプがキーマテリアルに使用されます。
メモリからキーを完全に削除できるpipモジュールであるSecureStringがあります...(私はそれを少しリファクタリングしてSecureBytesと呼びました)。キーが完全に削除されていることを示す単体テストをいくつか作成しました。
ただし、大きな注意点があります。誰かのパスワードが「type」の場合、「type」という単語は、関数定義やオブジェクト属性を含むすべてのPythonから消去されます。
言い換えれば...不変の型を変更することはひどい考えであり、特に注意しない限り、実行中のプログラムをすぐにクラッシュさせる可能性があります。
適切な解決策...キーマテリアルやパスワードなどに不変の型を使用しないでください。「getpass」のような暗号化ライブラリまたはルーチンを構築する人は、python strings」ではなく「bytearray」を使用する必要があります。 。
これは、numpychararrayを使用して実行できます。
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[:] = ''
パスワードの最大サイズを決定する必要がありますが、これにより、データが上書きされたときにデータが削除されます。
ここで:以下は、変数のメモリアドレスバイトをゼロに置き換えてから、メモリ位置へのポインタを逆参照します。
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.