Back to blog
AzureBest PracticesCloud SecurityDatabasesOperations & Compliance

MySQL SSL Not Enforced on Azure: Risk and Remediation

Learn why Azure MySQL servers must enforce SSL, the risks of plaintext connections, and how to fix and prevent it with CLI, Terraform, and Azure Policy.

TL;DR

This check flags Azure Database for MySQL servers that allow unencrypted connections. Without SSL enforcement, credentials and query data can be read in transit. Set ssl-enforcement to Enabled and require TLS 1.2 to fix it.

Azure Database for MySQL supports encrypted connections by default, but encryption is only useful if you require it. When SSL enforcement is turned off, the server happily accepts plaintext connections alongside encrypted ones. That means a single misconfigured client, or an attacker positioned on the network path, can move data over the wire with no protection at all.

This check, mysql_sslnotenforced, looks at the sslEnforcement property on your Azure MySQL servers and reports any server where it is set to Disabled.


What this check detects

The check inspects each Azure Database for MySQL server in your subscription and reads its SSL enforcement setting. Azure exposes this as a server-level property:

  • Enabled — the server rejects any connection that does not negotiate SSL/TLS.
  • Disabled — the server accepts both encrypted and plaintext connections. This is what the check flags.

The setting applies to both the Single Server and Flexible Server deployment models, though the property names and tooling differ slightly between them. We cover both below.

Note: SSL enforcement being "Disabled" does not mean encryption is impossible, only that it is optional. Clients can still connect with TLS. The risk is that nothing stops them from connecting without it.


Why it matters

MySQL credentials and query payloads travel over the network on every connection. If that traffic is unencrypted, anyone who can observe the path can read it. Here is where that bites in practice.

Credentials in cleartext

The MySQL wire protocol does not hash the password in a way that protects it from a passive observer once a connection is established without TLS. An attacker capturing packets can recover authentication material and then connect directly to the database themselves.

Data exfiltration on the path

Query results, including PII, payment data, and internal records, flow back to the client. Without TLS, a compromised hop between your app and the database, a misconfigured peering, or a rogue process on a shared subnet can read all of it.

Compliance failures

PCI DSS, HIPAA, SOC 2, and most internal security baselines require encryption of data in transit. A MySQL server accepting plaintext connections is a finding waiting to happen during an audit, even if every current client happens to use TLS today.

Warning: "All our clients already use SSL" is not a control. Enforcement is the control. A new microservice, a debugging session from a laptop, or a misconfigured connection string can silently downgrade to plaintext if the server permits it.


How to fix it

The fix is to enforce SSL at the server level and, ideally, require a modern TLS version. Choose the path that matches your deployment model.

Single Server (Azure CLI)

az mysql server update \
  --resource-group myResourceGroup \
  --name my-mysql-server \
  --ssl-enforcement Enabled

While you are here, set the minimum TLS version so older, weaker protocols are rejected:

az mysql server update \
  --resource-group myResourceGroup \
  --name my-mysql-server \
  --minimal-tls-version TLS1_2

Flexible Server (Azure CLI)

Flexible Server uses server parameters rather than a dedicated SSL enforcement flag. The relevant parameter is require_secure_transport:

az mysql flexible-server parameter set \
  --resource-group myResourceGroup \
  --server-name my-mysql-flex \
  --name require_secure_transport \
  --value ON

az mysql flexible-server parameter set \
  --resource-group myResourceGroup \
  --server-name my-mysql-flex \
  --name tls_version \
  --value TLSv1.2

Warning: Enabling enforcement immediately rejects any client still connecting without TLS. Confirm that your application connection strings request SSL before you flip this, or you will cause an outage. Roll it out in non-production first.

Console steps

  1. Open the Azure portal and navigate to your MySQL server.
  2. For Single Server, select Connection security. For Flexible Server, select Server parameters.
  3. Single Server: set Enforce SSL connection to ENABLED and set Minimum TLS version to 1.2.
  4. Flexible Server: set require_secure_transport to ON and tls_version to TLSv1.2.
  5. Save.

Make sure clients are ready

Download the certificate Azure recommends and point your client at it. For example, with the MySQL CLI:

mysql -h my-mysql-server.mysql.database.azure.com \
  -u myadmin@my-mysql-server \
  -p \
  --ssl-mode=REQUIRED \
  --ssl-ca=DigiCertGlobalRootCA.crt.pem

Tip: In application connection strings, set the SSL mode explicitly rather than relying on driver defaults. For most MySQL drivers that means sslmode=REQUIRED or useSSL=true&requireSSL=true (JDBC). Pin to VERIFY_CA or VERIFY_IDENTITY where your driver supports it for protection against man-in-the-middle attacks.


Fixing it with infrastructure as code

If you provision MySQL through Terraform, set enforcement directly in the resource so it cannot drift.

Terraform — Single Server

resource "azurerm_mysql_server" "example" {
  name                = "my-mysql-server"
  resource_group_name = azurerm_resource_group.example.name
  location            = azurerm_resource_group.example.location

  administrator_login          = "myadmin"
  administrator_login_password = var.db_password

  sku_name   = "GP_Gen5_2"
  storage_mb = 5120
  version    = "8.0"

  ssl_enforcement_enabled          = true
  ssl_minimal_tls_version_enforced = "TLS1_2"
}

Terraform — Flexible Server

resource "azurerm_mysql_flexible_server_configuration" "require_secure_transport" {
  name                = "require_secure_transport"
  resource_group_name = azurerm_resource_group.example.name
  server_name         = azurerm_mysql_flexible_server.example.name
  value               = "ON"
}

resource "azurerm_mysql_flexible_server_configuration" "tls_version" {
  name                = "tls_version"
  resource_group_name = azurerm_resource_group.example.name
  server_name         = azurerm_mysql_flexible_server.example.name
  value               = "TLSv1.2"
}

How to prevent it from happening again

One-off fixes do not survive a busy team. Bake enforcement into the places where servers get created and reviewed.

Azure Policy

Azure ships a built-in policy, "Enforce SSL connection should be enabled for MySQL database servers." Assign it with a deny or audit effect at the subscription or management group level:

az policy assignment create \
  --name 'enforce-mysql-ssl' \
  --display-name 'Enforce SSL on MySQL servers' \
  --policy 'e802a67a-daf5-4436-9ea6-f6d821dd0c5d' \
  --scope '/subscriptions/<subscription-id>'

Use deny in production to block non-compliant servers at creation time, and audit in lower environments if you need a softer rollout.

CI/CD gate for Terraform

Catch the misconfiguration before it ever reaches Azure. Tools like Checkov and tfsec read your plan and fail the pipeline on insecure settings:

checkov -d . --check CKV_AZURE_28,CKV_AZURE_29

Wire that into your pull request checks so a server without SSL enforcement cannot merge.

Tip: Pair the create-time gate with continuous scanning. Lensix re-runs mysql_sslnotenforced across your subscriptions on a schedule, so a server that gets reconfigured manually after deployment still surfaces as a finding instead of sitting quietly until your next audit.


Best practices

  • Enforce, do not assume. Always set enforcement on the server. Client behavior changes; server policy is the durable control.
  • Require TLS 1.2 or higher. TLS 1.0 and 1.1 are deprecated and have known weaknesses. Set the minimum version explicitly.
  • Verify the server certificate. Plain REQUIRED mode encrypts traffic but does not stop an impersonating server. Use VERIFY_CA or VERIFY_IDENTITY with the Azure CA bundle for end-to-end assurance.
  • Restrict network exposure too. SSL protects data in transit, but private endpoints and tight firewall rules reduce who can reach the server in the first place. Treat them as complementary, not alternatives.
  • Track certificate rotation. Azure periodically updates the root CA. Keep the bundle current in your clients so renewals do not break connections.
  • Test enforcement in staging first. Flipping enforcement on a live server with non-TLS clients causes immediate connection failures. Validate connection strings before production.

Enforcing SSL on Azure MySQL is a small change with an outsized payoff. It closes off credential sniffing and data interception, satisfies a control auditors always check, and costs nothing beyond a few minutes of coordination with your application teams.