teclado raw_input print imprimir declarar datos como comando capturar python security command-line console passwords

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''



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).