Python Authentication API - python

I'm looking for a python library that will help me to create an authentication method for a desktop app I'm writing.
I have found several method in web framework such as django or turbogears.
I just want a kind of username-password association stored into a local file.
I can write it by myself, but I'm really it already exists and will be a better solution (I'm not very fluent with encryption).

dbr said:
def hash_password(password):
"""Returns the hashed version of a string
"""
return hasher.new( str(password) ).hexdigest()
This is a really insecure way to hash passwords. You don't want to do this. If you want to know why read the Bycrypt Paper by the guys who did the password hashing system for OpenBSD. Additionally if want a good discussion on how passwords are broken check out this interview with the author of Jack the Ripper (the popular unix password cracker).
Now B-Crypt is great but I have to admit I don't use this system because I didn't have the EKS-Blowfish algorithm available and did not want to implement it my self. I use a slightly updated version of the FreeBSD system which I will post below. The gist is this. Don't just hash the password. Salt the password then hash the password and repeat 10,000 or so times.
If that didn't make sense here is the code:
#note I am using the Python Cryptography Toolkit
from Crypto.Hash import SHA256
HASH_REPS = 50000
def __saltedhash(string, salt):
sha256 = SHA256.new()
sha256.update(string)
sha256.update(salt)
for x in xrange(HASH_REPS):
sha256.update(sha256.digest())
if x % 10: sha256.update(salt)
return sha256
def saltedhash_bin(string, salt):
"""returns the hash in binary format"""
return __saltedhash(string, salt).digest()
def saltedhash_hex(string, salt):
"""returns the hash in hex format"""
return __saltedhash(string, salt).hexdigest()
For deploying a system like this the key thing to consider is the HASH_REPS constant. This is the scalable cost factor in this system. You will need to do testing to determine what is the exceptable amount of time you want to wait for each hash to be computed versus the risk of an offline dictionary based attack on your password file.
Security is hard, and the method I present is not the best way to do this, but it is significantly better than a simple hash. Additionally it is dead simple to implement. So even you don't choose a more complex solution this isn't the worst out there.
hope this helps,
Tim

I think you should make your own authentication method as you can make it fit your application best but use a library for encryption, such as pycrypto or some other more lightweight library.
btw, if you need windows binaries for pycrypto you can get them here

Treat the following as pseudo-code..
try:
from hashlib import sha as hasher
except ImportError:
# You could probably exclude the try/except bit,
# but older Python distros dont have hashlib.
try:
import sha as hasher
except ImportError:
import md5 as hasher
def hash_password(password):
"""Returns the hashed version of a string
"""
return hasher.new( str(password) ).hexdigest()
def load_auth_file(path):
"""Loads a comma-seperated file.
Important: make sure the username
doesn't contain any commas!
"""
# Open the file, or return an empty auth list.
try:
f = open(path)
except IOError:
print "Warning: auth file not found"
return {}
ret = {}
for line in f.readlines():
split_line = line.split(",")
if len(split_line) > 2:
print "Warning: Malformed line:"
print split_line
continue # skip it..
else:
username, password = split_line
ret[username] = password
#end if
#end for
return ret
def main():
auth_file = "/home/blah/.myauth.txt"
u = raw_input("Username:")
p = raw_input("Password:") # getpass is probably better..
if auth_file.has_key(u.strip()):
if auth_file[u] == hash_password(p):
# The hash matches the stored one
print "Welcome, sir!"
Instead of using a comma-separated file, I would recommend using SQLite3 (which could be used for other settings and such.
Also, remember that this isn't very secure - if the application is local, evil users could probably just replace the ~/.myauth.txt file.. Local application auth is difficult to do well. You'll have to encrypt any data it reads using the users password, and generally be very careful.

If you want simple, then use a dictionary where the keys are the usernames and the values are the passwords (encrypted with something like SHA256). Pickle it to/from disk (as this is a desktop application, I'm assuming the overhead of keeping it in memory will be negligible).
For example:
import pickle
import hashlib
# Load from disk
pwd_file = "mypasswords"
if os.path.exists(pwd_file):
pwds = pickle.load(open(pwd_file, "rb"))
else:
pwds = {}
# Save to disk
pickle.dump(pwds, open(pwd_file, "wb"))
# Add password
pwds[username] = hashlib.sha256(password).hexdigest()
# Check password
if pwds[username] = hashlib.sha256(password).hexdigest():
print "Good"
else:
print "No match"
Note that this stores the passwords as a hash - so they are essentially unrecoverable. If you lose your password, you'd get allocated a new one, not get the old one back.

import hashlib
import random
def gen_salt():
salt_seed = str(random.getrandbits(128))
salt = hashlib.sha256(salt_seed).hexdigest()
return salt
def hash_password(password, salt):
h = hashlib.sha256()
h.update(salt)
h.update(password)
return h.hexdigest()
#in datastore
password_stored_hash = "41e2282a9c18a6c051a0636d369ad2d4727f8c70f7ddeebd11e6f49d9e6ba13c"
salt_stored = "fcc64c0c2bc30156f79c9bdcabfadcd71030775823cb993f11a4e6b01f9632c3"
password_supplied = 'password'
password_supplied_hash = hash_password(password_supplied, salt_stored)
authenticated = (password_supplied_hash == password_stored_hash)
print authenticated #True
see also gae-authenticate-to-a-3rd-party-site

Use " md5 " it's much better than base64
>>> import md5
>>> hh = md5.new()
>>> hh.update('anoop')
>>> hh.digest
<built-in method digest of _hashlib.HASH object at 0x01FE1E40>

Related

Setting up netbox, AttributeError: module 'secrets' has no attribute 'choice'

I'm trying to set up Netbox on a RedHat 8 server and have hit a roadblock when trying to run the script to create the netbox superuser: python3 manage.py createsuperuser. I'm prompted for a new username/email/password, however afterward I get the error:
AttributeError: module 'secrets' has no attribute 'choice'
I'm running python 3.6, and the script does have import secrets already. Anyone know why choice wouldn't be recognized? I tried looking through previous threads but didn't find anything related to the secrets module.
edit: here is the script-
"""
Django's standard crypto functions and utilities.
"""
import hashlib
import hmac
import secrets
from django.conf import settings
from django.utils.encoding import force_bytes
def salted_hmac(key_salt, value, secret=None):
"""
Return the HMAC-SHA1 of 'value', using a key generated from key_salt and a
secret (which defaults to settings.SECRET_KEY).
A different key_salt should be passed in for every application of HMAC.
"""
if secret is None:
secret = settings.SECRET_KEY
key_salt = force_bytes(key_salt)
secret = force_bytes(secret)
# We need to generate a derived key from our base key. We can do this by
# passing the key_salt and our base key through a pseudo-random function and
# SHA1 works nicely.
key = hashlib.sha1(key_salt + secret).digest()
# If len(key_salt + secret) > sha_constructor().block_size, the above
# line is redundant and could be replaced by key = key_salt + secret, since
# the hmac module does the same thing for keys longer than the block size.
# However, we need to ensure that we *always* do this.
return hmac.new(key, msg=force_bytes(value), digestmod=hashlib.sha1)
def get_random_string(length=12,
allowed_chars='abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
"""
Return a securely generated random string.
The default length of 12 with the a-z, A-Z, 0-9 character set returns
a 71-bit value. log_2((26+26+10)^12) =~ 71 bits
"""
return ''.join(secrets.choice(allowed_chars) for i in range(length))
def constant_time_compare(val1, val2):
"""Return True if the two strings are equal, False otherwise."""
return secrets.compare_digest(force_bytes(val1), force_bytes(val2))
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
"""Return the hash of password using pbkdf2."""
if digest is None:
digest = hashlib.sha256
dklen = dklen or None
password = force_bytes(password)
salt = force_bytes(salt)
return hashlib.pbkdf2_hmac(digest().name, password, salt, iterations, dklen)
I had a similar issue after moving my project to production. I had named a "secrets.py" file for use with db and secret_key settings (to easily ignore from git) and apparently that name conflicted and broke my admin view.
Simply changing the name of my secrets.py file resolved my issue.

Matching Signing between Python and Ruby

I have been trying for a few days to validate some message signed with a private key in python. Note that the message has been signed using Ruby.
When I sign the same message in python I can verify it no problem. Note that I have already validated that the hash are the same.
Python code:
string_to_encrypt = b"aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
sha1 = SHA.new()
sha1.update(string_to_encrypt)
# load private key
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open('./license.pem', 'rb').read())
sign_ssl = OpenSSL.crypto.sign(pkey, sha1.digest(), 'RSA-SHA1')
b64_ssl = base64.b64encode(sign_ssl)
Ruby:
string_to_encrypt = "aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
sha1 = Digest::SHA1.digest(string_to_encrypt)
#sign it
private_key_file = File.join(File.dirname(__FILE__), 'license.pem')
rsa = OpenSSL::PKey::RSA.new(File.read(private_key_file))
signed_key = rsa.private_encrypt(sha1)
#update the license string with it
x = Base64.strict_encode64(signed_key)
I would expect b64_ssl and x to contain the same value and they don't. Could someone explain to me what I missing there?
Neither of these code snippets is actually producing the correct signature.
In the Ruby OpenSSL library you want to be using the sign method, not the private_encrypt method, which is a low level operation that doesn’t do everything required to produce a valid signature.
In both libraries the sign operation performs the hashing for you, you don’t need to do this beforehand. In fact your Python code is actually hashing the data twice.
Try the following Python code:
import OpenSSL
import base64
string_to_encrypt = b"aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
# load private key
pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, open('./license.pem', 'rb').read())
sign_ssl = OpenSSL.crypto.sign(pkey, string_to_encrypt, 'SHA1')
b64_ssl = base64.b64encode(sign_ssl)
print(b64_ssl.decode())
which produces the same output as this Ruby code:
require 'openssl'
require 'base64'
string_to_encrypt = "aaaaabbbbbaaaaabbbbbaaaaabbbbbCC"
#sign it
private_key_file = File.join(File.dirname(__FILE__), 'license.pem')
rsa = OpenSSL::PKey::RSA.new(File.read(private_key_file))
signed_key = rsa.sign('sha1', string_to_encrypt)
#update the license string with it
x = Base64.strict_encode64(signed_key)
puts x

Adding a user with random password on debian 7 (fabric)

I want to create a new user with a random password on debian 7 with fabric.
The fabfile looks like this:
import hashlib
import string
import random
def new_user(user):
passwd = random_pass()
passwd_sha512 = "%s" % hashlib.sha512(passwd).hexdigest()
sudo("useradd %s --password %s" % (user, passwd_sha512))
def random_pass(size=30, chars=string.ascii_letters + string.punctuation):
return ''.join(random.choice(chars) for x in range(size))
Whats wrong here? Blocksize? The type of encryption ($6$ for sha512) is also missing in /etc/shadow.
I tried to add it to the parameter with --password $6$%s.
Can someone point me to the right direction?
You might find the crypt module better suited to calculating the encrypted password.
your password might contain "weird" characters, that will make the actual cmd-string not what you expect. you might have more success using subprocess.call (with string-arrays) rather than a single string.
import subprocess
subprocess.call(['sudo',
'useradd',
'--password', passwd_sha512,
user])

Encrypt string in Python

I need to encrypt a small string in Python. Is it possible to use a secret key to encrypt the string? Is there a good way to do this and achieve a reasonable encryption level using only Python libraries? Could you show me how to do this?
My knowledge about cryptography is quite basic.
Take a look at py-bcrypt. Perhaps it will meet your needs. From the web site:
py-bcrypt is a Python wrapper of OpenBSD's Blowfish password hashing code, as described in "A Future-Adaptable Password Scheme" by Niels Provos and David Mazières
KeyCzar has a nice interface and should meet your requirements. From the home page:
Keyczar is an open source cryptographic toolkit designed to make it easier and safer for devlopers to use cryptography in their applications. Keyczar supports authentication and encryption with both symmetric and asymmetric keys
crypter = Crypter.Read("/path/to/your/keys")
ciphertext = crypter.Encrypt("Secret message")
I solved this by using a lightweight XTEA library that i found on ASPN. It doesn't require any additional Python libraries and is quite simple to implement whilst acheiving a resonable encryption level.
I recently created a piece of code that does pretty much what your saying. It takes a codeword such as 'abc'gets the values (1, 2, 3) and then adds them to each letter in the word to encrypt. So if 'abc' was the codeword and 'bcd' the text to encrypt. (1+2 =3 2+3 =5 and 3+4 = 7) so the output would then be 'ceg'
codeword = input('Enter codeword : ')
codeword = codeword.replace(" ", "")
encrypt = input('Enter text to encrypt : ')
encrypt = encrypt.replace(" ", "")
j = 0
for i in codeword:
print(chr(ord(encrypt[j])+ ord(codeword[j])-96))
j+=1
Here's my code:
from cryptography.fernet import Fernet
import pyperclip
print("For this program to work, please send the file named 'pwkey' and the encrypted code to the other user.")
key = Fernet.generate_key()
file = open('pwkey', 'wb')
file.write(key)
file.close()
print('File Generated')
original = input('Enter message>>>')
message = original.encode()
f = Fernet(key)
encrypted = f.encrypt(message)
encrypted = encrypted.decode("ascii")
print('Encrypted:', encrypted)
pyperclip.copy(encrypted)
print('Please tell the other user to input the encrypted code in the Decrypt program')
print('(Code copied to Clipboard)')
print("Note: Please delete the 'pwkey' file after sending it to the other user. It
is for one-time use only.")
And decrypting
# Decrypt
from cryptography.fernet import Fernet
print("For this program to work, make sure you have the file named 'pwkey' in your Python folder and the encrypted "
"code.")
file = open('pwkey', 'rb')
key = file.read()
file.close()
print('Key Retrieved')
encrypted = input('Please input your encrypted code>>>')
encrypted = bytes(encrypted, 'utf-8')
f = Fernet(key)
decrypted = f.decrypt(encrypted)
decrypted = decrypted.decode()
print('Decrypted Message:')
print(decrypted)
print("Note: Please delete the 'pwkey' file after getting the decrypted message. It is for one-time use only.")
I would do this with the easy to use cryptocode library (https://github.com/gdavid7/cryptocode).
Encrypting and decrypting is relatively simple compared to other libraries:
## Encrypting:
>>> import cryptocode
>>> myEncryptedMessage = cryptocode.encrypt("I like trains", "password123")
>>> print(myEncryptedMessage)
M+Wykmlub0z7FhEdmA==*PvAbXRNx0SiSDHHxLsKZ5w==*ihQM/fdkgrX3G+yOItyAUQ==*QFNDmuUP1ysgo01/P2MNpg==
## Decrypting:
>>> import cryptocode
>>> myDecryptedMessage = cryptocode.decrypt("M+Wykmlub0z7FhEdmA==*PvAbXRNx0SiSDHHxLsKZ5w==*ihQM/fdkgrX3G+yOItyAUQ==*QFNDmuUP1ysgo01/P2MNpg==", "password123")
>>> print(myDecryptedMessage)
I like trains
The only large disadvantage to this library over others is that it does not have many options to change around your string compared to other cryptography libraries. But it is perfect for a lot of people who just want an abstraction, and don't need to configure custom settings.

GAE Python optimization: Django filter for language support

I have a filter that I use for lang support in my webapp. But when I publish it to GAE it keeps telling me that it the usage of CPU is to high.
I think I located the problem to my filters I use for support. I use this in my templates:
<h1>{{ "collection.header"|translate:lang }}</h1>
The filter code looks like this:
import re
from google.appengine.ext import webapp
from util import dictionary
register = webapp.template.create_template_register()
def translate(key, lang):
d = dictionary.GetDictionaryKey(lang, key)
if d == False:
return "no key for " + key
else:
return d.value
register.filter(translate)
I'm to new to Python to see what's wrong with it. Or is the the entire wrong approach?
..fredrik
Little more about what I'm trying to do: I'm trying to find away to handle language support. A user needs to be able to update text elements via an admin page. As of now I have all text elements stored in a db.model. And use a filter to get the right key based on language.
After a lot of testing I still can't get to work well enough. When published I still get error messages in the logs about to much CPU usage. A typical page has about 30-50 text elements. And according to the logs it uses about 1500ms (900ms API) for each page load. I'm starting to think this might not be the best approach?
I've tried using both memcache and indexes to get around the CPU usage. It helps a little. Should one use memcache and manually added indexes?
This is how my filter looks like:
import re
from google.appengine.ext import webapp
from google.appengine.api import memcache
from util import dictionary
register = webapp.template.create_template_register()
def translate(key, lang):
re = "no key for " + key
data = memcache.get("dictionary" + lang)
if data is None:
data = dictionary.GetDictionaryKey(lang)
memcache.add("dictionary" + lang, data, 60)
if key in data:
return data[key]
else:
return "no key for " + key
register.filter(translate)
And util.dictionary looks like this:
from google.appengine.ext import db
class DictionaryEntries(db.Model):
lang = db.StringProperty()
dkey = db.StringProperty()
value = db.TextProperty()
params = db.StringProperty()
#property
def itemid(self):
return self.key().id()
def GetDictionaryKey(lang):
entries = DictionaryEntries.all().filter("lang = ", lang)
if entries.count() > 0:
langObj = {}
for entry in entries:
langObj[entry.dkey] = entry.value
return langObj
else:
return False
Your initial question is about high cpu usage, the answer i think is simple, with GAE and databases like BigTable (non-relational) the code with entries.count() is expensive and the for entry in entrie too if you have a lot of data.
I think you must have to do a couple of things:
in your utils.py
def GetDictionaryKey(lang, key):
chache_key = 'dictionary_%s_%s' % (lang, key)
data = memcache.get(cache_key)
if not data:
entry = DictionaryEntries.all().filter("lang = ", lang).filter("value =", key).get()
if entry:
data = memcache.add(cache_key, entry.value, 60)
else:
data = 'no result for %s' % key
return data
and in your filter:
def translate(key, lang):
return dictionary.GetDictionaryKey(lang, key)
This approach is better because:
You don't make the expensive query of count
You respect the MVC pattern, because a filter is part of the Template (View in the pattern) and the method GetDictionaryKey is part of the Controler.
Besides, if you are using django i suggest you slugify your cache_key:
from django.template.defaultfilters import slugify
def GetDictionaryKey(lang, key):
chache_key = 'dictionary_%s_%s' % (slugify(lang), slugify(key))
data = memcache.get(cache_key)
if not data:
entry = DictionaryEntries.all().filter("lang = ", lang).filter("value =", key).get()
if entry:
data = memcache.add(cache_key, entry.value, 60)
else:
data = 'no result for %s' % key
return data
Have you considered switching to standard gettext methods? Gettext is a widely spread approach for internationalization and very well embedded in the Python (and the Django) world.
Some links:
Python's gettext module
Django's support for gettext with special attention to unicode
PoEdit, an editor for .po-files produced by pygettext
Your template would then look like this:
{% load i18n %}
<h1>{% trans "Header of my Collection" %}</h1>
The files for translations can be generated by manage.py:
manage.py makemessages -l fr
for generating french (fr) messages, for example.
Gettext is quite performant, so I doubt that you will experience a significant slow-down with this approach compared to your storage of the translation table in memcache. And what's more, it let's you work with "real" messages instead of abstract dictionary keys, which is, at least in my experience, ways better, if you have to read and understand the code (or if you have to find and change a certain message).

Categories

Resources