34. PostgreSQL Security

PostgreSQL has a rich and flexible permissions system, with the ability to parcel out particular privileges to particular roles, and provide users with the powers of one or more of those roles.

In addition, the PostgreSQL server can use multiple different systems to authenticate users. This means that the database can use the same authentication infrastructure as other architecture components, simplifying password management.

34.1. Users and Roles

In this chapter we will create two useful production users:

  • A read-only user for use in a publishing application.

  • A read/write user for use by a developer in building a software or analyzing data.

Rather than creating users and granting them the necessary powers, we will create two roles with the right powers and then create two users and add them to the appropriate roles. That way we can easily reuse the roles when we create further users.

34.1.1. Creating Roles

A role is a user and a user is a role. The only difference is that a "user" can be said to be a role with the "login" privilege.

So functionally, the two SQL statements below are the same, they both create a "role with the login privilege", which is to say, a "user".


34.1.2. Read-only Users

Our read-only user will be for a web application to use to query the nyc_streets table.

The application will have specific access to the nyc_streets table, but will inherit the necessary system access for PostGIS operations from the postgis_reader role.

-- A user account for the web app
-- Web app needs access to specific data tables
GRANT SELECT ON nyc_streets TO app1;

-- A generic role for access to PostGIS functionality
CREATE ROLE postgis_reader INHERIT;
-- Give that role to the web app
GRANT postgis_reader TO app1;

Now, when we login as app1, we can select rows from the nyc_streets table. However, we cannot run an ST_Transform call! Why not?

-- This works!
SELECT * FROM nyc_streets LIMIT 1;

-- This doesn't work!
SELECT ST_AsText(ST_Transform(geom, 4326))
  FROM nyc_streets LIMIT 1;
ERROR:  permission denied for relation spatial_ref_sys
CONTEXT:  SQL statement "SELECT proj4text FROM spatial_ref_sys WHERE srid = 4326 LIMIT 1"

The answer is contained in the error statement. Though our app1 user can view the contents of the nyc_streets table fine, it cannot view the contents of spatial_ref_sys, so the call to ST_Transform fails.

So, we need to also grant the postgis_reader role read access to all the PostGIS metadata tables:

GRANT SELECT ON geometry_columns TO postgis_reader;
GRANT SELECT ON geography_columns TO postgis_reader;
GRANT SELECT ON spatial_ref_sys TO postgis_reader;

Now we have a nice generic postgis_reader role we can apply to any user that need to read from PostGIS tables.

-- This works now!
SELECT ST_AsText(ST_Transform(geom, 4326))
  FROM nyc_streets LIMIT 1;

34.1.3. Read/write Users

There are two kinds of read/write scenarios we need to consider:

  • Web applications and others that need to write to existing data tables.

  • Developers or analysts that need to create new tables and geometry columns as part of their work.

For web applications that require write access to data tables, we just need to grant extra permissions to the tables themselves, and we can continue to use the postgis_reader role.

-- Add insert/update/delete abilities to our web application

These kinds of permissions would be required for a read/write WFS service, for example.

For developers and analysts, a little more access is needed to the main PostGIS metadata tables. We will need a postgis_writer role that can edit the PostGIS metadata tables!

-- Make a postgis writer role
CREATE ROLE postgis_writer;

-- Start by giving it the postgis_reader powers
GRANT postgis_reader TO postgis_writer;

-- Add insert/update/delete powers for the PostGIS tables
GRANT INSERT,UPDATE,DELETE ON spatial_ref_sys TO postgis_writer;

-- Make app1 a PostGIS writer to see if it works!
GRANT postgis_writer TO app1;

Now try the table creation SQL above as the app1 user and see how it goes!

34.2. Encryption

PostgreSQL provides a lot of encryption facilities, many of them optional, some of them on by default.

  • By default, all passwords are MD5 encrypted. The client/server handshake double encrypts the MD5 password to prevent re-use of the hash by anyone who intercepts the password.

  • SSL connections are optionally available between the client and server, to encrypt all data and login information. SSL certificate authentication is also available when SSL connections are used.

  • Columns inside the database can be encrypted using the pgcrypto module, which includes hashing algorithms, direct ciphers (blowfish, aes) and both public key and symmetric PGP encryption.

34.2.1. SSL Connections

In order to use SSL connections, both your client and server must support SSL.

  • First, turn off PostgreSQL, since activating SSL will require a restart.

  • Next, we acquire or generate an SSL certificate and key. The certificate will need to have no passphrase on it, or the database server won't be able to start up. You can generate a self-signed key as follows:

    # Create a new certificate, filling out the certification info as prompted
    openssl req -new -text -out server.req
    # Strip the passphrase from the certificate
    openssl rsa -in privkey.pem -out server.key
    # Convert the certificate into a self-signed cert
    openssl req -x509 -in server.req -text -key server.key -out server.crt
    # Set the permission of the key to private read/write
    chmod og-rwx server.key
  • Copy the server.crt and server.key into the PostgreSQL data directory.

  • Enable SSL support in the postgresql.conf file by turning the "ssl" parameter to "on".

  • Now re-start PostgreSQL; the server is ready for SSL operation.

With the server enabled for SSL, creating an encrypted connection is easy. In PgAdmin, create a new server connection (File > Add Server...), and set the SSL parameter to “require”.


Once you connect with the new connection, you can see in its properties that it is using an SSL connection.


Since the default SSL connection mode is "prefer", you don't even need to specify an SSL preference when connecting. A connection with the command line psql terminal will pick up the SSL option and use it by default:

psql (8.4.9)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.


Note how the terminal reports the SSL status of the connection.

34.2.2. Data Encryption

The pgcrypto module has a huge range of encryption options, so we will only demonstrate the simplest use case: encrypting a column of data using a symmetric cipher.

  • First, enable pgcrypto by loading the contrib SQL file, either in PgAdmin or psql.

  • Then, test the encryption function.

    -- encrypt a string using blowfish (bf)
    SELECT encrypt('this is a test phrase', 'mykey', 'bf');
  • And make sure it's reversible too!

    -- round-trip a string using blowfish (bf)
    SELECT decrypt(encrypt('this is a test phrase', 'mykey', 'bf'), 'mykey', 'bf');

34.3. Authentication

PostgreSQL supports many different authentication methods, to allow easy integration into existing enterprise architectures. For production purposes, the following methods are commonly used:

  • Password is the basic system where the passwords are stored by the database, with MD5 encryption.

  • Kerberos is a standard enterprise authentication method, which is used by both the GSSAPI and SSPI schemes in PostgreSQL. Using SSPI, PostgreSQL can authenticate against Windows servers.

  • LDAP is another common enterprise authentication method. The OpenLDAP server bundled with most Linux distributions provides an open source implementation of LDAP.

  • Certificate authentication is an option if you expect all client connections to be via SSL and are able to manage the distribution of keys.

  • PAM authentication is an option if you are on Linux or Solaris and use the PAM scheme for transparent authentication provision.

Authentication methods are controlled by the pg_hba.conf file. The "HBA" in the file name stands for "host based access", because in addition to allowing you to specify the authentication method to use for each database, it allows you to limit host access using network addresses.

Here is an example pg_hba.conf file:


# "local" is for Unix domain socket connections only
local   all         all                               trust
# IPv4 local connections:
host    all         all          trust
# IPv6 local connections:
host    all         all         ::1/128               trust
# remote connections for nyc database only
host    nyc         all         ldap

The file consists of five columns

  • TYPE determines the kind of access, either "local" for connections from the same server or "host" for remote connections.

  • DATABASE specifies what database the configuration line refers to or "all" for all databases

  • USER specifies what users the line refers to or "all" for all users

  • CIDR-ADDRESS specifies the network limitations for remote connections, using network/netmask syntax

  • METHOD specifies the authentication protocol to use. "trust" skips authentication entirely and simply accepts any valid username without challenge.

It's common for local connections to be trusted, since access to the server itself is usually privileged. Remote connections are disabled by default when PostgreSQL is installed: if you want to connect from remote machines, you'll have to add an entry.

The line for nyc in the example above is an example of a remote access entry. The nyc example allows LDAP authenticated access only to machines on the local network (in this case the 192.168.1. network) and only to the nyc database. Depending on the security of your network, you will use more or less strict versions of these rules in your production set-up.