How to Use the credcheck Extension in PostgreSQL 18

PostgreSQL provides strong authentication and role management features out of the box, but by default, it does not enforce password reuse rules or automatically track repeated authentication failures. This gap becomes critical in production systems where database credentials are shared across applications, automation scripts, and teams.

The credcheck extension fills this gap by adding password history enforcement and authentication failure tracking directly inside PostgreSQL.

In this blog, we will explore:

  • What credcheck is and why it matters
  • How the extension works internally
  • How to build and install it from source
  • How to configure PostgreSQL correctly
  • Examples of all major functions
  • Use cases

This guide is written for DBAs, backend engineers, and anyone interested in PostgreSQL internals and security extensions.

What is the Credcheck Extension?

Credcheck is a PostgreSQL C extension developed by HexaCluster that enhances database security by enforcing:

  1. Password history tracking – Prevents users from reusing old passwords
  2. Authentication failure tracking – Detects repeated login failures and bans roles

Unlike application-level controls, credcheck works inside PostgreSQL itself, making it effective for all clients (psql, apps, cron jobs, ORMs, etc.).

GitHub repository:

https://github.com/HexaCluster/credcheck

Why CredCheck is important

By default, PostgreSQL:

  • Allows password reuse
  • Does not store password change history
  • Does not track authentication failure counts

This can lead to:

  • Weak password hygiene
  • Brute-force login attempts
  • Compliance issues

Credcheck addresses these problems without external tooling.

Extension Architecture Overview

Credcheck consists of:

  • C code (credcheck.c)
    • Hooks into PostgreSQL authentication and password change logic
  • SQL files (credcheck--X.Y.Z.sql)
    • Define types, views, and SQL-level functions
  • Event trigger support
    • Tracks role changes and password updates

Because it hooks into authentication, the extension must be loaded at server startup.

Building and Installing credcheck from Source

You downloaded version 4.3 and built it against PostgreSQL 18.

Directory Structure

credcheck-4.3/
+-- credcheck.c
+-- credcheck.control
+-- credcheck--4.3.0.sql
+-- updates/
+-- event_trigger.sql
+-- Makefile

Download the source code of the credcheck extension

git clone https://github.com/HexaCluster/credcheck.git

Compile the Extension

make USE_PGXS=1 PG_CONFIG=/usr/lib/postgresql/18/bin/pg_config

Install the Extension

sudo make USE_PGXS=1 PG_CONFIG=/usr/lib/postgresql/18/bin/pg_config install

This installs:

  • credcheck.so> PostgreSQL library directory
  • SQL and control files> extension directory

Creating the Extension

Connect to PostgreSQL:

psql -p 5433

Create the extension:

CREATE EXTENSION credcheck;

Verify installation:

\dx+ credcheck

You should see:

  • Functions
  • Custom types
  • Views

Example:

postgres=# \dx+ credcheck 
                   Objects in extension "credcheck"
                          Object description                           
-----------------------------------------------------------------------
 function pg_banned_role()
 function pg_banned_role_reset()
 function pg_banned_role_reset(name)
 function pg_password_history()
 function pg_password_history_reset()
 function pg_password_history_reset(name)
 function pg_password_history_timestamp(name,timestamp with time zone)
 type pg_banned_role
 type pg_banned_role[]
 type pg_password_history
 type pg_password_history[]
 view pg_banned_role
 view pg_password_history
(13 rows)

Required Configuration: shared_preload_libraries

If you try to use the functions immediately, you will see errors like:

ERROR: credcheck must be loaded via shared_preload_libraries

This is expected.

Edit postgresql.conf:

You can view the path of postgres.conf file like this

postgres=# show config_file ;
               config_file               
-----------------------------------------
 /etc/postgresql/18/main/postgresql.conf
(1 row)

Go to the parameter named shared_preload_libraries

shared_preload_libraries = 'credcheck'

Restart PostgreSQL:

sudo systemctl restart postgresql

This step is mandatory because credcheck hooks into authentication.

Objects Created by credcheck

After successful loading:

Views

  • pg_banned_role
  • pg_password_history

Functions

  • pg_banned_role()
  • pg_banned_role_reset()
  • pg_banned_role_reset(name)
  • pg_password_history()
  • pg_password_history_reset()
  • pg_password_history_reset(name)
  • pg_password_history_timestamp(name, timestamptz)

Practical Examples: Using CredCheck in Real Life

This section demonstrates simple and effective examples to understand how the credcheck extension works in daily PostgreSQL administration.

1. Authentication Failure Tracking (Login Ban)

Purpose:

You want PostgreSQL to automatically block users after repeated failed login attempts.

Step 1: Enable Authentication Failure Limit

ALTER SYSTEM SET credcheck.max_auth_failure = 2;
SELECT pg_reload_conf();

Meaning

A user will be banned after 2 failed login attempts.

Step 2: Trigger Login Failures

psql -U postgres -p 5433

Enter a wrong password twice.

Step 3: User Gets Banned

On the next attempt, PostgreSQL blocks the user:

FATAL: rejecting connection, user "postgres" has been banned

Example:

postgres@cybrosys:/home/cybrosys$ psql -p 5433 -U postgres
Password for user postgres: 
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5433" failed: FATAL:  password authentication failed for user "postgres"
postgres@cybrosys:/home/cybrosys$ psql -p 5433 -U postgres
Password for user postgres: 
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5433" failed: FATAL:  rejecting connection, user 'postgres' has been banned

Step 4: Check Banned Users

SELECT * FROM pg_banned_role;

Shows:

  • Which user is banned
  • Failure count
  • Ban timestamp

This is useful for security audits and monitoring.

Example:

postgres=# select * from pg_banned_role ;
   roleid    | failure_count |        banned_date         
-------------+---------------+----------------------------
 postgres    |             2 | 2026-01-01 17:39:29.518929
(1 row)

Step 5: Unban a Specific User

SELECT pg_banned_role_reset('postgres');

Step 6: Unban All Users

SELECT pg_banned_role_reset();

Best Use Case

  • Emergency access recovery
  • Incident response handling

2. Adding Delay to Failed Logins (Anti-Brute Force)

Purpose:

You want to slow down brute-force attacks.

Enable Authentication Delay

ALTER SYSTEM SET credcheck.auth_delay_ms = 3000;
SELECT pg_reload_conf();

Every failed login attempt pauses for 3 seconds before returning an error.

This makes automated attacks extremely slow.

3. Password Strength Enforcement (Basic Example)

Purpose:

You want strong passwords with:

  • Minimum length
  • At least one digit
  • At least one special character

Configure Password Rules

ALTER SYSTEM SET credcheck.password_min_length = 8;
ALTER SYSTEM SET credcheck.password_min_digit = 1;
ALTER SYSTEM SET credcheck.password_min_special = 1;
SELECT pg_reload_conf();

Weak Password (Rejected)

CREATE USER weak_user PASSWORD 'password';

Result:

postgres=# CREATE USER weak_user PASSWORD 'password';
ERROR:  password does not contain the configured credcheck.password_min_digit characters (1)

Strong Password (Accepted)

CREATE USER strong_user PASSWORD 'Pass@1234';

User created successfully.

4. Preventing Password Reuse

Purpose:

Users should not reuse their last 2 passwords.

Enable Password Reuse Policy

ALTER SYSTEM SET credcheck.password_reuse_history = 2;
SELECT pg_reload_conf();

Password Change History

CREATE USER reuse_user PASSWORD 'Abc@1234';
ALTER USER reuse_user PASSWORD 'Xyz@5678';

Reusing Old Password (Blocked)

ALTER USER reuse_user PASSWORD 'Abc@1234';

postgres=# ALTER USER reuse_user PASSWORD 'Abc@1234';
ERROR:  Cannot use this credential following the password reuse policy

View Password History

SELECT rolename, password_date
FROM pg_password_history
WHERE rolename = 'reuse_user';

Result:

  rolename  |          password_date           
------------+----------------------------------
 reuse_user | 2026-01-01 23:17:30.996003+05:30
 reuse_user | 2026-01-01 23:17:31.003545+05:30
(2 rows)

Purpose

  • Auditing password changes
  • Compliance verification

5. Reset Password History (Admin Action)

Reset for One User

SELECT pg_password_history_reset('reuse_user');

Reset for All Users

SELECT pg_password_history_reset();

Useful When

  • Migrating environments
  • Security policy reset
  • Emergency recovery

6. Excluding Users from Policies (Whitelist)

Purpose:

Application users should not be blocked.

Add to Whitelist

ALTER SYSTEM SET credcheck.whitelist = 'app_user,service_user';
SELECT pg_reload_conf();

Effect

  • Password rules ignored
  • Login ban disabled

7. Monitoring with Functions

Get Banned Users (Programmatically)

SELECT pg_banned_role();

Example:

             pg_banned_role             
----------------------------------------
 (16388,3,"2026-01-01 17:45:26.916198")
(1 row)

Get Password History Summary

SELECT pg_password_history();

Example:

                                                  pg_password_history                                                   
------------------------------------------------------------------------------------------------------------------------
 (first_login_user,"2026-01-01 23:20:55.764843+05:30",4c47cbbcb95715601d98fad489c033c7ef7b6b96afea81b74e876b00143a0117)
 (expiry_user,"2026-01-01 23:20:04.908903+05:30",f3f0f383bfdf049e0826dd4f43c8863828711aea11b3a9602ca4d15ab61f52be)
(2 rows)

These functions are ideal for:

  • Cron jobs
  • Monitoring dashboards
  • Security alerts

Conclusion

Using credcheck, PostgreSQL can now:

  • Block brute-force login attempts
  • Enforce strong password policies
  • Prevent password reuse
  • Force password expiration
  • Require password change on first login
  • Centralize credential security inside the database

All without modifying application code.

Database credential security is often treated as an application concern, but relying only on application-level checks leaves gaps that attackers can exploit. PostgreSQL intentionally keeps authentication rules simple, which makes it flexible but also shifts responsibility to administrators. The credcheck extension addresses this gap by bringing strong, configurable credential policies directly into the database layer.

By using credcheck, PostgreSQL can enforce password complexity, prevent password reuse, control password lifetime, and block users after repeated authentication failures. These protections apply uniformly to all connection methods, ensuring consistent behavior regardless of how users or applications connect. Features such as authentication delays, password history tracking, and forced password changes significantly reduce the risk of brute-force attacks and weak credential practices.

For production systems where security, compliance, and operational reliability matter, credcheck provides a practical and effective enhancement to PostgreSQL’s native capabilities. When combined with proper role management and network-level controls, it helps establish a strong foundation for database security without requiring changes to application code.

whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message