Date post: | 11-Jan-2017 |
Category: |
Data & Analytics |
Upload: | juliano-atanazio |
View: | 537 times |
Download: | 1 times |
Juliano Atanazio
PostgreSQL: How to Store Passwords SafelyPostgreSQL: How to Store Passwords Safely
2/30
About me
Juliano Atanazio
● Graduated in Computer Science for Business Management (Informática para Gestão de Negócios), FATEC Zona Sul, São Paulo – SP;
● PostgreSQL DBA;
● Linux admin;
● Instructor (PostgreSQL);
● LPIC-1, LPIC-2 Certified;
● Linux user since 2000;
● Free Software enthusiast;
● Favorite technologies: PostgreSQL, Linux, Python, Shell Script, FreeBSD, etc...;
● Headbanger :) \m/
3/30
Plain Text Passwords
● Store passwords (in plain text) of application users in database is not a good practice;
● The DBA doesn’t need and shouldn’t know the users passwords;
● If your database is invaded, the attacker will have access to user passwords;
● The unencrypted password should never be stored in the database.
4/30
Hashing and Salting
Hashing and salting is a technique to store passwords securely:
1. Generate a random string (salt) with a desired algorithm (e. g. blowfish based);
2. This salt and the plain text password are used to generate the hash password through hashing process;
3. Hash password is stored in the database;
The password must be encrypted in an irreversible way, that you can not decrypt it (theoretically).
5/30
pgcrypto
pgcrypto is a extension that provides cryptographic functions for PostgreSQL.
https://www.postgresql.org/docs/current/static/pgcrypto.html
6/30
pgcrypto
gen_salt and crypt Functions
They are specifically designed for hashing passwords. crypt() does the hashing and gen_salt() prepares algorithm parameters for it.
● gen_salt(): Generates a new random salt string for use in crypt(). The salt string also tells crypt() which algorithm to use.
● crypt(): It generate hash password through passing the password (in plain text) and salt as parameters.
7/30
Salting and Hashing: Practice
First, you need to enable the pgcrypto extension in your database.
pgcrypto is a additional supplied module (a contrib module).
Enable the pgcrypto extension:
> CREATE EXTENSION pgcrypto;
8/30
Salting and Hashing: Practice
Password Creation
9/30
Salting and Hashing: Practice
Test; generate a salt string:
salt_string ------------------------------- $2a$06$YmO7UZZZxkTWZfT8s7b/GO
> SELECT gen_salt('bf') AS salt_string;
blowfish algorithm
10/30
Salting and Hashing: Practice
Test; generate the password hash with the previous salt string:
pw_hash -------------------------------------------------------------- $2a$06$YmO7UZZZxkTWZfT8s7b/GOlO0rZpMhU674srD/dbSyplwO/clTZzi
> SELECT crypt('mypass', '$2a$06$YmO7UZZZxkTWZfT8s7b/GO') AS pw_hash;
11/30
Salting and Hashing: Practice
Test; comparison with crypt function and previous hash string:
simple_auth_test ------------------ t
> SELECT crypt('mypass', '$2a$06$YmO7UZZZxkTWZfT8s7b/GOlO0rZpMhU674srD/dbSyplwO/clTZzi') = '$2a$06$YmO7UZZZxkTWZfT8s7b/GOlO0rZpMhU674srD/dbSyplwO/clTZzi' AS simple_auth_test;
12/30
Salting and Hashing: Practice
Create the table:
> CREATE TABLE tb_user( username varchar(50) PRIMARY KEY, -- natural primary key password VARCHAR(72) NOT NULL);
13/30
Salting and Hashing: Practice
Using CTE* to INSERT new user with password creation:
* CTE = Common Table Expressionshttps://www.postgresql.org/docs/current/static/queries-with.html
> WITH x AS ( SELECT 'foo'::text AS user, '123'::text AS pw, gen_salt('bf')::text AS salt ) INSERT INTO tb_user (username, password) SELECT x.user, crypt(x.pw, x.salt) -- password hash
FROM x;
14/30
Salting and Hashing: Practice
Enable expanded display automatically (psql):
Querying username and password in the table:
username | password ----------+-------------------------------------------------------------- foo | $2a$06$RqHcf7F.nUGLkQF1fOea.OLAU0gyz/liF3dO58JWTB0oyVirzUdgK
> \x auto
> SELECT username, password FROM tb_user;
15/30
Salting and Hashing: Practice
Password Verification
16/30
Salting and Hashing: Practice
User authentication test with correct password:
acessed --------- t
True value (boolean) returned.
> SELECT crypt('123', password) = password AS acessed FROM tb_user WHERE username = 'foo';
Plain text (password) provided by user
17/30
Salting and Hashing: Practice
User authentication test with wrong password:
acessed --------- f
False value (boolean) returned.
> SELECT crypt('1234', password) = password AS acessed FROM tb_user WHERE username = 'foo';
18/30
Application Test: The Code (Python)
Continue
#!/usr/bin/env python3# _*_ encoding: utf-8 _*_
from argparse import ArgumentParserfrom getpass import getpassfrom os import systemfrom psycopg2 import connectfrom psycopg2 import Errorfrom sys import exit
19/30
Application Test: The Code (Python)
Continue
# Argument parserparser = ArgumentParser()
# Argument help strings
help_d = 'Database'help_H = 'Hostname or IP address'help_p = 'Port'help_u = 'Username'help_w = 'With password prompt'
20/30
Application Test: The Code (Python)
Continue
# Arguments creation
parser.add_argument('-d', '--database', type=str, help=help_d, action='store', metavar='dbname', dest='dbname', default='postgres')parser.add_argument('-H', '--host', type=str, help=help_H, action='store', metavar='dbserver', dest='host', default=None)parser.add_argument('-p', '--port', type=int, help=help_p, action='store', metavar='port_number', dest='port', default=5432)parser.add_argument('-u', '--user', type=str, help=help_u, action='store', metavar='username', dest='user', default='postgres')parser.add_argument('-w', '--with-pass', help=help_w, action='store_true', dest='password')
# Parsed argumentsargs = parser.parse_args()
21/30
Application Test: The Code (Python)
Continue
# Test if password prompt is requiredif args.password: args.password = getpass('Database user password: ')else: args.password = None
# Connection string variable (initially as a list)conn_str = []
# Take all provided paramaters and make the connection stringfor k, v in vars(args).items(): if v: str_tmp = "{} = '{}'".format(k, v) conn_str.append(str_tmp)conn_str = ' '.join(conn_str)
22/30
Application Test: The Code (Python)
Continue
# SQL string for PREPARE commandsql_prepare = """PREPARE q_user (text, text) ASSELECT crypt($2, password) = password
FROM tb_user WHERE username = $1;"""
# SQL string for EXECUTE commandsql_execute = "EXECUTE q_user('{}', '{}');"
# When occur authentication error...def user_pw_error(connection): print('\nError: Invalid user and password combination!') connection.close() exit(1)
23/30
Application Test: The Code (Python)
Continue
try: # Connection conn = connect(conn_str)
# Cursor creation to execute SQL commands cursor = conn.cursor()
# Execute the SQL string in database cursor.execute(sql_prepare)
# Clear Screen system('clear')
# Get user and password of the application app_user = input('\nApplication user: ') app_user_pw = getpass('Application user password: ')
# Execute the SQL string in database cursor.execute(sql_execute.format(app_user, app_user_pw))
24/30
Application Test: The Code (Python) # The result of the string SQL execution res = cursor.fetchone()
try:
# User login validation if res[0]: print('\nAcessed!') else: raise except: user_pw_error(conn)
except Error as e: print('\nAn error has occurred!') print(format(e)) exit(1)
# Close the database connectionconn.close()
25/30
Application Test: Execution
$ ./salting.py
A simple test access with correct password:
Application user: fooApplication user password:
Acessed!
26/30
Application Test: Execution
$ ./salting.py
A simple test access with wrong password:
Application user: fooApplication user password:
Error: Invalid user and password combination!
27/30
Conclusion
PostgreSQL has its own mechanisms of encryption passwords which makes it very independent of the application.
This makes it easier for the application developer, may delegate such tasks to the database, avoiding technical adjustments in the application and finally provide a robust solution independent of programming language.
28/30
Donate!
The elephant needs you!
Contribute!
:)
http://www.postgresql.org/about/donate/
29/30
Save our planet!Save our planet!
30/30
See you soon!!!
Juliano Atanazio
http://slideshare.net/spjuliano
https://speakerdeck.com/julianometalsp
https://juliano777.wordpress.com
:)