postgresql: how to store passwords safely
TRANSCRIPT
![Page 1: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/1.jpg)
Juliano Atanazio
PostgreSQL: How to Store Passwords SafelyPostgreSQL: How to Store Passwords Safely
![Page 2: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/2.jpg)
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/
![Page 3: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/3.jpg)
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.
![Page 4: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/4.jpg)
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).
![Page 5: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/5.jpg)
5/30
pgcrypto
pgcrypto is a extension that provides cryptographic functions for PostgreSQL.
https://www.postgresql.org/docs/current/static/pgcrypto.html
![Page 6: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/6.jpg)
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.
![Page 7: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/7.jpg)
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;
![Page 8: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/8.jpg)
8/30
Salting and Hashing: Practice
Password Creation
![Page 9: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/9.jpg)
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
![Page 10: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/10.jpg)
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;
![Page 11: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/11.jpg)
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;
![Page 12: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/12.jpg)
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);
![Page 13: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/13.jpg)
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;
![Page 14: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/14.jpg)
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;
![Page 15: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/15.jpg)
15/30
Salting and Hashing: Practice
Password Verification
![Page 16: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/16.jpg)
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
![Page 17: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/17.jpg)
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';
![Page 18: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/18.jpg)
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
![Page 19: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/19.jpg)
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'
![Page 20: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/20.jpg)
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()
![Page 21: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/21.jpg)
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)
![Page 22: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/22.jpg)
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)
![Page 23: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/23.jpg)
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))
![Page 24: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/24.jpg)
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()
![Page 25: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/25.jpg)
25/30
Application Test: Execution
$ ./salting.py
A simple test access with correct password:
Application user: fooApplication user password:
Acessed!
![Page 26: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/26.jpg)
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!
![Page 27: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/27.jpg)
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.
![Page 28: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/28.jpg)
28/30
Donate!
The elephant needs you!
Contribute!
:)
http://www.postgresql.org/about/donate/
![Page 29: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/29.jpg)
29/30
Save our planet!Save our planet!
![Page 30: PostgreSQL: How to Store Passwords Safely](https://reader033.vdocument.in/reader033/viewer/2022052405/5875d2791a28ab8f438b580d/html5/thumbnails/30.jpg)
30/30
See you soon!!!
Juliano Atanazio
http://slideshare.net/spjuliano
https://speakerdeck.com/julianometalsp
https://juliano777.wordpress.com
:)