When working with PostgreSQL, authentication security is an important concern. One useful extension that helps mitigate brute-force attacks is the auth_delay module. This module introduces an artificial delay after failed authentication attempts, making repeated login attempts slower and less effective.
auth_delay is a PostgreSQL module that adds a delay (in milliseconds) after failed authentication attempts. This helps in slowing down attackers who try to guess passwords through repeated login attempts.
Load the Extension
First, connect to PostgreSQL and load the extension:
sudo su postgres
psql
Then run:
LOAD 'auth_delay';
Verify available parameters:
show auth_delay.milliseconds;
Output:
auth_delay.milliseconds
-------------------------
0
(1 row)
By default, the delay is 0, meaning no delay is applied.
Enable auth_delay via shared_preload_libraries
To properly use the module, it must be added to shared_preload_libraries.
Check current value:
show shared_preload_libraries;
Check the postgresql configuration file path
Show config_file:
You get a location like this
config_file
-----------------------------------------
/etc/postgresql/18/main/postgresql.conf
(1 row)
Edit the PostgreSQL configuration file:
sudo nano /etc/postgresql/18/main/postgresql.conf
Update:
shared_preload_libraries = 'auth_delay'
Restart PostgreSQL:
sudo systemctl restart postgresql
Configure Delay Time
Set the delay duration:
ALTER SYSTEM SET auth_delay.milliseconds = 10000;
Reload configuration:
SELECT pg_reload_conf();
Verify:
show auth_delay.milliseconds;
Output:
auth_delay.milliseconds
-------------------------
10s
(1 row)
Now PostgreSQL will delay failed authentication attempts by 10 seconds.
Verify Authentication Method
To test auth_delay, password-based authentication must be enabled.
Check current rules:
select * from pg_hba_file_rules;
Example output:
rule_number | file_name | line_number | type | database | user_name | address | netmask | auth_method | options | error
-------------+-------------------------------------+-------------+-------+---------------+-----------+-----------+-----------------------------------------+-------------+---------+-------
1 | /etc/postgresql/18/main/pg_hba.conf | 121 | local | {all} | {all} | | | peer | |
2 | /etc/postgresql/18/main/pg_hba.conf | 123 | host | {all} | {all} | 127.0.0.1 | 255.255.255.255 | trust | |
3 | /etc/postgresql/18/main/pg_hba.conf | 125 | host | {all} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | trust | |
4 | /etc/postgresql/18/main/pg_hba.conf | 128 | local | {replication} | {all} | | | peer | |
5 | /etc/postgresql/18/main/pg_hba.conf | 129 | host | {replication} | {all} | 127.0.0.1 | 255.255.255.255 | trust | |
6 | /etc/postgresql/18/main/pg_hba.conf | 130 | host | {replication} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | trust | |
(6 rows)
Here, authentication methods like peer and trust do not require passwords, so auth_delay will not be triggered.
Set Password for postgres User
Set a password:
ALTER USER postgres WITH PASSWORD 'cool';
Check the location of the pg_hba.conf file of postgres
show hba_fie;
You get a result like this
hba_file
-------------------------------------
/etc/postgresql/18/main/pg_hba.conf
(1 row)
Edit pg_hba.conf:
sudo nano /etc/postgresql/18/main/pg_hba.conf
Add this line
# Database administrative login by Unix domain socket
# TYPE DATABASE USER ADDRESS METHOD
local all postgres md5
Restart PostgreSQL:
sudo systemctl restart postgresql
Verify changes:
select * from pg_hba_file_rules;
Updated output:
rule_number | file_name | line_number | type | database | user_name | address | netmask | auth_method | options | error
-------------+-------------------------------------+-------------+-------+---------------+------------+-----------+-----------------------------------------+-------------+---------+-------
1 | /etc/postgresql/18/main/pg_hba.conf | 119 | local | {all} | {postgres} | | | md5 | |
2 | /etc/postgresql/18/main/pg_hba.conf | 121 | local | {all} | {all} | | | peer | |
3 | /etc/postgresql/18/main/pg_hba.conf | 123 | host | {all} | {all} | 127.0.0.1 | 255.255.255.255 | md5 | |
4 | /etc/postgresql/18/main/pg_hba.conf | 125 | host | {all} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | trust | |
5 | /etc/postgresql/18/main/pg_hba.conf | 128 | local | {replication} | {all} | | | peer | |
6 | /etc/postgresql/18/main/pg_hba.conf | 129 | host | {replication} | {all} | 127.0.0.1 | 255.255.255.255 | trust | |
7 | /etc/postgresql/18/main/pg_hba.conf | 130 | host | {replication} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | trust | |
(7 rows)
Test auth_delay
Now try connecting:
psql -U postgres
Enter a wrong password
Password for user postgres:
You will notice a delay of 10 seconds before the authentication fails.
This confirms that auth_delay is working correctly.
Understanding pre_auth_delay
PostgreSQL also provides another parameter:
\dconfig+ pre_auth_delay
Output:
Parameter | Value | Type | Context
-----------------+-------+---------+---------
pre_auth_delay | 0 | integer | sighup
Set it:
ALTER SYSTEM SET pre_auth_delay = '10s';
Reload configuration:
SELECT pg_reload_conf();
Verify:
\dconfig+ pre_auth_delay
Output:
List of configuration parameters
Parameter | Value | Type | Context | Access privileges
----------------+-------+---------+---------+-------------------
pre_auth_delay | 10s | integer | sighup |
(1 row)
Test pre_auth_delay
Open a new terminal and run:
psql -U postgres
Observation:
- The password prompt itself is delayed by 10 seconds.
- Even with the correct password, authentication completes only after 10 seconds.
auth_delay - Delays only after failed authentication
pre_auth_delay - Delays before authentication begins
The auth_delay module is a simple yet effective way to improve PostgreSQL security by slowing down brute-force attacks. By combining it with proper authentication methods like md5, you can ensure that login attempts are both secure and controlled.
Additionally, pre_auth_delay provides another layer of control by delaying all authentication attempts, regardless of success or failure.
Using these configurations wisely can significantly strengthen your database security posture without requiring complex changes.