Back to blog
AzureBest PracticesCloud SecurityDatabasesOperations & Compliance

PostgreSQL SSL Not Enforced on Azure: Risks and Remediation

Learn why Azure PostgreSQL servers must enforce SSL connections, the risks of plaintext traffic, and how to remediate and prevent this misconfiguration.

TL;DR

This check flags any Azure Database for PostgreSQL server that allows unencrypted client connections. Without SSL enforcement, credentials and query data travel in plaintext and can be intercepted. Fix it by setting ssl_enforcement_enabled to Enabled on the server.

Database traffic is one of the easiest things to overlook when you're hardening a cloud environment. The server sits behind a firewall, access is gated by passwords, and everything feels reasonably locked down. But if the server accepts plaintext connections, none of that protects the data while it's actually moving across the network. That's exactly what the PostgreSQL SSL Not Enforced check looks for.


What this check detects

Lensix raises this finding when an Azure Database for PostgreSQL server has the sslEnforcement setting turned off. When SSL enforcement is disabled, the server will happily accept connections that have no transport encryption at all. A client can connect over a plain TCP socket and the data, including the authentication handshake, flows in cleartext.

This applies to the Azure Database for PostgreSQL Single Server deployment model, where SSL enforcement is a top-level server property you can flip on or off. On Flexible Server the equivalent control is the require_secure_transport server parameter, which behaves the same way conceptually.

Note: SSL enforcement does not encrypt your data at rest, and it does not replace network controls like private endpoints or firewall rules. It governs one specific thing: whether the server will reject connections that aren't using TLS.


Why it matters

When SSL is not enforced, the door is open for an attacker positioned anywhere on the network path to read or tamper with traffic between your application and the database. A few concrete scenarios:

  • Credential theft. PostgreSQL authentication can pass over the wire in a form that an on-path attacker can capture. Once they have valid credentials, your firewall rules stop being a barrier.
  • Data exfiltration. Query results, including PII, payment data, or session tokens, are readable by anyone who can sniff the connection.
  • Man-in-the-middle tampering. Without TLS there is no integrity guarantee, so a determined attacker can modify queries or responses in transit.

The risk isn't purely theoretical. In hybrid setups, traffic crossing VPN gateways, peered VNets, or ExpressRoute circuits passes through hardware and links you don't fully control. And a misconfigured application that connects from outside the VNet over the public endpoint makes the exposure even worse.

There's a compliance angle too. PCI DSS, HIPAA, SOC 2, and most internal security baselines require encryption of data in transit. A PostgreSQL server that accepts plaintext connections will fail those audits, and "we assumed the app always used SSL" is not a control auditors accept.

Warning: Even if your application currently connects with SSL, leaving enforcement off means a future deployment, a debugging session, or a misconfigured connection string can silently downgrade to plaintext with no error. Enforcement is what closes that gap.


How to fix it

The fix is a single setting change. Before you flip it, confirm your clients can actually negotiate TLS, otherwise enforcement will break their connections.

Azure Portal

  1. Open the Azure Database for PostgreSQL server in the portal.
  2. Under Settings, select Connection security.
  3. Set Enforce SSL connection to ENABLED.
  4. Click Save.

Azure CLI (Single Server)

az postgres server update \
  --resource-group myResourceGroup \
  --name mypgserver \
  --ssl-enforcement Enabled

Verify the change:

az postgres server show \
  --resource-group myResourceGroup \
  --name mypgserver \
  --query "sslEnforcement"

The output should be "Enabled".

Azure CLI (Flexible Server)

Flexible Server uses a server parameter instead of a top-level property:

az postgres flexible-server parameter set \
  --resource-group myResourceGroup \
  --server-name mypgflexserver \
  --name require_secure_transport \
  --value ON

Warning: Enabling enforcement immediately rejects any client that isn't using TLS. If a production application connects without SSL, it will start failing the moment you save. Test in a non-production environment first and confirm your connection strings include sslmode=require (or stricter) before rolling this out.

Make sure clients connect with SSL

For libpq-based clients (including psql), use an sslmode of require or higher. For production, prefer verify-full with the Azure CA certificate so the client also validates the server identity:

# Download the Azure root CA
curl -o DigiCertGlobalRootCA.crt.pem \
  https://dl.cacerts.digicert.com/DigiCertGlobalRootCA.crt.pem

psql "host=mypgserver.postgres.database.azure.com \
  port=5432 dbname=mydb user=adminuser@mypgserver \
  sslmode=verify-full sslrootcert=DigiCertGlobalRootCA.crt.pem"

Fixing it as infrastructure-as-code

If you manage your databases through Terraform or Bicep, set enforcement in the resource definition so it can never drift back off.

Terraform (Single Server)

resource "azurerm_postgresql_server" "main" {
  name                = "mypgserver"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location

  administrator_login          = "adminuser"
  administrator_login_password = var.db_password
  sku_name                     = "GP_Gen5_2"
  version                      = "11"
  storage_mb                   = 51200

  ssl_enforcement_enabled          = true
  ssl_minimal_tls_version_enforced = "TLS1_2"
}

Terraform (Flexible Server)

resource "azurerm_postgresql_flexible_server_configuration" "secure_transport" {
  name      = "require_secure_transport"
  server_id = azurerm_postgresql_flexible_server.main.id
  value     = "ON"
}

Tip: Set ssl_minimal_tls_version_enforced = "TLS1_2" at the same time. Enforcing SSL while still permitting TLS 1.0 and 1.1 leaves you exposed to known weaknesses in those older protocol versions.


How to prevent it from coming back

A one-time fix doesn't help if the next server someone spins up ships with enforcement off. Bake the control into the places where infrastructure gets created and reviewed.

Azure Policy

Azure ships a built-in policy named "Enforce SSL connection should be enabled for PostgreSQL database servers". Assign it with a Deny effect to block non-compliant servers at deployment time, or use it in audit mode first to find existing violations:

az policy assignment create \
  --name "enforce-pg-ssl" \
  --display-name "Enforce SSL on PostgreSQL servers" \
  --policy "/providers/Microsoft.Authorization/policyDefinitions/c29c38cb-74eb-4dd5-bb25-3f1b3c4bb13c" \
  --scope "/subscriptions/"

Note: Run the policy in audit mode across your subscription before switching to deny. That gives you a clean list of existing servers to remediate without breaking anything, and it confirms nothing legitimate depends on plaintext.

CI/CD gates with policy-as-code

Catch the misconfiguration before it ever reaches Azure by scanning Terraform in your pipeline. Tools like Checkov, tfsec, or Terrascan flag PostgreSQL servers with SSL disabled. A minimal Checkov step:

checkov -d ./infra --framework terraform \
  --check CKV_AZURE_29

Fail the build on any violation so the pull request can't merge until enforcement is set.

Continuous monitoring

Policy and pipeline checks cover new and changed resources, but configuration can still drift through manual portal changes or out-of-band scripts. Keep this Lensix check running on a schedule so any server that loses SSL enforcement surfaces quickly, and route the finding to whoever owns the database.


Best practices

  • Enforce SSL everywhere, no exceptions. There is no production workload that benefits from accepting plaintext PostgreSQL connections.
  • Set a minimum TLS version of 1.2. Enforcing SSL without a version floor still allows weak, deprecated protocols.
  • Use verify-full on clients. sslmode=require encrypts the connection but doesn't verify the server's identity. verify-full with the CA certificate protects against impersonation.
  • Combine transport encryption with network isolation. Use private endpoints or VNet rules so the database isn't reachable from the public internet, and treat SSL as defense in depth rather than the only control.
  • Audit existing servers regularly. A server created before your standards were in place is the most likely place to find this gap.
  • Document the connection requirements. Make sslmode=verify-full and the CA cert part of your standard connection string template so application teams get it right by default.

Enforcing SSL on PostgreSQL is a low-effort, high-value change. It takes one setting, it rarely costs anything, and it closes off a class of attacks that firewall rules and strong passwords simply don't address. Turn it on, gate it in your pipeline, and keep an eye on it so it stays on.