問題:プログラマーはユーザーにパスワードの入力を求めます。 getpass()関数はこの目的に適していますが、その使用には欠点があります。パスワードを入力している間、stdout。
質問:ユーザーが入力したすべての文字に対してアスタリスクが出力されている間に、getpass()をどのように実装できますか? (もちろんbackspace-理想的にはpos1とend-とるべきですそれに応じて注意してください。)
動機:なぜこの質問がなされたのかを理解していないコミュニティの人々がいます。次に、getpass()をa)参照して、a)この方法で手元のタスクを無視し、b)参照が質問に答えないことを考えずにs.o.の理由ユーザーの便宜のために、アスタリスクを印刷することをお勧めします。パスワードの入力中に直接視覚的に応答します。そのため、キーを押しても混乱することはなく、目には何も起こっていないようです。
ソリューションへのステップ:
ここで解決策への最初のステップを紹介しましょう。それを実際のソリューションに進化させるために助けてください。
gdinという名前のモジュールがあり、stdinから1文字ずつ読み取ることができるようです。バックスペースは-奇妙なことに-127の整数値にマッピングされますが、そのようなソリューションは次のようになります。
def readLineWithAsterisks():
sBuffer = ''
while True:
c = getch.getch()
if c == '\n':
return sBuffer
Elif ord(c) == 127:
if len(sBuffer) > 0:
sys.stdout.write('\x08 \x08')
sys.stdout.flush()
sBuffer = sBuffer[0:-1]
continue
else:
sys.stdout.write('*')
sys.stdout.flush()
sBuffer += c
しかし、このコードにはいくつかの欠点があります。最初に、s.oの場合、cが '\ b'にならないことについて非常に混乱しています。 backspaceを入力しました。多分s.o.これについて説明がありますか? 2番目のみASCII文字は少なくともLinuxでは処理されます。ここではWindowsについては知りませんが、A-Z0-9以外の文字が押された場合、行c = getch.getch()は例外をスローします。getch()はウムラウトや他の種類の文字を処理できないようです。少なくともある程度は。
ソリューションの入力を行うには、次の問題に対処する必要があります。
そこで最初の答えを見てください:
python端末からキーボード入力を検出する最も簡単な方法は何ですか?
キーが押されたときに星印「*」または何かを印刷するだけです。
すべての信用は明らかに研究のためにフィリダに向けられています。
私は、プラットフォームを個別にどのように実行するかを大まかに説明するためにモジュールを書きました。
_#!/usr/bin/python2
def _masked_input_unix(Prompt="Password: ", mask="*"):
pw = ""
# save terminal settings
fd = sys.stdin.fileno()
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)
# setup 'cbreak' mode
new[3] = new[3] & ~termios.ECHO
new[3] = new[3] & ~termios.ICANON
new[6][termios.VMIN] = '\x01'
new[6][termios.VTIME] = '\x00'
try:
termios.tcsetattr(fd, termios.TCSADRAIN, new)
print Prompt,
# Read the password
while True:
c = sys.stdin.read(1)
# submit chars
if c == '\r' or c == '\n':
sys.stdout.write("%s" % (c))
break
# delete chars
Elif c == '\b' or c == '\x7f':
if len(pw) > 0:
pw = pw[:-1]
sys.stdout.write("%s" % ('\b \b'))
# password chars
else:
pw += c
sys.stdout.write("%s" % (mask))
finally:
# ensure we reset the terminal
termios.tcsetattr(fd, termios.TCSADRAIN, old)
return pw
def _masked_input_win(Prompt="Password: ", mask='*'):
pw = ""
while True:
c = msvcrt.getch()
# submit chars
if c == '\r' or c == '\n':
while msvcrt.kbhit():
msvcrt.getch()
print
break
Elif c == '\x03':
raise KeyboardInterrupt
# delete chars
Elif c == '\b' or c == '\x7f':
if len(pw) > 0:
pw = pw[:-1]
msvcrt.putch('\b')
msvcrt.putch(' ')
msvcrt.putch('\b')
# password chars
else:
pw += c
msvcrt.putch(mask)
return pw
## initialize windows or posix function pointer
masked_input = None
try:
import msvcrt
masked_input = _masked_input_win
except ImportError:
import sys, termios
masked_input = _masked_input_unix
if __name__ == "__main__":
p = masked_input()
print "Password is:", p
_
そして、これはシングルバイトエンコーディングで機能します。ユニコードのサポートを追加することは簡単ではありません。 Windowsでgetpass
モジュールを使用すると、Unicodeがうまく機能しないと思います。 (注:すべてをユニコード文字列に変更してgetwch()
を使用するほど簡単ではありません)
注:私の他の答えには、プラットフォームに依存しない方法でこれを行うためのpython2コードが含まれています
安全なプラットフォームに依存しない方法では、getpass.getpass()
と同じものがすべてセットアップされるので、ソース(_/usr/lib/python2.7/getpass.py
_の場合)を確認してください。とても簡単です。
星の響きは......
win_getpass()
はすでに1文字ずつ読み込んでいます。そのループで_*
_をエコーしてください。 msvcrt.getwch()
の代わりにmsvcrt.getch()
を使用する必要がある場合がありますが、これはpython getpass
モジュールにバグがあることを意味します。
unix_getpass()
は扱いにくいです。 cbreak
がすでに無効になっているのと同様に、ターミナルにECHO
を設定する必要があります( https://utcc.utoronto.ca/~cks/space/blog/unixを参照)。/CBreakAndRaw )。次に、read(1)
が使用しているwin_getpass()
ではなく、readline()
をループで使用する必要があります(_raw_input()
と同様)。
バイトごとに読み取ると、「文字」を構成するものを決定するのに苦労することができます。これはエンコーディングに依存し、長さが可変である場合もあります(UTF-8の場合)。
これはLinuxのみのバージョンであり、Python 2およびPython 3 with nicodeサポートで動作します。
Unicode文字を入力するには、Ctrl+Shift
を同時に入力し、u
と入力してリリースCtrl+Shift
、コードポイントを入力して<Enter>
。
os.read
およびos.write
(libcおよびpython IO)バッファリングの問題を回避し、カーネルからバイトを読み取るための関数。
ターミナルKILL(^ U)、ERASE(ascii DEL別名Backspace
キー)、EOF(^ D)およびascii BS(\b
)がサポートされています。
パスワードの読み取り中はSIGTSTP
を無視します。これは、バックグラウンドからの再開時に入力された文字がエコーされるためです。
import tty
import os
import sys
import signal
from array import array
# disable (^Z) SIGTSTP
signal.signal(signal.SIGTSTP, signal.SIG_IGN)
stdin = sys.__stdin__.fileno()
stream = sys.__stderr__.fileno()
old = tty.tcgetattr(stdin)
os.write(stream, b"Passwd: ")
try:
tty.setcbreak(stdin)
passwd = array("u")
while True:
# UTF-8 is 4 octets (bytes) at max
c = os.read(stdin, 4)
# ERASE ascii DEL (0x7f) <Backspace> and ascii BS (0x08) <^H>
if c in (old[tty.CC][tty.VERASE], b"\b"):
if passwd:
os.write(stream, b"\b \b")
passwd.pop()
# KILL ascii NAK (0x15) <^U>
Elif c == old[tty.CC][tty.VKILL]:
if passwd:
os.write(stream, b"\b \b" * len(passwd))
passwd = array("u")
# ascii LF (0x0a) <^J>, CR (0x0d) <^M> and <Enter> and EOT (0x04) <^D>
Elif c in (b"\n", old[tty.CC][tty.VEOF]):
break
else:
#c = c.decode('utf-8')
c = c.decode(sys.__stdin__.encoding)
passwd.append(c)
os.write(stream, b"*")
finally:
# restore terminal settings
tty.tcsetattr(stdin, tty.TCSAFLUSH, old)
# enable (^Z) SIGTSTP
signal.signal(signal.SIGTSTP, signal.SIG_DFL)
os.write(stream, b"\n")
print(passwd.tounicode())
テスト;
$ # To input "Þàsswõrd"
$ # U+00de, U+00e0, s,s, w, U+00f5, r, d
$ python getpass.py
$ Passwd: ********
Þàsswõrd
このレシピは、プラットフォームに依存しない独自のソリューションの出発点として役立つかもしれません
http://code.activestate.com/recipes/134892/
Windowsの場合、msvcrt libを使用します。 getch()の呼び出しをgetwch()に置き換えると、Unicodeを処理できます。
ActivePython 2.7.10.12 (ActiveState Software Inc.) based on
Python 2.7.10 (default, Aug 21 2015, 12:07:58) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import msvcrt
>>> msvcrt.getch()
'j'
>>> msvcrt.getch()
'r'
>>> msvcrt.getch()
'?'
>>> msvcrt.getwch()
u'\u0432'