wGrow
menu
SQL Server hardening checklist.
Infra & Security 4 May 2026 · 9 min

SQL Server hardening checklist.

By Timothy Mo ·

Our working SQL Server hardening checklist — the controls that aged well kept, the ones that aged badly cut, and the ones that need new framing in light of MGF and modern Azure SQL.

Three things shape this checklist beyond the boring fundamentals:

  1. MGF (IMDA Model Governance Framework for Agentic AI) was published in January 2026. Audit trails and identity-scoped access — already best practices — are now procurement-relevant in Singapore.
  2. Managed instances are the default. Azure SQL MI, RDS for SQL Server, and (for some) ScaleArc/SQL on managed Linux mean a meaningful portion of the older Windows-only, self-managed assumptions no longer match the typical deployment.
  3. PDPA enforcement matured. PDPC has been more active on cases involving inadequate access controls and excessive sysadmin scope. The “principle of least privilege” item needs sharper teeth.

Below: the checklist, item by item.

The checklist

1. Patch posture is a pipeline, not a checkbox

“Keep your servers patched” is true and almost meaningless. We expect:

  • Cumulative Updates applied within 30 days of release for non-critical, immediately for critical.
  • A documented rollback runbook for the last two CU versions you ran.
  • Patch state visible from outside the server — we ingest patch state into a monitoring view alongside other compliance signals.

If patch posture only lives in someone’s head, treat it as not-patched.

2. Authentication: Windows / Entra ID, not SQL logins

Sharper rules:

  • No SQL-auth logins in new builds. Mixed mode only when an external system provably can’t speak Entra ID / Windows auth.
  • Service accounts use gMSA where possible.
  • No shared application logins. Every service has its own identity, every identity has its own role.

This last one matters more now: under MGF, agent processes are first-class principals. Sharing an AppLogin between three services and an agent loop makes audit unworkable.

3. Password policy: rotate on event, not on schedule

Length / complexity / history is the floor. We now:

  • Set length floor at 16 for service accounts, 12 for human accounts (with MFA).
  • Stop scheduled rotation for high-entropy service-account secrets (NIST has been against scheduled rotation for years; we finally got our procurement teams to agree).
  • Rotate on event — incident, role change, departure, vendor change.

4. Sysadmin scope: the principle of least, with audit teeth

Beyond “limit sysadmin to absolute minimum”:

  • A monthly sysadmin roster review with HR/IT, signed.
  • Break-glass sysadmin accounts that are disabled by default and audited every login.
  • No personal sysadmin on production. SREs use a named role with PIM-style time-boxed elevation.

5. Permissions: data-class-aware, not just role-based

Role-based access is the floor. We treat permission grants as a function of what data is being read as well as who is reading it:

  • Classify columns by sensitivity tier. (We use four tiers: public, internal, restricted, regulated.)
  • Mask at view-time for restricted/regulated tiers; the full value never leaves the database.
  • Audit access to regulated columns at row-level when feasible.

6. Server Audit, with a real ingestion pipeline

The rule:

  • Audit logs ship off-box within five minutes to a write-only sink.
  • The DBA cannot delete audit logs. Period.
  • An automated check verifies daily that the audit pipeline is alive.

Sample audit creation:

USE [master];
GO
CREATE SERVER AUDIT [Audit_Server_Access]
TO FILE (
    FILEPATH         = 'C:\Audit',
    MAXSIZE          = 100 MB,
    MAX_ROLLOVER_FILES = 2147483647,
    RESERVE_DISK_SPACE = OFF
)
WITH (
    QUEUE_DELAY = 1000,
    ON_FAILURE  = CONTINUE
);
GO

CREATE SERVER AUDIT SPECIFICATION [Audit_Login_Logout]
FOR SERVER AUDIT [Audit_Server_Access]
ADD (FAILED_LOGIN_GROUP),
ADD (SUCCESSFUL_LOGIN_GROUP)
WITH (STATE = ON);
GO

If you stop here, you have evidence sitting on the server. Add the off-box ship.

7. Encryption: TDE everywhere, plus column-level for regulated tiers

  • TDE is the floor, not the ceiling.
  • Always Encrypted (or column-level encryption) for regulated-tier data.
  • Customer-Managed Keys in HSM or Key Vault for any system that ships under MGF or PDPA-regulated workloads.
  • Document key custody — who has rotation rights, where does the master live, what’s the recovery story.

The legacy TDE setup (still works, still needed):

USE [master];
GO
CREATE MASTER KEY ENCRYPTION BY PASSWORD = '<strong-password>';
GO
CREATE CERTIFICATE TDECert WITH SUBJECT = 'TDE Certificate';
GO
USE [YourDatabase];
GO
CREATE DATABASE ENCRYPTION KEY
WITH ALGORITHM = AES_256
ENCRYPTION BY SERVER CERTIFICATE TDECert;
GO
ALTER DATABASE [YourDatabase] SET ENCRYPTION ON;
GO

8. Network: default-deny, allow-list, no public IP

  • No public IP on a SQL host. Ever. If the application needs internet egress, it goes through a separate worker.
  • Allow-list inbound by source application/subnet, not by user.
  • TLS 1.2+ required for client connections; TLS 1.0/1.1 disabled on host.

9. Logs and monitoring as a feedback loop

“Monitor your logs” is true and useless. We:

  • Pipe SQL Server error log + audit + Windows event log into a single SIEM view.
  • Alert on the patterns: failed-login bursts, role grants outside change windows, schema changes outside change windows, audit-pipeline drops.
  • Tune alerts monthly. A dashboard nobody reads is worse than nothing.

10. Network segmentation and east-west isolation

We treat agent processes (LLM-driven services) as a new east-west neighbour and isolate them from the database in their own subnet, with explicit allow-list. Don’t let an “ops agent” share a network segment with a SQL host without firewalls between them.

11. Linked servers: minimize, audit, justify

  • Inventory linked servers monthly. sp_linkedservers output, diffed.
  • Drop on deprecation, not on convenience.
  • Document why each link exists, in a place outside the database server.

12. Agent loops on the database

If an LLM-driven agent has read or write access to a SQL Server, treat it as a privileged actor:

  • Its own login, scoped to a minimal set of stored procs / read views.
  • All actions audited, with the agent identity in the audit row.
  • No raw sp_executesql-style dynamic SQL from the agent layer — actions go through a vetted procedure surface.
  • Eval the surface, like you’d eval any other agent capability. Prompt-injected SQL is a real failure mode.

This is the cheapest control to put in early and the most expensive to retrofit.

What doesn’t move

The instinct, mostly. Eighteen years of patching SQL Servers in Singapore gov, MNC and SME estates has not changed our view that boring controls compound. Patch. Authenticate. Encrypt. Audit. Off-box the audit. Read your logs. The dramatic-sounding controls (zero-trust, AI-driven anomaly detection, etc.) are mostly only useful on top of the boring ones.

— Timothy Mo, wGrow