raw_input - ¿Cómo se puede hacer una entrada de contraseña en python con la impresión de un asterisco para cada carácter del usuario?
python input() (7)
Es posible que desee ver cómo jupyter / ipython implementó esto. Aparece un punto que se muestra inmediatamente para cada carácter escrito con getpass ().
Esta pregunta se ha hecho antes y todavía falta una respuesta, por lo que puedo ver. Así que vuelvo a hacer esta pregunta para finalmente obtener una respuesta a esta pregunta. (Si me equivoco, por favor, dame la URL de la respuesta).
El problema: los programadores quieren que los usuarios ingresen contraseñas. La función getpass () es buena para este propósito, pero su uso tiene un inconveniente: al ingresar una contraseña, nada se imprime en la salida estándar .
La pregunta: ¿Cómo se puede implementar un getpass () mientras se imprimen los asteriscos para cada carácter escrito por un usuario? (Por supuesto, el retroceso (e idealmente pos1 y end ) debe cuidarse en consecuencia).
La motivación: ha habido personas en la comunidad que no comprenden por qué se ha formulado esta pregunta. Y luego se refirió a getpass () con a) de esta manera ignorando la tarea en cuestión yb) sin pensar en eso, la referencia no respondería la pregunta. La razón por la que podría querer tener asteriscos impresos es para la conveniencia de los usuarios: obtienen una respuesta visual directa durante la entrada de la contraseña. Por lo tanto, no se confunden presionando las teclas y, para los ojos, nada parece estar sucediendo.
Un paso hacia una solución:
Permítanme presentarles un primer paso hacia una solución aquí. Por favor ayuda para evolucionarlo en una solución real.
Hay un módulo llamado getch que parece permitir la lectura de carácter por carácter desde la entrada estándar . El retroceso se asigna, de forma bastante extraña, a un valor entero de 127, pero una solución de este tipo podría tener este aspecto:
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
Pero este código tiene algunos inconvenientes. Primero, estoy muy confundido acerca de que c no sea ''/ b'' si así se ingresó en un retroceso . Tal vez así tiene una explicación para esto? En segundo lugar, solo se procesan los caracteres ASCII, al menos en Linux. No sé sobre Windows aquí, pero si se presiona un carácter que no sea A-Z0-9, la línea c = getch.getch () lanzará una excepción. getch () no parece poder procesar diéresis y otros tipos de caracteres, al menos hasta cierto punto.
Para llegar a la entrada de la solución se deben abordar los siguientes problemas:
- ¿Cómo se puede hacer una lectura desde la entrada estándar carácter por carácter con respecto a los caracteres no ASCII?
- ¿Cómo se puede hacer esto de una manera independiente de la plataforma?
- ¿Cómo puede hacerse esto de manera segura (sin problemas de seguridad)? ( Getpass de alguna manera parece abordar esto, pero no entiendo completamente cómo).
Tal vez nadie sepa sobre esto. Que yo sepa, preguntas como esta que se habían formulado anteriormente quedaron sin respuesta. ¿Pero quizás hay algunos desarrolladores de software en la comunidad que saben más de todo esto y pueden ayudar aquí? En caso de éxito, puedo obtener la solución provista en GitHub como un módulo de python.
Escribí un módulo para ilustrar aproximadamente cómo se hace la plataforma de forma independiente.
#!/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
Y esto funciona para las codificaciones de un solo byte. Agregar soporte para Unicode no es trivial. Sospecho que Unicode no funciona bien con el módulo getpass
en Windows. (NOTA: no es tan simple como cambiar todo a cadenas Unicode y usar getwch()
)
Esta es una versión solo para Linux, funciona en Python 2 y Python 3 con soporte Unicode .
Para escribir caracteres Unicode, mantenga presionadas las Ctrl+Shift
simultáneamente, escriba u
y suelte las Ctrl+Shift
, ahora escriba el punto de código y <Enter>
.
Uso exclusivamente las os.read
y os.write
para omitir (libc y python IO) los problemas de almacenamiento en búfer y leer bytes desde el kernel.
Terminal KILL (^ U), ERASE (ascii DEL también conocido como tecla Backspace
), EOF (^ D) y ascii BS ( /b
) son compatibles.
SIGTSTP
mientras leo la contraseña, porque en la reanudación desde el fondo se repiten los caracteres escritos.
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())
Prueba;
$ # To input "Þàsswõrd"
$ # U+00de, U+00e0, s,s, w, U+00f5, r, d
$ python getpass.py
$ Passwd: ********
Þàsswõrd
Probablemente sea más fácil usar tkinter, aunque probablemente no sea tan seguro, esto es lo más cerca que puedo estar de lo que está pidiendo.
from tkinter import *
from tkinter import ttk
root = Tk()
parent = ttk.Frame(root)
parent.grid()
password = ttk.Entry(parent, show="*").grid()#shows each character as an asterix
root.mainloop()
Lo siento, no pude evitar más.
Puede encontrar esta receta tan útil como punto de partida para su propia solución independiente de plataforma.
http://code.activestate.com/recipes/134892/
En el caso de windows usa msvcrt lib. Puede reemplazar una llamada a getch () a getwch () para poder procesar 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''
Vea la primera respuesta allí:
¿Cuál es la forma más sencilla de detectar entradas de teclado en python desde el terminal?
Solo imprime las estrellas ''*'' o cualquier cosa cuando se presiona una tecla.
Todo el crédito, obviamente, va a Phylliida para la investigación.
NOTA: mi otra respuesta contiene código python2 para hacer esto de manera independiente de la plataforma.
La forma segura de la plataforma independiente configurará todo lo idéntico a getpass.getpass()
así que mire la fuente ( /usr/lib/python2.7/getpass.py
para mí); Es bastante sencillo.
En cuanto al eco de las estrellas ...
win_getpass()
ya está leyendo char por char, solo hace eco de algunos *
en ese bucle. Es posible que necesite usar msvcrt.getwch()
lugar de msvcrt.getch()
pero eso significaría que el módulo getpass
Python tiene un error.
unix_getpass()
es más complicado. cbreak
configurar un cbreak
para el terminal similar a cómo ECHO
se está deshabilitando (consulte https://utcc.utoronto.ca/~cks/space/blog/unix/CBreakAndRaw ). Luego tendrá que usar read(1)
en un bucle (similar a win_getpass()
) en lugar de readline()
que _raw_input()
está usando.
Una vez que esté leyendo byte a byte, puede pasar por los dolores de determinar qué constituye una "letra". Esto depende de la codificación e incluso puede tener una longitud variable (en el caso de UTF-8).