sqlencryption-review — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited sqlencryption-review (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Audit the complete encryption posture of a SQL Server instance or database. Applies 112 checks (A1–A112) across 20 categories:
For background on encryption concepts, algorithm comparisons, TLS versions, the SQL Server key hierarchy, and PCI-DSS / HIPAA / GDPR requirements, read references/concepts.md.
Accept any combination of the following. Apply all checks that are relevant to the data provided. When the user describes symptoms in natural language, apply checks based on the described state.
sys.dm_database_encryption_keys joined with sys.databases and master.sys.certificates — TDE checkssys.columns joined with sys.column_encryption_keys and sys.column_master_keys — Always Encrypted checkssys.symmetric_keys, sys.asymmetric_keys, sys.certificates, sys.key_encryptions — key management checksmsdb.dbo.backupset — backup encryption checkssys.dm_exec_connections — transport encryption checkssys.sensitivity_classifications — compliance coverage checkssys.cryptographic_providers or sys.dm_cryptographic_provider_properties — EKM checkssys.endpoints — Service Broker / AG endpoint checkssys.server_audits and sys.database_audit_specifications — audit checkssys.ledger_* DMVs — SQL Server 2022 Ledger checks (A73–A76)sys.dm_exec_connections with connection attribute details — driver version checks (A64)-- 1. TDE status across all databases
SELECT
d.name AS database_name,
d.is_encrypted,
dek.encryption_state,
dek.encryption_state_desc,
dek.percent_complete,
dek.encryptor_type,
dek.key_algorithm,
dek.key_length,
c.name AS certificate_name,
c.expiry_date AS cert_expiry,
c.pvt_key_encryption_type_desc
FROM sys.databases d
LEFT JOIN sys.dm_database_encryption_keys dek ON d.database_id = dek.database_id
LEFT JOIN master.sys.certificates c ON dek.encryptor_thumbprint = c.thumbprint
ORDER BY d.name;
-- 2. Always Encrypted column inventory
SELECT
SCHEMA_NAME(t.schema_id) AS schema_name,
t.name AS table_name,
c.name AS column_name,
c.encryption_type,
c.encryption_type_desc,
c.encryption_algorithm_name,
cek.name AS cek_name,
cmk.name AS cmk_name,
cmk.key_store_provider_name,
cmk.key_path
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
JOIN sys.column_encryption_keys cek ON c.column_encryption_key_id = cek.column_encryption_key_id
JOIN sys.column_master_keys cmk ON cek.column_master_key_id = cmk.column_master_key_id
WHERE c.column_encryption_key_id IS NOT NULL;
-- 3. CEK version history (rotation check)
SELECT
cek.name AS cek_name,
cek.create_date,
cekv.column_master_key_id,
cmk.name AS cmk_name,
cekv.create_date AS version_created
FROM sys.column_encryption_keys cek
JOIN sys.column_encryption_key_values cekv ON cek.column_encryption_key_id = cekv.column_encryption_key_id
JOIN sys.column_master_keys cmk ON cekv.column_master_key_id = cmk.column_master_key_id;
-- 4. Symmetric and asymmetric keys
SELECT
sk.name,
sk.symmetric_key_id AS key_id,
'SYMMETRIC' AS key_type,
sk.algorithm_desc,
CAST(sk.key_length AS VARCHAR(10)) AS key_length,
sk.create_date,
sk.modify_date,
ke.crypt_type_desc AS key_protection_type
FROM sys.symmetric_keys sk
OUTER APPLY (
SELECT TOP 1 crypt_type_desc
FROM sys.key_encryptions
WHERE key_id = sk.symmetric_key_id
ORDER BY crypt_type
) ke
WHERE sk.name NOT LIKE '##%'
UNION ALL
SELECT
name,
asymmetric_key_id,
'ASYMMETRIC',
algorithm_desc,
CAST(key_length AS VARCHAR(10)),
create_date,
modify_date,
pvt_key_encryption_type_desc
FROM sys.asymmetric_keys
WHERE name NOT LIKE '##%';
-- 5. Certificates (all purposes)
SELECT
name,
certificate_id,
pvt_key_encryption_type_desc,
issuer_name,
subject,
start_date,
expiry_date,
DATEDIFF(DAY, GETDATE(), expiry_date) AS days_until_expiry,
NULL AS sig_algorithm -- CERTPROPERTY does not expose 'Algorithm'; verify via certutil or Get-ChildItem Cert:\ | Select SignatureAlgorithm
FROM sys.certificates
WHERE name NOT LIKE '##%'
ORDER BY expiry_date;
-- 6. Backup encryption history (last 30 days)
SELECT TOP 30
database_name,
backup_start_date,
backup_finish_date,
type AS backup_type,
key_algorithm,
encryptor_type,
encryptor_thumbprint
FROM msdb.dbo.backupset
WHERE backup_start_date > DATEADD(DAY, -30, GETDATE())
ORDER BY backup_start_date DESC;
-- 7. Connection encryption status
SELECT
encrypt_option,
auth_scheme,
COUNT(*) AS connection_count,
SUM(CASE WHEN client_net_address NOT IN ('<local machine>', '127.0.0.1', '::1')
THEN 1 ELSE 0 END) AS remote_connections
FROM sys.dm_exec_connections
GROUP BY encrypt_option, auth_scheme;
-- 8. Key hierarchy: DMK protection status
SELECT
d.name AS database_name,
d.is_master_key_encrypted_by_server,
sk.name AS dmk_name,
sk.create_date,
sk.modify_date
FROM sys.databases d
LEFT JOIN sys.symmetric_keys sk ON sk.name = N'##MS_DatabaseMasterKey##'
WHERE d.database_id = DB_ID();
-- 9. EKM providers
SELECT
provider_id,
name,
dll_path,
is_enabled,
provider_version,
sqlcrypt_version
FROM sys.cryptographic_providers;
-- 10. Sensitivity classifications
SELECT
SCHEMA_NAME(t.schema_id) AS schema_name,
t.name AS table_name,
c.name AS column_name,
sc.information_type,
sc.label,
sc.rank_desc,
c.column_encryption_key_id AS ae_key_id
FROM sys.sensitivity_classifications sc
JOIN sys.objects t ON sc.major_id = t.object_id
JOIN sys.columns c ON sc.major_id = c.object_id AND sc.minor_id = c.column_id;
-- 11. Endpoints using certificate authentication
SELECT
e.name AS endpoint_name,
e.type_desc,
e.connection_auth_desc,
c.name AS certificate_name,
c.expiry_date,
DATEDIFF(DAY, GETDATE(), c.expiry_date) AS days_until_expiry
FROM sys.endpoints e
LEFT JOIN sys.certificates c ON e.certificate_id = c.certificate_id
WHERE e.connection_auth_desc LIKE '%CERTIFICATE%';Walk A1–A80 in category order. Report every triggered finding — do not stop at the first match per category. For checks where the relevant DMV data is absent from the input, mark them as NOT ASSESSED rather than skipping silently. For checks where the data is partial (e.g., a description rather than DMV output), state your assumption explicitly before applying the check.
When multiple databases or instances are present in the input, run all applicable checks per database/instance and aggregate findings. Prioritize production databases for Critical and Warning findings.
For T-SQL source-level checks (A18, A19, A43), scan provided SQL modules in sys.sql_modules or pasted code; note any checks that could not be verified due to missing source code.
| Metric | Info | Warning | Critical |
|---|---|---|---|
| Certificate / key days until expiry | — | < 90 days | Expired (≤ 0 days) |
| Key rotation age (symmetric / CEK) | — | > 365 days since last rotation | > 730 days |
| CMK rotation age | — | > 730 days | — |
| RSA asymmetric key length | — | RSA_1024 | RSA_512 |
| Unencrypted remote connections | 0 | > 0 | — |
| TDE DEK algorithm | AES_128 / AES_192 | — | TRIPLE_DES_3KEY (the only non-AES DEK algorithm; RC4 is not valid for a DEK) |
| Symmetric key algorithm (CLE) | AES_128 / AES_192 | DES / DESX / TRIPLE_DES | RC4 / RC2 |
| Backup encryption algorithm | AES_128 | TRIPLE_DES_3KEY | None (unencrypted) |
| Non-FIPS algorithm anywhere | — | SHA1 / DES / 3DES | RC4 / MD5 |
sys.databases WHERE is_encrypted = 0 AND database_id > 4 (non-system database)ALTER DATABASE [db] SET ENCRYPTION ON. Note: tempdb will encrypt automatically.sys.dm_database_encryption_keys WHERE encryption_state IN (2, 4, 5, 6) AND percent_complete < 1002 = encryption in progress, 4 = key change in progress (DEK algorithm or key being regenerated via ALTER DATABASE ENCRYPTION KEY REGENERATE), 5 = decryption in progress, 6 = protection change in progress (the certificate or asymmetric key encrypting the DEK is being replaced)percent_complete; avoid heavy index rebuild or backup jobs during scan; on SQL 2019+, use ALTER DATABASE [db] SET ENCRYPTION SUSPEND | RESUME if I/O pressure is highmaster.sys.certificates but no evidence of a BACKUP CERTIFICATE operation (no corresponding file path documented, no SQL Agent job containing BACKUP CERTIFICATE)BACKUP CERTIFICATE [tde_cert] TO FILE = 'path\tde_cert.cer' WITH PRIVATE KEY (FILE = 'path\tde_cert.pvk', ENCRYPTION BY PASSWORD = 'StrongPassword') — store the .cer, .pvk, and password in separate secure, off-server locationsmaster.sys.certificates WHERE cert thumbprint matches a sys.dm_database_encryption_keys.encryptor_thumbprint AND DATEDIFF(DAY, GETDATE(), expiry_date) < 90expiry_date < GETDATE(); Warning if within 90 daysCREATE CERTIFICATE [tde_cert_new] WITH EXPIRY_DATE = '20270101'; re-key DEK: ALTER DATABASE [db] ENCRYPTION KEY ENCRYPTION BY SERVER CERTIFICATE [tde_cert_new]; keep old cert until all backups protected by it have been supersededsys.dm_database_encryption_keys WHERE key_algorithm != 'AES_256' — specifically TRIPLE_DES_3KEY or AES_128/AES_192ALTER DATABASE [db] ENCRYPTION KEY REGENERATE WITH ALGORITHM = AES_256 — triggers a re-encryption scan; plan for I/O impactsys.dm_database_encryption_keys GROUP BY encryptor_thumbprint HAVING COUNT(*) > 1TDE_[dbname]_[year]sys.dm_database_encryption_keys WHERE database_id IN (1, 2, 3) (master, model, msdb — tempdb at database_id 2 is acceptable and expected)ALTER DATABASE master SET ENCRYPTION OFF; verify that AG seeding, RESTORE DATABASE, and DAC connections still function after changesys.databases WHERE name = 'tempdb' AND is_encrypted = 1 AND COUNT of user databases (database_id > 4) with is_encrypted = 1 = 0sys.databases shows is_encrypted = 0 for tempdb after restartsys.columns WHERE encryption_type = 1 (DETERMINISTIC) AND column name does not suggest a join key or lookup field (no _id, _key, _code, _number suffix pattern)Set-SqlColumnEncryptionsys.columns WHERE encryption_type = 2 (RANDOMIZED) AND sys.configurations WHERE name = 'column encryption enclave type' returns 0 (no enclave)sp_configure 'column encryption enclave type', 1) and configure VBS enclave attestation on the clientsys.columns WHERE column_encryption_key_id IS NOT NULL AND encryption_algorithm_name != 'AEAD_AES_256_CBC_HMAC_SHA_256'ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256' in the column definition; coordinate with application teams for driver updates if neededsys.configurations WHERE name = 'column encryption enclave type' = 0 AND randomized-encrypted columns exist in the databaseBETWEEN, <, >) and LIKE on AE columns are currently impossibleEXEC sp_configure 'column encryption enclave type', 1; RECONFIGURE — requires Windows Server 2019+ with Virtualization Based Security or Intel SGX hardware; configure attestation service URL in client connection stringsys.column_master_keys WHERE key_store_provider_name = 'MSSQL_CERTIFICATE_STORE'key_store_provider_name = 'AZURE_KEY_VAULT') or a FIPS 140-2 Level 3 HSM; generate new CMK in the new store, re-encrypt CEKs under the new CMK, then drop the old CMKsys.columns WHERE name matches any of %ssn%, %social_sec%, %credit_card%, %card_num%, %cvv%, %cvc%, %password%, %passwd%, %\bpin\b%, %dob%, %date_of_birth%, %salary%, %tax_id%, %passport%, %national_id%, %medical_record%, %diagnosis%, %account_number% — AND column_encryption_key_id IS NULLsys.sensitivity_classifications label after encryptingsys.column_encryption_key_values WHERE for a given column_encryption_key_id there is only one record (single CMK version, never rotated) AND sys.column_encryption_keys.create_date < DATEADD(YEAR, -1, GETDATE())ALTER COLUMN ENCRYPTION KEY [cek] ADD VALUE (COLUMN_MASTER_KEY = [new_cmk], ALGORITHM = 'RSA_OAEP', ENCRYPTED_VALUE = 0x...) — use SSMS wizard to automate CEK re-encryption; then DROP VALUE for the old CMK versionsys.column_master_keys WHERE create_date < DATEADD(YEAR, -2, GETDATE()) AND no newer CMK with the same logical name pattern existsInvoke-SqlColumnMasterKeyRotation -InputObject $db -SourceColumnMasterKeyName [old_cmk] -TargetColumnMasterKeyName [new_cmk]; distribute new CMK to all application servers before completing rotationsys.symmetric_keys WHERE algorithm_desc IN ('DES', 'Triple_DES', 'RC2', 'RC4', 'DESX', 'TRIPLE_DES_3KEY') AND name NOT LIKE '##%'CREATE SYMMETRIC KEY [key_new] WITH ALGORITHM = AES_256, KEY_SOURCE = '...', IDENTITY_VALUE = '...', ENCRYPTION BY CERTIFICATE [cert]; re-encrypt all data with ENCRYPTBYKEY(KEY_GUID('[key_new]'), plaintext); close and DROP SYMMETRIC KEY [key_old]OPEN SYMMETRIC KEY with no CLOSE SYMMETRIC KEY or CLOSE ALL SYMMETRIC KEYS before the end of the batch; OR sys.openkeys showing keys open across long-running or idle sessionsDECRYPTBYKEY() without re-opening; connection pool reuse means keys may be open in unexpected contextsCLOSE SYMMETRIC KEY [key_name] after every use; add a CATCH block that also closes the key on error; add CLOSE ALL SYMMETRIC KEYS as a session cleanup safeguard in session-level error handlerssys.key_encryptions WHERE crypt_type_desc = 'ENCRYPTION BY PASSWORD' AND the same key_id does NOT also appear in a row with crypt_type_desc IN ('ENCRYPTION BY CERTIFICATE', 'ENCRYPTION BY ASYMMETRIC KEY')ALTER SYMMETRIC KEY [key] ADD ENCRYPTION BY CERTIFICATE [cert] — then test the OPEN statement without the password parameter — then ALTER SYMMETRIC KEY [key] DROP ENCRYPTION BY PASSWORD = 'old_password'sys.symmetric_keys WHERE modify_date = create_date AND create_date < DATEADD(DAY, -365, GETDATE()) AND name NOT LIKE '##%'CLE_CustomerSSN_2025)column_encryption_key_id IS NOT NULL (Always Encrypted) AND sys.sql_modules.definition contains ENCRYPTBYKEY or DECRYPTBYKEY calls referencing the same tablemsdb.dbo.backupset WHERE backup_start_date > DATEADD(DAY, -30, GETDATE()) AND key_algorithm IS NULL (no encryption) — for database_name matching production databasesWITH ENCRYPTION (ALGORITHM = AES_256, SERVER CERTIFICATE = [backup_cert]) to all BACKUP DATABASE and BACKUP LOG statements; create the backup certificate in master DB first; update Ola Hallengren or native maintenance plan scriptsmsdb.dbo.backupset.key_algorithm IS NOT NULL) AND no BACKUP CERTIFICATE evidence for the referenced certificateBACKUP CERTIFICATE [backup_cert] TO FILE = 'D:\CertBackups\backup_cert.cer' WITH PRIVATE KEY (FILE = 'D:\CertBackups\backup_cert.pvk', ENCRYPTION BY PASSWORD = 'VaultStoredPassword') — keep the .cer, .pvk, and password in separate, geographically distributed secure storagemsdb.dbo.backupset WHERE key_algorithm IN ('TRIPLE_DES_3KEY', 'AES_128') AND backup_start_date > DATEADD(DAY, -30, GETDATE())ALGORITHM = AES_256; existing backups retain their original algorithm (still restorable); change affects only new backups going forwardmsdb.dbo.backupset.encryptor_thumbprint with DATEDIFF(DAY, GETDATE(), expiry_date) < 90sys.dm_server_registry WHERE registry_key LIKE N'%SuperSocketNetLib%' AND value_name = N'ForceEncryption' AND value_data = 0; or no TLS certificate configured in SQL Server Configuration ManagerEncrypt=False (which was the default in drivers before ODBC 18 / JDBC 12 / .NET 7) establishes a plaintext session; credentials and query results travel in clear text over the networksys.dm_exec_connections WHERE encrypt_option = 'FALSE' AND client_net_address NOT IN ('<local machine>', '127.0.0.1', '::1', '<named pipe>')Encrypt=True; for interim mitigation, use IPsec between application servers and the SQL Server hostissuer_name = subjectTrustServerCertificate=True, which disables all certificate validation and opens a man-in-the-middle vectorsql_batch_starting or rpc_starting captures session attribute trust_server_certificate = 1; or connection string audit reveals TrustServerCertificate=True in application configuration filesTrustServerCertificate=True from all production connection strings; test each application after the changesys.certificates WHERE pvt_key_encryption_type_desc = 'ENCRYPTED_BY_PASSWORD' (password-only, DMK not used) OR pvt_key_encryption_type_desc = 'NO_PRIVATE_KEY' (key was stripped or never imported)ALTER CERTIFICATE [cert] WITH PRIVATE KEY (DECRYPTION BY PASSWORD = 'old_pwd', ENCRYPTION BY DATABASE MASTER KEY) — verify the DMK exists and is open; for missing private key, restore cert from BACKUP CERTIFICATE outputsys.endpoints WHERE type_desc = 'SERVICE_BROKER' AND connection_auth_desc = 'CERTIFICATE' joined to sys.certificates WHERE create_date < DATEADD(YEAR, -2, GETDATE())sys.remote_service_bindings at the remote end; update the local endpoint: ALTER ENDPOINT [sb_endpoint] FOR SERVICE_BROKER (AUTHENTICATION = CERTIFICATE [new_cert]); test message flow before dropping old certsys.endpoints WHERE type_desc = 'DATABASE_MIRRORING' (used for AG) AND connection_auth_desc LIKE '%CERTIFICATE%' joined to sys.certificates WHERE DATEDIFF(DAY, GETDATE(), expiry_date) < 90CREATE CERTIFICATE … FROM FILE; update endpoint on each replica: ALTER ENDPOINT [hadr_endpoint] FOR DATABASE_MIRRORING (AUTHENTICATION = CERTIFICATE [new_cert]); verify replica states with SELECT * FROM sys.dm_hadr_availability_replica_statessys.server_principals WHERE type = 'C' (certificate login) JOIN sys.server_role_members WHERE role_principal_id IN (SELECT principal_id FROM sys.server_principals WHERE name IN ('sysadmin', 'securityadmin', 'processadmin', 'dbcreator'))GRANT EXECUTE ON [schema].[proc] TO [cert_login]); consider using EXECUTE AS within the signed procedure instead of elevating the cert login itselfsys.certificates WHERE name NOT LIKE '##%' where the signature algorithm cannot be confirmed as SHA256 or stronger; verify via certutil -dump <certfile> (shows Signature Algorithm) or PowerShell Get-ChildItem Cert:\ | Select-Object Subject, SignatureAlgorithm — CERTPROPERTY() does not expose the signature algorithm and cannot be used for this checkCREATE CERTIFICATE uses SHA1 by default on older instances — explicitly specify a stronger algorithm in newer versions or generate via OpenSSL/certreq with SignatureAlgorithm = sha256RSAsys.certificates WHERE issuer_name = subject (self-signed) AND the certificate is referenced by an endpoint, linked server, or backup job in a production contextsys.certificates WHERE pvt_key_encryption_type_desc NOT IN ('NO_PRIVATE_KEY') (certificate has a private key) AND no BACKUP CERTIFICATE operation documented in SQL Agent job history or maintenance scriptsBACKUP CERTIFICATE [cert] TO FILE = '…' WITH PRIVATE KEY (FILE = '…', ENCRYPTION BY PASSWORD = '…'); store the .cer file, .pvk file, and password in separate physical locations; document the restore procedure and test itsys.certificates GROUP BY subject HAVING COUNT(*) > 1 WHERE name NOT LIKE '##%'CREATE CERTIFICATE … FROM FILE during disaster recovery may create additional duplicatesTDE_ProductionDB_2025); include the subject in the cert name to keep them uniquesys.asymmetric_keys WHERE key_length IN (512, 1024) AND name NOT LIKE '##%'CREATE ASYMMETRIC KEY [new_key] WITH ALGORITHM = RSA_2048; re-encrypt any symmetric keys protected by the old asymmetric key: ALTER SYMMETRIC KEY [sk] ADD ENCRYPTION BY ASYMMETRIC KEY [new_key]; ALTER SYMMETRIC KEY [sk] DROP ENCRYPTION BY ASYMMETRIC KEY [old_key]; then DROP ASYMMETRIC KEY [old_key]sys.database_permissions WHERE class_desc IN ('SYMMETRIC_KEY', 'ASYMMETRIC_KEY') AND permission_name = 'CONTROL' AND grantee_principal_id NOT IN (list of sysadmin-mapped users in the database)REFERENCES for Always Encrypted CMK metadata access; ENCRYPT + DECRYPT for CLE operations; EXECUTE for stored procedures that wrap the key operations; revoke CONTROLsys.symmetric_keys WHERE modify_date = create_date AND create_date < DATEADD(YEAR, -2, GETDATE()) AND name NOT LIKE '##%'DROP SYMMETRIC KEY [old_key] after verifying all dependent data has been re-encryptedname from sys.symmetric_keys or sys.asymmetric_keys (excluding ##% system keys) does not appear in any sys.sql_modules.definition or sys.server_sql_modules.definition — no ENCRYPTBYKEY, DECRYPTBYKEY, SIGNBYASYMKEY, or VERIFYBYASYMKEY calls referencing the keyDROP SYMMETRIC KEY [key] or DROP ASYMMETRIC KEY [key] after verification; scan application source code in addition to SQL modulesKEY_SOURCE = 'password', KEY_SOURCE = 'test', or any KEY_SOURCE that is identical across multiple environments (detected via code review, git history, or duplicate key GUIDs visible across database clones)SELECT CONVERT(VARCHAR(36), NEWID()) AS unique_source; never reuse KEY_SOURCE across environments; treat KEY_SOURCE as a secret equal in sensitivity to the key itselfsys.symmetric_keys WHERE name = '##MS_DatabaseMasterKey##' exists (DMK is present) AND no BACKUP MASTER KEY evidence in SQL Agent job history or maintenance documentationBACKUP MASTER KEY TO FILE = 'D:\Keys\[dbname]_master_key.mk' ENCRYPTION BY PASSWORD = 'VaultStoredPassword' — store the .mk file and password in separate secure locations; incorporate into disaster recovery runbookSELECT is_master_key_encrypted_by_server FROM sys.databases WHERE name = DB_NAME() returns 0OPEN MASTER KEY DECRYPTION BY PASSWORD = '...' call before any encrypted objects are accessible; this causes application errors until the DBA intervenesALTER MASTER KEY ADD ENCRYPTION BY SERVICE MASTER KEY — requires that the DMK is currently open or the password is provided; verify with SELECT is_master_key_encrypted_by_server FROM sys.databases WHERE name = DB_NAME() = 1is_master_key_encrypted_by_server = 0 AND no other protection method layered on the DMKALTER MASTER KEY ADD ENCRYPTION BY SERVICE MASTER KEY to add automatic decryption; separately back up the DMK (A44) so the password provides a recovery path even after instance migrationBACKUP SERVICE MASTER KEY in any SQL Agent job or maintenance script; the SMK is the root of the entire instance-level key hierarchy (protects all DMKs, linked-server passwords, and proxy account credentials)BACKUP SERVICE MASTER KEY TO FILE = 'D:\Keys\smk_[hostname]_[date].smk' ENCRYPTION BY PASSWORD = 'VaultStoredPassword' — run once after SQL Server installation and again after any SMK regeneration; store file and password in separate secure locations outside the SQL Server machinesys.servers WHERE is_linked = 1 AND server is on a different host (different from @@SERVERNAME) AND the provider string in sys.linked_logins or SSMS Linked Server properties does not include Encrypt=yes or Use Encryption for Data=TrueEXEC sp_addlinkedserver @server = N'RemoteSrv', @srvproduct = N'SQL Server', @provider = N'MSOLEDBSQL', @provstr = N'Encrypt=Mandatory;TrustServerCertificate=no' — ensure the remote SQL Server has a valid CA-signed TLS cert; use MSOLEDBSQL (OLE DB Driver 19+) rather than the deprecated SQLNCLI11 which is removed in SQL Server 2022sys.cryptographic_providers WHERE is_enabled = 0; OR sys.dm_cryptographic_provider_properties WHERE the provider is in an error, not_connected, or degraded statesys.cryptographic_providers.dll_path; ensure EKM provider enabled = 1 in sys.configurations; restart the EKM provider service; consult vendor documentation for the specific error code; test with OPEN SYMMETRIC KEY using the EKM keysys.dm_database_encryption_keys.encryptor_type = 'ASYMMETRIC_KEY') AND no Azure Automation runbook, AKV rotation policy, or SQL Agent job configured to detect key version changes and update the TDE protectoraz keyvault key set-attributes --name [key] --vault-name [vault] --expires [date]; configure Azure Monitor alerts on key expiry; use Set-AzSqlServerTransparentDataEncryptionProtector to re-point TDE to the new key versionSet-AzSqlServerTransparentDataEncryptionProtector -ServerName [server] -ResourceGroupName [rg] -Type AzureKeyVault -KeyId [akv_key_uri]sys.cryptographic_providers.provider_version does not match the latest version published by the EKM vendor (compare against vendor release notes; common EKM providers: nCipher, Thales Luna, Azure Key Vault EKM connector)ALTER CRYPTOGRAPHIC PROVIDER [provider] FROM FILE = 'path\new_provider.dll'; follow vendor's upgrade guide; verify with SELECT * FROM sys.cryptographic_providers after updatesys.sensitivity_classifications WHERE information_type IN ('Financial', 'Health', 'Credentials', 'Banking', 'National ID', 'Government', 'Payment') AND the classified column has column_encryption_key_id IS NULL (no AE) AND the column name does not appear in any ENCRYPTBYKEY call in sys.sql_modulessys.masked_columns) is sufficient for some read-paths as a complementary controlsys.columns WHERE name matches any of: ssn, social_security, credit_card, card_number, cvv, cvc, pin, password, passwd, pw, dob, date_of_birth, birth_date, salary, wage, compensation, tax_id, ein, tin, passport, national_id, nhs, medical_record, mrn, diagnosis, prescription, account_number, routing_number, iban, swift — AND column_encryption_key_id IS NULL AND column_id not in sys.sensitivity_classificationsADD SENSITIVITY CLASSIFICATION TO [schema].[table].[column] WITH (LABEL = '…', INFORMATION_TYPE = '…', RANK = HIGH); apply encryption (A14 fix paths); run a data discovery scan (SQL Data Discovery & Classification in SSMS) to confirm column contentssys.dm_database_encryption_keys.key_algorithm = TRIPLE_DES_3KEY; sys.symmetric_keys.algorithm_desc IN ('DES', 'RC4', 'DESX', 'RC2', 'Triple_DES'); certificate signature algorithm is MD5 or SHA1 (verify via certutil or PowerShell — CERTPROPERTY() does not expose signature algorithm); msdb.dbo.backupset.key_algorithm = TRIPLE_DES_3KEY; sys.asymmetric_keys.key_length ≤ 1024sys.server_audits and sys.database_audit_specifications — no DATABASE_OBJECT_ACCESS_GROUP, SCHEMA_OBJECT_ACCESS_GROUP, or DATABASE_PRINCIPAL_CHANGE_GROUP action group is configured to target symmetric key, asymmetric key, or certificate objectsALTER DATABASE AUDIT SPECIFICATION [enc_audit] FOR SERVER AUDIT [instance_audit] ADD (SCHEMA_OBJECT_ACCESS_GROUP); enable the audit: ALTER SERVER AUDIT [instance_audit] WITH (STATE = ON); for finer granularity, add an audit action on sys.symmetric_keys, sys.asymmetric_keys, and sys.certificates object classesHKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server\Enabled = 1 OR TLS 1.1\Server\Enabled = 1; OR xp_regread output showing either protocol enabled; OR ERRORLOG does not contain "TLS 1.0 is disabled" (SQL 2016+)Enabled = 0 and DisabledByDefault = 1 for TLS 1.0 and TLS 1.1 under the SChannel registry path; restart SQL Server; verify with nmap --script ssl-enum-ciphers -p 1433 <host> or openssl s_client -tls1_0 -connect <host>:1433HKLM\...\SCHANNEL\Ciphers OR TLS cipher order in Group Policy contains RC4, 3DES, DES, NULL, EXPORT ciphers; OR nmap cipher scan reveals weak ciphers offered on port 1433HKLM\...\SCHANNEL\Protocols\TLS 1.3\Server\Enabled = 1; ensure Windows Server 2022 or later; update SQL Server drivers to versions supporting TLS 1.3 (ODBC 18+, JDBC 12.4+, .NET 8+); verify with openssl s_client -tls1_3 -connect <host>:1433sys.dm_exec_connections shows encrypt_option = 'FALSE' AND no evidence of IPsec policy securing TCP port 1433 between application servers and SQL Server (no Windows Firewall Connection Security Rule, no netsh ipsec static show policy output, no Group Policy IPsec rule)New-NetIPsecRule -DisplayName "SQL Encryption" -LocalPort 1433 -Protocol TCP -RequireEncryption -Authentication RequireAuthDomain OR configure via Group Policy → Windows Firewall with Advanced Security → Connection Security Rules; test with netsh ipsec dynamic show mmsasComputer Configuration → Windows Settings → Security Settings → Local Policies → Security Options → Network security: Restrict NTLM — Kerberos armoring not configured) AND sys.dm_exec_connections.auth_scheme = 'KERBEROS' for domain accountsComputer Configuration → Administrative Templates → System → Kerberos → Support for Kerberos armoring (FAST) = Enabled and require armoring; requires domain functional level Windows Server 2012+; verify with klist get krbtgt showing Armor: yessys.dm_server_registry shows SuperSocketNetLib\Np present and Enabled; Named Pipes traffic is unencrypted by default (SMB encryption is separate)Set-SmbServerConfiguration -EncryptData $truesys.configurations WHERE name = 'column encryption enclave type' AND value_in_use > 0 (enclave enabled) AND the client connection string or application configuration does not include Enclave Attestation Url parameter — OR the attestation URL is missing, unreachable, or using HGS/ICM in an unsupported configuration for the SQL Server versionEnclave Attestation Url = https://<attest-server>/attest/SgxEnclave; verify attestation works with a simple query like SELECT GETDATE() with enclave-enabled connection; monitor attestation failures in SQL Server ERRORLOGsys.dm_exec_sessions.program_name reveals outdated driver versions: ODBC < 17.10, JDBC < 12.2, .NET < 7.0, or OLEDB < 19; AND Always Encrypted columns exist in the database — outdated drivers may lack enclave support, AEAD support, or CEK cachingColumn Encryption Setting=Enabled in the updated connection string; test all AE queries after the driver updateColumn Encryption Key Cache Time-To-Live = 7200 (seconds); JDBC — columnEncryptionKeyCacheTtl = 7200; .NET — SqlConnection.ColumnEncryptionKeyCacheTtl = TimeSpan.FromHours(2); monitor AKV API call volume after enabling to confirm reductionsys.configurations WHERE name = 'column encryption enclave type' AND value_in_use > 0 (enclave enabled, consuming VBS memory and attestation infrastructure) AND no columns use ENCRYPTION_TYPE = RANDOMIZED with enclave-enabled queries OR no LIKE, BETWEEN, or range queries on encrypted columns are observed~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.