web-dev-qa-db-ja.com

メモリ内のパスワードを安全に消去する(Python)

ユーザーが入力したパスワードをメモリに保存し、不要になったパスワードを安全に消去するにはどうすればよいですか?

詳細に説明すると、現在、次のコードがあります。

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

loginメソッドを呼び出した後、誰かがコアダンプを実行してパスワードを回復できないように、パスワードを含むメモリ領域を文字化けした文字で埋めるために何をする必要がありますか?

同様の質問がありますが、Javaであり、ソリューションは文字配列を使用します: アカウントを作成するときに、パスワードハッシュをメモリに安全に保存するにはどうすればよいですか?

これはPythonで実行できますか?

44
maxyfc

Pythonには、メモリに対する制御レベルがそれほど低くありません。それを受け入れて、次に進みます。 best実行できるのは、del passwordを呼び出した後にmail.loginを実行して、パスワード文字列オブジェクトへの参照が残らないようにすることです。それ以上のことができると主張する解決策は、あなたに誤った安心感を与えるだけです。

Python文字列オブジェクトは不変です。文字列の作成後に文字列の内容を直接変更する方法はありません。 たとえpasswordによって参照される文字列の内容をなんとかして上書きすることができたとしても(これは愚かなctypesトリックで技術的に可能です) 、さまざまな文字列操作で作成されたパスワードの他のコピーがまだあります。

  • 入力されたパスワードから末尾の改行を取り除くときにgetpassモジュールによって
  • imaplibモジュールがパスワードを引用し、完全なIMAPコマンドを作成してからソケットに渡す場合

どういうわけか、これらすべての文字列への参照を取得し、それらのメモリも上書きする必要があります。

44
Miles

実際には、Pythonで文字列を安全に消去する方法があります。 Pythonで機密としてデータをマークする のようにmemsetC関数を使用します

投稿が行われてからずっと後に、追加するように編集されました: 文字列のインターンについて詳しく説明します 。 CPython参照カウントGCに基づいて、インターンが発生せず、文字列値のクリーンアップが少し明確になる状況がいくつかあります(主に非定数文字列が関係します)。 (まだ「スクラブ」/「サニタイズ」クリーンアップではありませんが。)

19
amcgregor

メールオブジェクトを使い終わったら永続化する必要がない場合は、サブプロセスでメール送信作業を実行するのが最善の策だと思います( subprocess モジュールを参照)。サブプロセスが停止すると、パスワードも停止します。

6
zdan

正しい解決策は、可変であるbytearray()...を使用することです。これにより、RAMからキーと機密情報を安全にクリアできます。

ただし、いくつかのライブラリ、特に「bytearray」の使用を妨げるpython "cryptography"ライブラリ)があります。これには問題があります...ある程度、これらの暗号ライブラリはのみ可変タイプがキーマテリアルに使用されます。

メモリからキーを完全に削除できるpipモジュールであるSecureStringがあります...(私はそれを少しリファクタリングしてSecureBytesと呼びました)。キーが完全に削除されていることを示す単体テストをいくつか作成しました。

ただし、大きな注意点があります。誰かのパスワードが「type」の場合、「type」という単語は、関数定義やオブジェクト属性を含むすべてのPythonから消去されます。

言い換えれば...不変の型を変更することはひどい考えであり、特に注意しない限り、実行中のプログラムをすぐにクラッシュさせる可能性があります。

適切な解決策...キーマテリアルやパスワードなどに不変の型を使用しないでください。「getpass」のような暗号化ライブラリまたはルーチンを構築する人は、python strings」ではなく「bytearray」を使用する必要があります。 。

1
Erik Aronesty

これは、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[:] = ''

パスワードの最大サイズを決定する必要がありますが、これにより、データが上書きされたときにデータが削除されます。

1
heplat

ここで:以下は、変数のメモリアドレスバイトをゼロに置き換えてから、メモリ位置へのポインタを逆参照します。

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.
0
nightm4re