Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

PostgreSQL: How to Store Passwords Safely


Published on

Many people doesn't know about passwords stored in database...
What's the right and safe way to do?
OK! In this short presentation teaches about this.
The pgcrypto extension provides functions to ease hashing and salting passwords.
A Python script was used as example.

Published in: Data & Analytics
  • Login to see the comments

PostgreSQL: How to Store Passwords Safely

  1. 1. Juliano Atanazio PostgreSQL: How to Store Passwords SafelyPostgreSQL: How to Store Passwords Safely
  2. 2. 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. 3. 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. 4. 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. 5. 5/30 pgcrypto pgcrypto is a extension that provides cryptographic functions for PostgreSQL.
  6. 6. 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. 7. 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. 8. 8/30 Salting and Hashing: Practice Password Creation
  9. 9. 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. 10. 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. 11. 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. 12. 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. 13. 13/30 Salting and Hashing: Practice Using CTE* to INSERT new user with password creation: * CTE = Common Table Expressions > 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.salt) -- password hash FROM x;
  14. 14. 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. 15. 15/30 Salting and Hashing: Practice Password Verification
  16. 16. 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. 17. 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. 18. 18/30 Application Test: The Code (Python) Continue #!/usr/bin/env python3 # _*_ encoding: utf-8 _*_ from argparse import ArgumentParser from getpass import getpass from os import system from psycopg2 import connect from psycopg2 import Error from sys import exit
  19. 19. 19/30 Application Test: The Code (Python) Continue # Argument parser parser = 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. 20. 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 arguments args = parser.parse_args()
  21. 21. 21/30 Application Test: The Code (Python) Continue # Test if password prompt is required if 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 string for k, v in vars(args).items(): if v: str_tmp = "{} = '{}'".format(k, v) conn_str.append(str_tmp) conn_str = ' '.join(conn_str)
  22. 22. 22/30 Application Test: The Code (Python) Continue # SQL string for PREPARE command sql_prepare = """ PREPARE q_user (text, text) AS SELECT crypt($2, password) = password FROM tb_user WHERE username = $1; """ # SQL string for EXECUTE command sql_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. 23. 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. 24. 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 connection conn.close()
  25. 25. 25/30 Application Test: Execution $ ./ A simple test access with correct password: Application user: foo Application user password: Acessed!
  26. 26. 26/30 Application Test: Execution $ ./ A simple test access with wrong password: Application user: foo Application user password: Error: Invalid user and password combination!
  27. 27. 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. 28. 28/30 Donate! The elephant needs you! Contribute! :)
  29. 29. 29/30 Save our planet!Save our planet!
  30. 30. 30/30 See you soon!!! Juliano Atanazio :)