Pregunta

Primero, contexto: estoy tratando de crear una herramienta basada en línea de comandos (Linux) que requiere inicio de sesión. Las cuentas en esta herramienta no tienen nada que ver con cuentas a nivel del sistema: nada de esto se ve en / etc / passwd.

Estoy planeando almacenar cuentas de usuario en un archivo de texto con el mismo formato (aproximadamente) que / etc / passwd.

A pesar de no usar los archivos de contraseña de nivel de sistema, parecía usar cripta ser una buena práctica para usar, en lugar de almacenar contraseñas en Borrar texto. (Si bien la cripta es ciertamente mejor que almacenar contraseñas en texto claro, estoy abierto a otras formas de hacer esto).

Mi conocimiento de la cripta se basa en esto: https://docs.python.org/2/library/crypt.html

La documentación parece pedir algo que no es posible: " se recomienda utilizar la contraseña encriptada completa como sal al verificar para una contraseña. "

¿Eh? Si estoy creando la contraseña cifrada (como en, al crear un usuario registro) ¿cómo puedo usar la contraseña cifrada como una sal? Eso aún no existe (Supongo que debe usar la misma sal para crear y verificar una contraseña).

He intentado usar la contraseña de texto sin formato como una sal. Esto hace trabaja, pero tiene dos problemas; uno fácilmente superable y otro serio:

1) Las dos primeras letras de la contraseña de texto sin formato se incluyen en el contraseña encriptada Puedes arreglar esto no escribiendo los dos primeros caracteres al archivo:

user_record = '%s:%s:%s' % (user_name, crypted_pw[2:], user_type)

2) Al usar la contraseña de texto sin formato como la sal, parecería ser reduciendo la cantidad de entropía en el sistema. Posiblemente soy malentendido el propósito de la sal.

La mejor práctica que he podido obtener es usar los dos primeros caracteres del nombre de usuario como la sal. ¿Sería esto apropiado? ¿O hay algo que me he perdido que hace que sea un mal movimiento?

Mi comprensión de una sal es que impide la contraseña de pre-computación hashes de un diccionario. Podría usar una sal estándar para todos contraseñas (como mis iniciales, "JS", pero eso parece ser menos carga para un atacante que usar dos caracteres del nombre de usuario de cada usuario.

¿Fue útil?

Solución

Para el uso del módulo de cripta:

Al GENERAR la contraseña cifrada, usted proporciona la sal. También podría ser aleatorio aumentar la resistencia al forzamiento bruto, siempre que cumpla con las condiciones enumeradas. Al VERIFICAR una contraseña, debe proporcionar el valor de getpwname, en caso de que esté en un sistema que admita tamaños de sal más grandes y no lo haya generado usted mismo.

Comentarios generales:

Si esto no tiene nada que ver con los inicios de sesión reales del sistema, no hay nada que le impida utilizar un método más fuerte que crypt. Podría generar aleatoriamente N caracteres de sal por usuario, para combinarlos con la contraseña del usuario en un hash SHA-1.

string_to_hash = user.stored_salt + entered_password
successful_login = (sha1(string_to_hash) == user.stored_password_hash)

ACTUALIZACIÓN: Si bien esto es mucho más seguro contra las tablas del arco iris, el método anterior todavía tiene debilidades criptográficas. La aplicación correcta de un algoritmo HMAC puede aumentar aún más su seguridad, pero está más allá de mi competencia.

Otros consejos

El crypt () de Python es un contenedor para la función crypt () del sistema. Desde la página de manual de Linux crypt ():

char *crypt(const char *key, const char *salt);

key is a user’s typed password.
salt is a two-character string chosen from the set [a–zA–Z0–9./]. 
This string is used to perturb the algorithm in one of 4096 
different ways.

El énfasis está en " cadena de dos caracteres " ;. Ahora, si observa el comportamiento de crypt () en Python:

>>> crypt.crypt("Hello", "World")
'Wo5pEi/H5/mxU'
>>> crypt.crypt("Hello", "ABCDE")
'AB/uOsC7P93EI'

descubres que los primeros dos caracteres del resultado siempre coinciden con los dos primeros caracteres de la sal original, que de hecho forman la verdadera sal de dos caracteres. Es decir, el resultado de crypt () tiene la forma 2char-salt + encrypted-pass. Por lo tanto, no hay diferencia en el resultado si en lugar de pasar la sal de dos caracteres o la sal de muchos caracteres original, pasa la contraseña cifrada completa.

Nota: el conjunto [a & # 8211; zA & # 8211; Z0 & # 8211; 9. /] contiene 64 caracteres y 64 * 64 = 4096. Así es como dos personajes se relacionan con " 4096 formas diferentes " ;.

Está malentendido la documentación; dice que, dado que la longitud de la sal puede variar según la implementación de crypt () subyacente, debe proporcionar la contraseña encriptada completa como el valor de sal al verificar las contraseñas . Es decir, en lugar de quitar los dos primeros caracteres para que sean la sal, simplemente agregue todo.

Su idea de que la sal inicial se base en el nombre de usuario parece estar bien.

Aquí hay algunos consejos generales sobre las contraseñas de sal:

  1. En general, las sales se utilizan para hacer que tablas ranbow sean demasiado costosas de calcular. Por lo tanto, debe agregar un poco de sal aleatoria a todos los hashes de su contraseña, y simplemente almacenarlo en texto sin formato junto al valor de la contraseña hash.
  2. Use HMAC : es un buen estándar y es más seguro que concatenar la contraseña y sal.
  3. Usar SHA1: MD5 está roto. Sin ánimo de ofender si supieras esto, solo por ser minucioso. ;)

Yo no permitiría que la sal sea una función de la contraseña. Un atacante tendría que generar una tabla de arcoíris para tener una base de datos de búsqueda instantánea de contraseñas, pero solo tendría que hacerlo una vez. Si elige un entero aleatorio de 32 bits, tendrían que generar 2 ^ 32 tablas, lo que (a diferencia de una sal determinista) cuesta mucho, demasiada memoria (y tiempo).

Para mayor fortaleza, puede hacer que el módulo de cripta use md5 usando una sal en el formato.

$1$ABCDEFGH$

donde ABCDEFGH es tu cadena de sal.

>>> p = crypt.crypt('password', '$1$s8Ty3/f

(tenga en cuenta que esta es una extensión de GNU para la cripta, vea '' Man cripta '' en un sistema Linux). MD5 (y ahora incluso SHA1) pueden estar "rotos", pero siguen siendo relativamente buenos para los hashes de contraseñas, y md5 sigue siendo el estándar para las contraseñas locales de Linux.

) >>> p Out: '$1$s8Ty3/f<*>H/M0JswK9pl3X/e.n55G1' >>> p == crypt.crypt('password', p) Out: True

(tenga en cuenta que esta es una extensión de GNU para la cripta, vea '' Man cripta '' en un sistema Linux). MD5 (y ahora incluso SHA1) pueden estar "rotos", pero siguen siendo relativamente buenos para los hashes de contraseñas, y md5 sigue siendo el estándar para las contraseñas locales de Linux.

La contraseña, o cualquier cosa derivada de la contraseña, nunca debe usarse como sal. La sal para una contraseña en particular debe ser impredecible.

Un nombre de usuario o parte del nombre de usuario es tolerable, pero aún mejor serían bytes aleatorios de un RNG criptográfico.

Use PBKDF2, consulte este comentario en un hilo diferente ( incluye la implementación de Python).

Eche un vistazo al artículo TrueCrypt explicado por Björn Edström . Contiene una explicación fácil de entender sobre cómo funciona truecrypt y una implementación simple de Python de algunas de las funciones de truecrypt incluyendo la gestión de contraseñas .

  

Está hablando del módulo Python crypt (), no de TrueCrypt en Python

Predeterminado crypt.crypt () en Python 2 no es muy seguro y el artículo explica cómo podrían funcionar alternativas más seguras.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top