Skip to content

MySQL Tutorial Users, Roles and Security

Encrypted Connections (TLS) and Data at Rest

Lesson 255 of 308 5 min read By Deepak Paswan Updated Oct 5, 2026

By default, data between a client and MySQL travels over the network as readable bytes. Anyone who can capture the traffic (on a shared network, a compromised router or between cloud zones) can read queries and results. TLS (the successor of SSL) encrypts the connection and lets the client check the server’s identity.

Is my connection encrypted?

query.sql
SQL
SHOW STATUS LIKE 'Ssl_cipher';
Output
OUTPUT
+---------------+------------------------+
| Variable_name | Value                  |
+---------------+------------------------+
| Ssl_cipher    | TLS_AES_128_GCM_SHA256 |
+---------------+------------------------+

A non-empty Ssl_cipher means this connection uses TLS. An empty value means it is not encrypted (a local socket connection is also unencrypted, which is fine on one machine). In mysql, \s or status shows a line SSL: Cipher in use is ....

MySQL 8 creates a self-signed certificate automatically at startup (auto_generate_certs), so TLS is available out of the box. A self-signed certificate protects against eavesdropping, but not against a man-in-the-middle, because the client cannot verify who it is talking to.

query.sql
SQL
SELECT @@tls_version AS tls_versions, @@require_secure_transport AS require_tls, @@ssl_ca IS NOT NULL AS has_ca_file;
Output
OUTPUT
+-----------------+-------------+-------------+
| tls_versions    | require_tls | has_ca_file |
+-----------------+-------------+-------------+
| TLSv1.2,TLSv1.3 |           0 |           1 |
+-----------------+-------------+-------------+
1 row in set

Server settings

VariableMeaning
tls_versionThe accepted protocol versions: TLSv1.2,TLSv1.3. Older ones (TLS 1.0 and 1.1) were removed in MySQL 8.0.28
require_secure_transportON rejects all non-TLS connections (except local sockets and shared memory)
ssl_ca, ssl_cert, ssl_keyThe certificate authority, the server certificate and the key
admin_tls_version, tls_ciphersuitesFine-tuning

Make require_secure_transport = ON on any server that is reachable over a network other than a trusted private link.

Require TLS per account

query.sql
SQL
CREATE USER 'shop_app'@'10.0.0.%' IDENTIFIED BY 'Ap9!pass#Long#Random#1' REQUIRE SSL;
CREATE USER 'partner'@'%' IDENTIFIED BY 'Pa7!ner#Long#Random#2' REQUIRE X509;
SELECT user, host, ssl_type FROM mysql.user WHERE user IN ('shop_app', 'partner') ORDER BY user;
Output
OUTPUT
+----------+----------+----------+
| user     | host     | ssl_type |
+----------+----------+----------+
| partner  | %        | X509     |
| shop_app | 10.0.0.% | ANY      |
+----------+----------+----------+
2 rows in set
RequirementMeaning
REQUIRE SSLThe connection must use TLS
REQUIRE X509The client must also present a valid certificate
REQUIRE ISSUER '...' / SUBJECT '...'The certificate must have this issuer or subject
REQUIRE NONENo requirement

A client without TLS then gets: ERROR 1045 (28000): Access denied for user ... (using password: YES). Use the mysql --ssl-mode=REQUIRED option from the client.

Client side: ssl-mode

--ssl-modeBehaviour
PREFERRED (default)TLS if the server supports it, silently falls back to unencrypted
REQUIREDTLS or fail. The certificate is not checked
VERIFY_CATLS, and the server certificate must be signed by the given CA
VERIFY_IDENTITYAs VERIFY_CA, and the host name must match the certificate. Use this across untrusted networks
terminal
BASH
mysql --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/mysql-ca.pem -h db.example.com -u shop_app -p

The default PREFERRED is dangerous across networks: an attacker who strips TLS support from the exchange makes the client fall back to plain text. Use REQUIRED at least, and VERIFY_IDENTITY when you can.

PHP

index.php
PHP
<?php
$pdo = new PDO('mysql:host=db.example.com;dbname=shop;charset=utf8mb4', $user, $password, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::MYSQL_ATTR_SSL_CA => '/etc/ssl/mysql-ca.pem',
    PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
]);
echo $pdo->query("SHOW STATUS LIKE 'Ssl_cipher'")->fetch(PDO::FETCH_ASSOC)['Value'];

Other drivers have the same settings (sslmode, ssl-ca and so on).

Certificates

For a private database server, create your own certificate authority and sign the server certificate, and distribute the CA certificate to clients. MySQL’s mysql_ssl_rsa_setup tool creates a CA and keys for testing. In production, use certificates from your organisation’s CA, or from your cloud provider (managed databases give you a CA bundle to download).

Remember to rotate certificates before they expire (SHOW STATUS LIKE 'Ssl_server_not_after') and to run ALTER INSTANCE RELOAD TLS; to load new files without a restart.

query.sql
SQL
SHOW STATUS LIKE 'Ssl_server_not_after';
Output
OUTPUT
+----------------------+--------------------------+
| Variable_name        | Value                    |
+----------------------+--------------------------+
| Ssl_server_not_after | Oct  3 09:12:01 2036 GMT |
+----------------------+--------------------------+

(An auto-generated certificate on the test server. The date will differ.)

Encryption at rest

TLS protects data in motion. Data at rest (the files on disk and in backups) is protected by InnoDB tablespace encryption, which needs a keyring component to store the master key:

query.sql
SQL
CREATE TABLE secrets (id INT PRIMARY KEY, note VARCHAR(50)) ENCRYPTION = 'Y';

If no keyring is configured, MySQL refuses:

Output
OUTPUT
ERROR 3185 (HY000): Can't find master key from keyring, please check in the server log if a keyring is loaded and initialized successfully.

(The test servers have no keyring, so this is the real response.) With a keyring set up (the file-based keyring for tests, or a key management service such as HashiCorp Vault or a cloud KMS in production), ENCRYPTION='Y' encrypts the table’s .ibd file. You can make it the default for a database or schema with DEFAULT ENCRYPTION = 'Y'.

Protects againstTLSEncryption at rest
Eavesdropping on the networkYesNo
Stolen disks or backupsNoYes
A user with database access reading dataNoNo (they read the plain data through MySQL)
SQL injectionNoNo

Neither replaces good privileges and parameterised queries. Encryption at rest is often required by regulation (payment card, health data).

Application-level encryption

For especially sensitive columns (national IDs, card details), encrypt in the application with a vetted library, so that even the database administrator and a stolen backup see only ciphertext. Keep the keys outside the database (Hash and Encryption Functions).

Checklist

  • [ ] TLS 1.2 or higher only.
  • [ ] require_secure_transport = ON where traffic crosses a network.
  • [ ] Clients use VERIFY_IDENTITY (or at least REQUIRED).
  • [ ] Certificates come from a CA you control, and are renewed on time.
  • [ ] Backups are encrypted, and the keys are stored separately.
  • [ ] Tablespace encryption on for regulated data.
lightbulb
Test the encryption
Run SHOW STATUS LIKE 'Ssl_cipher' through the same path your application uses. A connection that you assume is encrypted is not always encrypted.