Python crypt module -- what's the correct use of salts?

First, context: I'm trying to create a command-line-based tool (Linux) that requires login. Accounts on this tool have nothing to do with system-level accounts -- none of this looks at /etc/passwd.

I am planning to store user accounts in a text file using the same format (roughly) as /etc/passwd.

Despite not using the system-level password files, using crypt seemed to be a good practice to use, as opposed to storing passwords in cleartext. (While crypt is certainly better than storing passwords in cleartext, I'm open to other ways of doing this.)

My crypt knowledge is based on this: https://docs.python.org/2/library/crypt.html

The documentation seems to ask for something that isn't possible: "it is recommended to use the full crypted password as salt when checking for a password."

Huh? If I'm creating the crypted password (as in, when creating a user record) how can I use the crypted password as a salt? It doesn't exist yet. (I'm assuming that you must use the same salt for creating and checking a password.)

I've tried using the plaintext password as a salt. This does work, but has two problems; one easily overcome, and one serious:

1) The first two letters of the plaintext password are included in the crypted password. You can fix this by not writing the first two characters to the file:

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

2) By using the plaintext password as the salt, you would seem to be reducing the amount of entropy in the system. Possibly I'm misunderstanding the purpose of the salt.

The best practice I've been able to derive is to use the first two characters from the username as the salt. Would this be appropriate, or is there something I've missed that makes that a bad move?

My understanding of a salt is that it prevents pre-computing password hashes from a dictionary. I could use a standard salt for all passwords (such as my initials, "JS,") but that seems to be less of a burden for an attacker than using two characters from each user's username.


Asked by: Dainton525 | Posted: 05-10-2021






Answer 1

Python's crypt() is a wrapper for the system's crypt() function. From the Linux crypt() man page:

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.

Emphasis is on "two-character string". Now, if you look at crypt()'s behavior in Python:

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

you discover that the first two characters of the result always coincide with the first two characters of the original salt, which indeed form the true two-character-salt itself. That is, the result of crypt() has the form 2char-salt + encrypted-pass. Hence, there is no difference in the result if instead of passing the two-character-salt or the original many-characters-salt you pass the whole encrypted password.

Note: the set [a–zA–Z0–9./] contains 64 characters, and 64*64=4096. Here's how two characters relate to "4096 different ways".

Answered by: Kimberly664 | Posted: 06-11-2021



Answer 2

For the use of the crypt module:

When GENERATING the crypted password, you provide the salt. It might as well be random to increase resistance to brute-forcing, as long as it meets the listed conditions. When CHECKING a password, you should provide the value from getpwname, in case you are on a system that supports larger salt sizes and didn't generate it yourself.

General comments:

If this has nothing to do w/ actual system logins, there is nothing preventing you from using a stronger method than crypt. You could randomly generate N characters of per-user salt, to be combined with the user's password in a SHA-1 hash.

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

UPDATE: While this is far more secure against rainbow tables, the method above still has cryptographic weaknesses. Correct application of an HMAC algorithm can yet further increase your security, but is beyond my realm of expertise.

Answered by: Rubie338 | Posted: 06-11-2021



Answer 3

You're misunderstanding the documentation; it says that since the length of the salt may vary depending on the underlying crypt() implementation, you should provide the entire crypted password as the salt value when checking passwords. That is, instead of pulling the first two chars off to be the salt, just toss in the whole thing.

Your idea of having the initial salt be based on the username seems okay.

Answered by: Maria603 | Posted: 06-11-2021



Answer 4

Here's some general advice on salting passwords:

  1. In general, salts are used to make ranbow tables too costly to compute. So, you should add a little randomized salt to all your password hashes, and just store it in plaintext next to the hashed password value.
  2. Use HMAC - it's a good standard, and it's more secure than concatenating the password and salt.
  3. Use SHA1: MD5 is broken. No offense intended if you knew this, just being thorough. ;)

I would not have the salt be a function of the password. An attacker would have to generate a rainbow table to have an instant-lookup database of passwords, but they'd only have to do that once. If you choose a random 32-bit integer, they'd have to generate 2^32 tables, which (unlike a deterministic salt) costs way, way too much memory (and time).

Answered by: Edgar461 | Posted: 06-11-2021



Answer 5

For some added strength, you can get the crypt module to use md5 by using a salt in the format.

$1$ABCDEFGH$

where ABCDEFGH is your salt string.

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

(note that this is a gnu extension to crypt, see "man crypt" on a linux system). MD5 (and now even SHA1) may be "broken", but they are still relatively good for password hashes, and md5 is still the standard for linux local passwords.

Answered by: Max480 | Posted: 06-11-2021



Answer 6

The password, or anything derived from the password, should never be used as salt. The salt for a particular password should be unpredictable.

A username or part of the user name is tolerable, but even better would be random bytes from a cryptographic RNG.

Answered by: Kellan153 | Posted: 06-11-2021



Answer 7

Use PBKDF2, see this comment on a different thread (includes Python implementation).

Answered by: Dexter892 | Posted: 06-11-2021



Answer 8

Take a look at the article TrueCrypt explained by Björn Edström. It contains easy to understand explanation of how truecrypt works and a simple Python implementation of some of truecrypt's functionality including password management.

He's talking about the Python crypt() module, not about TrueCrypt in Python

Default crypt.crypt() in Python 2 is not very secure and the article explains how more secure alternatives might work.

Answered by: Byron750 | Posted: 06-11-2021



Similar questions

Python JSON module gives error even though input is correct

For a group project I have to import a JSON object with python. The bad thing is that python keeps giving me an error message, even though the JSON string is correct (checked it with JSONLint). The script I use to test this is as follows: import json correct_json = """ { "created_at": "Tue Feb 19 18:42:07 +0000 2013", "id": 303937526471725060, "id_str": "303937526471725056", "text": "Ba...


python - Module is in correct folder, but still says that it cannot be found

I have pygame and numpy in the same folder. I can import pygame, but I can only import numpy in the terminal, not even in the IDLE. If I try to import numpy anywhere else, I get this message: Traceback (most recent call last): File "<stdin>", line 1, in <module> ModuleNotFoundError: No module named 'numpy' I can also still import pygame...


In Python how do I correct this Module Not Found Error

I have the following module structure in my Administrator project folder StockController.py (in the root project folder) then in a package called Utilities I have the following two modules ExcelManipulator.py StockPurchaseInfo.py Relevant Code from each module as as follows: StockController.py from Utilities.ExcelManipulator import ExcelManipulatorCla...


python - What is the correct way to backup ZODB blobs?

I am using plone.app.blob to store large ZODB objects in a blobstorage directory. This reduces size pressure on Data.fs but I have not been able to find any advice on backing up this data. I am already backing up Data.fs by pointing a network backup tool at a directory of repozo backups. Should I simply point that tool at the blobstorage directory to backup my blobs? What if the database is being repacked ...


python - eval giving syntax error even when correct code given

I have the following code, which uses the eval function: lines = self.fulltext.splitlines() CURRENT = 0 extractors = { "solar zenith angle" : (CURRENT, 1, "self.solar_z"), "ground pressure" : (CURRENT, 2, "self.ground_pressure") } print locals() for line in lines: for label, details in extractors.iteritems(): if label in ...


how to correct this in python 3.0

I am a newer to python. After I leraned some basic in python, I try to write some program by imitating other's. And I find some code in python2.0 and it can't work in 3.0. How can I fix it. import sys import os import string headers = [ ('JFIF', 6, 'jpg'), ('GIF', 0, 'gif'), ('PNG', 1, 'png') ] marker = [] fileName = r'd:\\first.doc' try: fid = open(fileName, 'rb') #open file in binary mode not tex...


I keep getting "list index out of range" in Python when the list has the correct index

I am new to Python and am getting the list index error, when I shouldn't. I have the following variable: date_array = ['2001','15','1'] I can access the first index. I can only access the last index, if I try something like this: date_array[-1] I get "list index out of range" error whenever I try: date_array[2] date_array[1]


Correct python re regex

I tried to make regex to validate phone number in format +38(0XX)XXX-XX-XX or 0XX-XXX-XX-XX. My regex is: '^(\+38)*(\(*0\d{2}\)*)[-|\s](\d{3})[-|\s]((\d{2})-|\s)+$'. And it's not matched any way. I reread re syntax a couple of times, can't get how to make it right.


python - Can't get my loop & match to append to correct list

I´m experiencing problems with my code. I can´t get it to append to the list not_found as well as it loops twice for some reason. Can anyone point me in the right direction? The match works for my_track, but it doesn't when it doesn't match. # coding: utf-8 #!/usr/bin/env python import spotimeta import sys import time my_tracks = raw_input("Please enter a sentence: ").title().split() playlist = [] real_pl...


Correct use of Popen in Python, or something alike

I'm completely new to Python, and now I've to change a script so it calls another program and get it's output. I've researched, and all the solutions point to something like p = sub.Popen('echo '+s+' | ${morphg_res:-./morphg.ix86_linux -t}', shell=True,stdout=sub.PIPE,stderr=sub.PIPE ) out,err=p.communicate() print out but I couldn't get it to work. "print out" prints nothing. I...


python - Correct the color on an image

I have an image that was taken and had a bayer filter applied to it. I am trying to correct the color because depenging on what filter we apply (BG 2 RGB for example) it comes out with a tint, be it yellow, blue, green, pink etc... I am using the python image library to try and fix the image. I have taken an image of the visible spectrum and can make it so that one or two colors are right by multiplying by the co...


python - How to set the correct path for a file in VIM?

Whenever I hit :pwd in vim the command always returns the path C:\Windows\system32, even if I'm in a Python file from the desktop. So whenever I run :!python % the command returns python: can't open file '\Users\myname': [Errno 2] No such file or directory. But if I set the path with the command :cd %:p:h and then run the same python com...


python - high chart graph doesnt start from the correct month

I have a chart in my django based project wher I need to display two series of data (previsions and consommations) over the twelve months of the year The problem is that when my data doesnt start from january (for example) , I still have to display january with empty data This works for one of the series (prvisiosns) but not the other meaning that previsions start frm february as it should be but consommat...


python - What is the correct way to backup ZODB blobs?

I am using plone.app.blob to store large ZODB objects in a blobstorage directory. This reduces size pressure on Data.fs but I have not been able to find any advice on backing up this data. I am already backing up Data.fs by pointing a network backup tool at a directory of repozo backups. Should I simply point that tool at the blobstorage directory to backup my blobs? What if the database is being repacked ...


what is the correct way to close a socket in python 2.6?

i have a simple server/client. and i am using the netcat as the client to test the server. if i stop the server before the client exit, i will not be able to start the server again for a while and i go this error: " [Errno 98] Address already in use " but if i close the client first, then the server stops, i will not have this issue. my server socket works like this: try: s=socket s.bind...


python - How can I make Django use the correct current time for my system locale?

From Python interpreter in the Gnome terminal on Ubuntu, I enter and get the following: >>> import datetime >>> now = datetime.datetime.now() >>> print now 2011-03-24 12:27:32.527229 With Django, the output to the web browser is an hour behind: It is now 2011-03-24 11:27:38.864572. The Django code (running on the same ...


Correct path to run python script with php from cgi-bin folder

I want to run python script 'test.py' from my cgi-bin directory on my webserver. the cgi-bin directory is at 'www/cgi-bin/'. The python scrip is in that directory. The php code I'm executing is at 'www/html/website/index.php'. what is the correct path that goes here ------> exec('path'); TYVM edit: My python script has been chmod +x'd and is executable (I have tested)


python - eval giving syntax error even when correct code given

I have the following code, which uses the eval function: lines = self.fulltext.splitlines() CURRENT = 0 extractors = { "solar zenith angle" : (CURRENT, 1, "self.solar_z"), "ground pressure" : (CURRENT, 2, "self.ground_pressure") } print locals() for line in lines: for label, details in extractors.iteritems(): if label in ...


how to correct this in python 3.0

I am a newer to python. After I leraned some basic in python, I try to write some program by imitating other's. And I find some code in python2.0 and it can't work in 3.0. How can I fix it. import sys import os import string headers = [ ('JFIF', 6, 'jpg'), ('GIF', 0, 'gif'), ('PNG', 1, 'png') ] marker = [] fileName = r'd:\\first.doc' try: fid = open(fileName, 'rb') #open file in binary mode not tex...


I keep getting "list index out of range" in Python when the list has the correct index

I am new to Python and am getting the list index error, when I shouldn't. I have the following variable: date_array = ['2001','15','1'] I can access the first index. I can only access the last index, if I try something like this: date_array[-1] I get "list index out of range" error whenever I try: date_array[2] date_array[1]


Correct python re regex

I tried to make regex to validate phone number in format +38(0XX)XXX-XX-XX or 0XX-XXX-XX-XX. My regex is: '^(\+38)*(\(*0\d{2}\)*)[-|\s](\d{3})[-|\s]((\d{2})-|\s)+$'. And it's not matched any way. I reread re syntax a couple of times, can't get how to make it right.


python - Can't seem to get django url route correct with numbers

I am trying to get a url structure like this. http://example.com/blog/1/title-name-goes-here My main urls file has this line, among others. url(r'^blog/', include('blog.urls')), in my blog.urls I have urlpatterns = patterns('', (r'', 'blog.views.index'), (r'^(?P<entry_id>\d+)/(?P<slug>[a-zA-Z0-9_.-]+)/$', 'blog.views.entry'),...


django - Is this correct usage of super in python?

class SessionWizardView(WizardView): @classonlymethod def as_view(cls, *args, **kwargs): #...snipped.. pass class ParentWizard(SessionWizardView): @classonlymethod def as_view(cls, *args, **kwargs): return super(SessionWizardView, cls).as_view( ... ) class ChildWizard(ParentWizard): @classonlymethod def as_view(cls, *args, **kwargs): return super(SessionWiza...






Still can't find your answer? Check out these communities...



PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python



top