twisted cred with http digest authentication - python

I'm trying to implement twisted cred with HTTP Digest Authentication, and I'm having some difficulty. I was able to get it to work with checkPassword, but I don't want to store my passwords in the clear in the database, obviously.
I'm storing the MD5 of username:realm:password in my password database, and I'm calculating it using:
from twisted.cred._digest import calcHA1
def calc_ha1(self, password, username=None):
if username is None:
username = self.avatarId
realm = self.digest_factory.digest.authenticationRealm
return calcHA1('md5', username, realm, password, None, None)
My password checker looks like this:
def requestAvatarId(self, credentials):
username = credentials.username
try:
user = self.session.query(models.User).filter_by(username=username).one()
except NoResultFound as e:
return defer.fail(credError.UnauthorizedLogin("No such administrator"))
if credentials.checkHash(user.password_hash):
return defer.succeed(username)
else:
return defer.fail(credError.UnauthorizedLogin("Bad password"))
However, when checkHash computes HA2 and combines it with the HA1 that I have in the DB, it does not match what the browser is sending. I stepped through the checkHash code with a debugger and everything is operating as I would expect. Does anyone have ideas?
Thanks
-s

Related

Login/signup script in python using sqlite3

I have created a login/signup script in python using sqlite3 and kivy. Affter creating the database, the user can enter their username, email and password. The password is hashed before being inserted into the database. The signup function works, however I am having an error with validating the user's username and password with those in the database, where the code outputs 'login sucessful' after a wrong username/password.
def login_menu_press(self):
login = False
lusername = self.login_username.text
lpassword = self.login_password.text
print(f"Your username is {lusername}, and your password is {lpassword}.")
self.login_username.text = ""
self.login_password.text = ""
# Hashing inputted password
context = CryptContext(
schemes=["pbkdf2_sha256"],
default="pbkdf2_sha256",
# More rounds = more secure, but slower to hash
pbkdf2_sha256__default_rounds=50000
)
# Hash the inputted password
hash_pass = context.hash(lpassword)
# Connect to database
conn = sqlite3.connect('users.db')
# Create cursor
c = conn.cursor()
# Query database
command = f"SELECT * from users WHERE username='{lusername}' AND Password = '{hash_pass}';"
c.execute(command)
if not c.fetchone():
print("Login successful")
login = True
else:
print("Login failed")
I belive that the error persits after the Query database comment, any help is appreciated, thanks!
I excpeted that login sucessful outputs when the username and password is supposed to be correct, which is correct. However, the code outputs this again when the username and password is anything else random.
Edit the password inputted by the user is hashed with the same algorithm that was used to hash the password that the user signs up with, therefore the hashed values will be the same.
Hash alghoritm which you used use salt, thus for exatly same input string it will output different hash values:
# Hash the inputted password
lpassword = "Rambo"
hash_pass = context.hash(lpassword)
print(hash_pass)
hash_pass = context.hash(lpassword)
print(hash_pass)
hash_pass = context.hash(lpassword)
print(hash_pass)
$pbkdf2-sha256$50000$wniP0br3XguBsJbSuvc.xw$vmebKLd0Uvz4IF7DbX/TzCgD5p1/cmPYApaf2eKeZ4w
$pbkdf2-sha256$50000$wJjT2tv7H8NY633vXYvROg$D0CxPTfO8jIaLPHvUtawhzKMX75LqDYSNa.z3Bf/58s
$pbkdf2-sha256$50000$tZbSWmvNmdPau9eas1ZKaQ$qMVGQWPtlLvFZ/WlG.iDO7.jIHhFiirMZw0gysfqXhs
So there is no way to match previously inserted and hashed password value. You have to use hash function which will always generate same value for same input string, like SHA-256 for instance. Replace:
hash_pass = context.hash(lpassword)
with:
import hashlib # on top of main.py
hash_pass = hashlib.sha256(lpassword.encode()).hexdigest()
And comment out this part of code:
context = CryptContext(
schemes=["pbkdf2_sha256"],
default="pbkdf2_sha256",
# More rounds = more secure, but slower to hash
pbkdf2_sha256__default_rounds=50000
)
Also you you have to change your condition: if not c.fetchone(): to if c.fetchone():

Unable to authenitcate with python minds api

I'm trying to access the social site, minds.com via this python api by by installing the module locally with python3 setup.py install && pipenv run python and following the instructions to log in.
However, I get this error message when trying to authenticate:
(For some reason, Stackoverflow doesn't allow me to post the python error-log becuse it isn't indented properly, so here it is https://pastebin.com/0sWa1hmY)
The code, which seems to be called from the python api looks like this:
minds/api.py
# -*- coding: utf-8 -*-
from pprint import pprint
from requests.utils import dict_from_cookiejar, cookiejar_from_dict
from minds.connections import XSRFSession
from minds.exceptions import AuthenticationError
from minds.profile import Profile
from minds.utils import add_url_kwargs
from minds.endpoints import *
from minds.sections import NewsfeedAPI, ChannelAPI, NotificationsAPI,
PostingAPI, InteractAPI
class Minds(NewsfeedAPI, ChannelAPI, NotificationsAPI, PostingAPI, ...):
_xsrf_retries = 5
def __init__(self, profile: Profile = None, login=True):
self.con = XSRFSession()
self.profile = profile
if self.profile:
if profile.cookie:
self.con.cookies = cookiejar_from_dict(profile.cookie)
if profile.proxy:
self.con.proxies = {'https': profile.proxy, 'http':\
profile.proxy}
self._establish_xsrf()
if self.profile and login and not self.is_authenticated:
if profile.username and profile.password:
self.authenticate(profile.username, profile.password)
(...)
def authenticate(self, username, password, save_profile=True) -> dict:
"""
Authenticate current instance with given user
:param: save_profile: whether to save profile locally
"""
auth = {
'username': username,
'password': password
}
resp = self.con.post(AUTHENTICATE_URL, json=auth)
self.user = resp.json()
if self.user['status'] == 'failed':
raise AuthenticationError("Couldn't log in with the ...")
self.guid = self.user['user']['guid']
if save_profile:
Profile(
username=username,
password=password,
cookie=self.get_cookies(),
proxy=self.con.proxies.get('https'),
).save()
return resp.json()
The python API doesn't seem to be maintained, but I think minds.com newly uses jsonwebtokens for authentication. Is the something missing from the api to be jwt-enabled?
After compare to browser request again and again. The reason why occurred error is you gave a wrong content-type, but actually you need to send as json data (it seems website is making joke). So you need to specify headers["content-type"] = "text/plain". Otherwise website will response 500.
Note: To avoid broad discussion, i can only answer this error.
Change the code to below, but there is only one line differs from source code :).
def authenticate(self, username, password, save_profile=True) -> dict:
"""
Authenticate current instance with given user
:param: save_profile: whether to save profile locally
"""
auth = {
'username': username,
'password': password
}
self.con.headers["content-type"] = "text/plain" ####
resp = self.con.post(AUTHENTICATE_URL, json=auth)
self.user = resp.json()
if self.user['status'] == 'failed':
raise AuthenticationError("Couldn't log in with the given credentials")
self.guid = self.user['user']['guid']
if save_profile:
Profile(
username=username,
password=password,
cookie=self.get_cookies(),
proxy=self.con.proxies.get('https'),
).save()
return resp.json()

How do i signin to a database using PyQt and QtSql

I have a PyQt4 application that offers signup and sign in. The signup works well that is, the credentials are store in the database as intended. The problem is with sign-in where by it accepts any credentials including those that don't exist in the database.Below is the code am using.
self.database1 = QtSql.QSqlDatabase().addDatabase('QMYSQL')
self.database1.setHostName('localhost')
self.database1.setDatabaseName('database')
self.database1.setUserName('root')
self.database1.setPassword('')
if self.database1.open():
print('Successful')
else:
print(self.database1.lastError().text())
username = self.userName1.text()
password = self.passwordSlot.text()
query = QtSql.QSqlQuery(self.database1)
credentials = query.prepare("SELECT * FROM credentials WHERE username = ? and password = ?")
if credentials:
query.addBindValue(username)
query.addBindValue(password)
query.exec_()
self.database1.close()
print('Successfully logged in')
else:
print('Failed')
prepare() returns a boolean value that indicates whether the statement is correct or not, so it can not indicate if the credentials match, since the correct statement will always be true, what you should know if the information is in the database is that the order returns at least one element, and we can do that with first():
self.database1 = QtSql.QSqlDatabase().addDatabase('QMYSQL')
self.database1.setHostName('localhost')
self.database1.setDatabaseName('database')
self.database1.setUserName('root')
self.database1.setPassword('')
if self.database1.open():
print('Successful')
else:
print(self.database1.lastError().text())
username = self.userName1.text()
password = self.passwordSlot.text()
query = QtSql.QSqlQuery(self.database1)
is_valid_query = query.prepare("SELECT * FROM credentials WHERE username = ? and password = ?")
if is_valid_query:
query.addBindValue(username)
query.addBindValue(password)
if query.exec_():
if query.first():
print('Successfully logged in')
else:
print('Failed')
else:
print(query.lastError().text())
self.database1.close()

Changing Active Directory user password in Python 3.x

I am trying to make a Python script that will open an LDAP connection to a server running AD, take a search entry (in this case a name), search for that entry and change that users password to a randomly generated password (as well as set the option to change password on logon) and then send them an automated secure email containing the new temporary password.
So far I have been able to connect to the server, and search for a single DN which returns. The temporary password is being generated, and an email is being sent (although the password is not hashed, and the email is not secure yet). However, I cannot find any information on where to go from here.
I have found Change windows user password with python however I see that this does not play well with AD, and the other LDAP in Python documentation I have been finding seems to be outdated from 2.x and no longer works. The documentation for ldap3 (https://media.readthedocs.org/pdf/ldap3/stable/ldap3.pdf) also doesnt seem to really mention anything for it, and exhaustive Googling has been fruitless. I am new to this kind of programming having only low level or academic knowledge previously, so this has been a bit frustrating but Python is my strongest language.
----------------EDITED CODE TO CURRENT STATUS-----------------------
#Takes input for name which will be used for search criterion
zid = input("ZID: ")
zid = str(zid).lower()
print(zid)
#Binds session to the server and opens a connection
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com' %zid, password = "<something>", auto_bind=True)
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com' %zid, password = "<something>", auto_bind=True)
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
try:
server = ldap3.Server('ldap://<IP_Address>', get_info=all)
conn = ldap3.Connection(server, '%s#something.com', password = "<something>", auto_bind=True) %zid
print("Successfully bound to server.\n")
except:
print("Unsucessful initialization of <IP_Address>")
sys.exit(0)
#Searches and prints LDAP entries
try:
base_dn = 'DC=<something>,DC=<something>,DC=<something>,DC=<something>,DC=com'
zid_filter = '(sAMAccountName=%s)' %zid
conn.search(base_dn, zid_filter, attributes=['mail'])
#i.e. "DN: CN=<First Last>,OU=<something>, DC= <something>
user_dn = str(conn.entries)
#i.e. "CN=<First Last>"
front = user_dn.find('C')
back = user_dn.find(',')
user_cn = user_dn[front:back]
#i.e. "<First Last>"
display_name = user_cn[3:]
#i.e. "first.last#<something>.com"
raw_email = str(conn.entries)
front = raw_email.find('mail: ')
back = raw_email.find('#<something>.com')
user_email = raw_email[front + 6:back] + '#<something>.com'
except:
print("Could not search entries")
#Generates random 12 digit alpha-numeric password
try:
new_password = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(12))
print(new_password)
print("New password successfully generated")
except:
print("New password could not be generated")
#Set and replace AD Password
try:
conn.extend.microsoft.modify_password(user_dn, None, new_password)
print ("Active Directory password was set successfully!")
except:
print('Error setting AD password')
sys.exit(0)
Any suggestions on how to get/set the user password and hash the password for security purposes during this whole ordeal? For the email I imagine I can force it to use HTTPS and that would be sufficient, but the connection to the server passing the new_password to I would like to secure.
ldap3 contains a specific method for changing AD password, just add the following after you generated a new password:
dn = conn.entries[0].entry_get_dn() # supposing you got back a single entry
conn.extend.microsoft.modify_password(dn, None, new_password)
This should properly encode the password and store it in AD.
This code is working with Windows 2012 R2 AD:
First, install latest ldap3:
sudo pip3 install ldap
#!/usr/bin/python3
import ldap3
SERVER='127.0.0.1'
BASEDN="DC=domain,DC=com"
USER="user_domain_login_name#domain.com"
CURREENTPWD="current_password"
NEWPWD="new_password"
SEARCHFILTER='(&(userPrincipalName='+USER+')(objectClass=person))'
USER_DN=""
USER_CN=""
ldap_server = ldap3.Server(SERVER, get_info=ldap3.ALL)
conn = ldap3.Connection(ldap_server, USER, CURREENTPWD, auto_bind=True)
conn.start_tls()
print(conn)
conn.search(search_base = BASEDN,
search_filter = SEARCHFILTER,
search_scope = ldap3.SUBTREE,
attributes = ['cn', 'givenName', 'userPrincipalName'],
paged_size = 5)
for entry in conn.response:
if entry.get("dn") and entry.get("attributes"):
if entry.get("attributes").get("userPrincipalName"):
if entry.get("attributes").get("userPrincipalName") == USER:
USER_DN=entry.get("dn")
USER_CN=entry.get("attributes").get("cn")
print("Found user:", USER_CN)
if USER_DN:
print(USER_DN)
print(ldap3.extend.microsoft.modifyPassword.ad_modify_password(conn, USER_DN, NEWPWD, CURREENTPWD, controls=None))
else:
print("User DN is missing!")

Cache user remote credentials for command line tool

I'm designing a Python command line tool that will essentially be a wrapper for a few REST APIs. The general idea is so that the user can make remote changes without having to leave the terminal.
The only detail I'm not really sure about is how to cache the user credentials so that they don't have to enter username and password every time they make a remote call. I'm worried about leaving user credentials exposed for long periods of time when they're not using the tool. Is there a typical way to do this without writing a file and making a thread that destroys the file after a certain time has passed?
I recommend using the keyring package from pypi, then you can wrap it up with some utility functions:
SERVICE_NAME = 'confluence_api'
def get_login_cli(username = None, prompt = False):
'''
Get the password for the username out of the keyring. If the password
isn't found in the keyring, ask for it from the command line.
'''
disp_username = False
if username is None or prompt:
username = getpass.getuser()
disp_username = True
passwd = keyring.get_password(SERVICE_NAME, username)
if passwd is None or prompt:
if disp_username:
print 'login: %s' % username
passwd = getpass.getpass()
set_password(username, passwd)
return (username, passwd)
def set_password(username, passwd):
'''
Writes the password to the keyring.
'''
keyring.set_password(SERVICE_NAME, username, passwd)
And then your run-time script can call it like so:
username, passwd = get_login_cli(username, **kwargs)
print("username = %s" % (username))
So after logging in, the password is cached and you won't be prompted the second time. keyring uses the native platform's keyring to store the credentials, and I believe after N time has passed, you will be prompted again, but you'll have to read the docs on keyring to know what N is.

Categories

Resources