{"id":111232,"date":"2026-07-17T12:00:00","date_gmt":"2026-07-17T12:00:00","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=111232"},"modified":"2026-07-10T11:46:22","modified_gmt":"2026-07-10T11:46:22","slug":"how-sql-server-maintenance-becomes-an-attack-path-and-what-to-do-about-it","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/how-sql-server-maintenance-becomes-an-attack-path-and-what-to-do-about-it\/","title":{"rendered":"How SQL Server maintenance becomes an attack path\u00a0&#8211; and what to do about it"},"content":{"rendered":"\n<p><strong>This article takes one practical thread from <a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/sql-server-security-vulnerabilities-you-werent-aware-of\/\" target=\"_blank\" rel=\"noreferrer noopener\">Fabiano Amorim&#8217;s broader SQL Server security research<\/a>: how apparently normal permissions and routine maintenance can become an attack path. The goal is not to walk through&nbsp;a full&nbsp;proof of concept, but to help DBAs answer a more useful production question: <em>how do I know whether my SQL Server instances are exposed to this kind of risk?&nbsp;<\/em><\/strong><\/p>\n\n\n\n<p><strong>The<\/strong> <strong>article covers four connected areas: over-privileged database users,&nbsp;msdb&nbsp;and SQL Server Agent exposure, privileged maintenance jobs, and trigger-based permission hijacking. <\/strong><\/p>\n\n\n\n<p><strong>For each area, we will look at why the risk exists, what signs a DBA should look for, what specifically to&nbsp;monitor, and how to mitigate the exposure.<\/strong><\/p>\n\n\n\n<p><em>This article is<\/em> <em>part of <a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/fabiano-amorims-complete-guide-to-sql-server-security\/\" target=\"_blank\" rel=\"noreferrer noopener\">Fabiano Amorim&#8217;s complete guide to SQL Server security.<\/a><\/em> <em>However, the article also takes learnings from Fabiano&#8217;s other series, in which he uncovers <a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/sql-server-security-vulnerabilities-you-werent-aware-of\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Server vulnerabilities you may not be aware of<\/a>.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-sql-server-secure-by-default\">SQL Server &#8211; secure by default? <\/h2>\n\n\n\n<p><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Server<\/a> is often marketed or perceived as <a href=\"https:\/\/techcommunity.microsoft.com\/blog\/sqlserver\/secure-by-default-what%E2%80%99s-new-in-sql-server-2025-security\/4424340\" target=\"_blank\" rel=\"noreferrer noopener\"><em>secure by default<\/em><\/a>, but real-world testing shows that <strong>default installations<\/strong> and&nbsp;<strong>default permissions<\/strong> <em>still <\/em>expose several attack surfaces. With this in mind, \u201csecure by default\u201d does <em>not<\/em> mean \u201csafe without review.\u201d&nbsp;<\/p>\n\n\n\n<p>Many risks come from legacy behavior, backward compatibility, broad <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/sql-server-access-control-basics\/#:~:text=table%20or%20function).-,Permissions%3A,-Types%20of%20access\" target=\"_blank\" rel=\"noreferrer noopener\">permissions<\/a> granted to the\u202fpublic\u202frole, system procedures, implicit trust boundaries, and administrative convenience features.&nbsp;<\/p>\n\n\n\n<p>The risk appears when they are combined: a database user with more permissions than necessary, a maintenance job running as a highly <a href=\"https:\/\/www.red-gate.com\/simple-talk\/security-and-compliance\/sql-server-privilege-escalation-via-replication-jobs\/\" target=\"_blank\" rel=\"noreferrer noopener\">privileged<\/a> owner, an unexpected trigger, or an impersonated module that changes how permissions are evaluated.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-attack-path-explained\">The attack path explained<\/h2>\n\n\n\n<p><strong>Real SQL Server security incidents rarely require one catastrophic bug. They often come from chaining smaller weaknesses that were each accepted as normal operational practice.<\/strong> <\/p>\n\n\n\n<p>A simplified chain looks like this:&nbsp;<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>An application login, support login, or database user is granted&nbsp;<code>db_owner<\/code>,&nbsp;<code>db_ddladmin<\/code>,&nbsp;<code>db_securityadmin<\/code>, or broad <code><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/security\/permissions-database-engine?view=sql-server-ver17#:~:text=within%20the%20database.-,ALTER,-Confers%20the%20ability\" target=\"_blank\" rel=\"noreferrer noopener\">ALTER<\/a><\/code> permissions in a production database.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>That principal can create or&nbsp;modify&nbsp;objects such as stored procedures, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/theory-and-design\/data-control-language-aka-security\/\" target=\"_blank\" rel=\"noreferrer noopener\">DDL<\/a> (data definition language) triggers, DML (data manipulation language) triggers, or modules that execute as&nbsp;<em>dbo<\/em>&nbsp;or <em>owner<\/em>.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>A maintenance job later touches the database while running as&nbsp;<a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/why-disabling-the-sql-server-sa-account-still-matters-in-2026\/\" target=\"_blank\" rel=\"noreferrer noopener\">sa<\/a>, a sysadmin login, or a highly privileged <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/setting-up-your-sql-server-agent-correctly\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Agent<\/a> proxy.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>The trigger or module runs during that trusted operation and inherits the permissions of the caller that fired it.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>The result is&nbsp;<a href=\"https:\/\/www.red-gate.com\/simple-talk\/security-and-compliance\/sql-server-privilege-escalation-via-replication-jobs\/\" target=\"_blank\" rel=\"noreferrer noopener\">privilege&nbsp;escalation<\/a>, audit evasion, unwanted configuration changes, or unauthorized data access.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<p>The&nbsp;important point&nbsp;is that none of these items may look suspicious when reviewed in isolation. DBAs create maintenance jobs. Applications sometimes need deployments. SQL Agent often runs administrative workflows. <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/sql-server-triggers-good-scary\/\" target=\"_blank\" rel=\"noreferrer noopener\">Triggers are a normal SQL Server feature<\/a>. The security problem is the trust boundary between them.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-over-privileged-database-users-in-sql-server-why-and-how-normal-permissions-can-become-dangerous\">Over-privileged database users in SQL Server: why (and how) normal permissions can become dangerous<\/h2>\n\n\n\n<p><strong>The&nbsp;<code>db_owner<\/code>&nbsp;fixed database role is&nbsp;frequently&nbsp;treated as a convenient application role. It is not. A&nbsp;<code>db_owner<\/code>&nbsp;member controls the database security boundary. <\/strong><\/p>\n\n\n\n<p><strong>The user can create and alter objects, define triggers, change permissions, create modules with elevated execution context, and often influence operations performed later by privileged users or automated jobs.&nbsp;<\/strong><\/p>\n\n\n\n<p>The same concern applies to&nbsp;<code>db_ddladmin<\/code>,&nbsp;<code>db_securityadmin<\/code>,&nbsp;<code>db_accessadmin<\/code>, <code>CONTROL DATABASE<\/code>, <code>ALTER ANY USER<\/code>, <code>ALTER ANY ROLE<\/code>, <code>ALTER ANY SCHEMA<\/code>, and broad <code>ALTER<\/code> permissions on important schemas. <\/p>\n\n\n\n<p>These rights may be necessary for controlled deployment accounts, but they are usually too powerful for runtime application accounts and permanent support accounts.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-are-the-warning-signs-a-dba-should-look-for-nbsp\">What are the warning signs a DBA should look for?&nbsp;<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Application service accounts, reporting users, vendor users, or generic support logins in&nbsp;<code>db_owner<\/code>.&nbsp;<br><\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Permanent membership in&nbsp;<code>db_ddladmin<\/code>,&nbsp;<code>db_securityadmin<\/code>, or&nbsp;<code>db_accessadmin<\/code>.&nbsp;<br><\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Runtime application&nbsp;accounts that&nbsp;also perform <a href=\"https:\/\/www.red-gate.com\/products\/sql-compare\/\" target=\"_blank\" rel=\"noreferrer noopener\">schema deployments<\/a>.&nbsp;<br><\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Users granted <code>CONTROL DATABASE<\/code>, <code>ALTER ANY SCHEMA<\/code>, <code>ALTER ANY USER<\/code>, <code>ALTER ANY ROLE<\/code>, or <code>IMPERSONATE<\/code>.&nbsp;<br><\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Databases where <code>TRUSTWORTHY<\/code> is ON, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/cross-database-ownership-chaining-in-sql-server-security-risks-behavior-and-privilege-escalation-explained\/\" target=\"_blank\" rel=\"noreferrer noopener\">cross-database ownership chaining<\/a> is enabled, or the owner is a sysadmin login.&nbsp;<br><\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Unexpected created or modified procedures, triggers, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/mastering-sql-views\/\" target=\"_blank\" rel=\"noreferrer noopener\">views<\/a>, synonyms, or assemblies in production.&nbsp;<br><\/li>\n<\/ul>\n<\/div>\n\n\n<section id=\"my-first-block-block_79213064762ac696bfee2575539eb25b\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Protect your data. Demonstrate compliance.<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            With Redgate, stay ahead of threats with real-time monitoring and alerts, protect sensitive data with automated discovery &#038; masking, and demonstrate compliance with traceability across every environment.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/solutions\/use-cases\/security-and-compliance\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Learn more: Protect your data. Demonstrate compliance.\">Learn more<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-to-nbsp-monitor-and-how\">What to&nbsp;monitor, and how<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Area<\/strong>&nbsp;<\/td><td><strong>What to check<\/strong>&nbsp;<\/td><td><strong>How to monitor<\/strong>&nbsp;<\/td><\/tr><tr><td>Role membership&nbsp;<\/td><td>Members of:&nbsp;<br><code>db_owner<\/code><br><code>db_ddladmin<br>db_securityadmin<br>db_accessadmin<br>SQLAgentUserRole<br>SQLAgentReaderRole<br>SQLAgentOperatorRole<\/code><\/td><td>Daily catalog checks and SQL Audit <code>DATABASE_ROLE_MEMBER_CHANGE_GROUP.&nbsp;<\/code><br><br>Alert on&nbsp;changes outside approved deployment windows.&nbsp;<\/td><\/tr><tr><td>Database permissions&nbsp;<\/td><td><code>CONTROL DATABASE<br>ALTER<br>ALTER ANY SCHEMA<br>ALTER ANY USER<\/code><br><code>ALTER ANY ROLE<\/code><br><code>IMPERSONATE<\/code><br><code>CREATE ASSEMBLY<\/code><br><code>CREATE PROCEDURE<\/code><br><code>CREATE TRIGGER<\/code><\/td><td>Query&nbsp;<code>sys.database_permissions<\/code>&nbsp;in every user database. Baseline expected permissions and diff against that baseline.&nbsp;<\/td><\/tr><tr><td>Risky database settings&nbsp;<\/td><td><code>TRUSTWORTHY<\/code><br><code>DB_CHAINING<\/code><br><code>owner_sid<br>containment<\/code><br>Service Broker activation procedures CLR settings<\/td><td>Query&nbsp;<code>sys.databases<\/code>&nbsp;and database scoped metadata. Treat deviations as configuration drift.&nbsp;<\/td><\/tr><tr><td>Object change activity&nbsp;<\/td><td><code>CREATE<\/code>, <code>ALTER<\/code>, and <code>DROP<\/code> activity for modules, triggers,&nbsp;schemas, users, roles, and assemblies.&nbsp;<\/td><td>SQL Audit <code>DATABASE_OBJECT_CHANGE_GROUP<\/code><br><code>SCHEMA_OBJECT_CHANGE_GROUP<\/code><br><code>DATABASE_PRINCIPAL_CHANGE_GROUP<\/code><br><code>DATABASE_PERMISSION_CHANGE_GROUP<\/code><br>Use DDL triggers only for logging, not as the only control.&nbsp;<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* High-risk database role membership in the current database *\/ \nSELECT \n    DB_NAME() AS database_name, \n    role_principal.name AS role_name, \n    member_principal.name AS member_name, \n    member_principal.type_desc AS member_type \nFROM sys.database_role_members AS drm \nJOIN sys.database_principals AS role_principal \n    ON drm.role_principal_id = role_principal.principal_id \nJOIN sys.database_principals AS member_principal \n    ON drm.member_principal_id = member_principal.principal_id \nWHERE role_principal.name IN \n( \n    N'db_owner', N'db_ddladmin', N'db_securityadmin', \n    N'db_accessadmin', N'db_backupoperator' \n) \nORDER BY role_principal.name, member_principal.name;<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \"> \n\/* Database-level permissions that deserve review *\/ \nSELECT \n    DB_NAME() AS database_name, \n    USER_NAME(dp.grantee_principal_id) AS grantee_name, \n    dp.state_desc, \n    dp.permission_name, \n    dp.class_desc, \n    OBJECT_SCHEMA_NAME(dp.major_id) AS securable_schema, \n    OBJECT_NAME(dp.major_id) AS securable_name \nFROM sys.database_permissions AS dp \nWHERE dp.permission_name IN \n( \n    N'CONTROL', N'ALTER', N'ALTER ANY SCHEMA', N'ALTER ANY USER', \n    N'ALTER ANY ROLE', N'IMPERSONATE', N'CREATE PROCEDURE', \n    N'CREATE FUNCTION', N'CREATE TRIGGER', N'CREATE ASSEMBLY' \n) \nORDER BY grantee_name, permission_name; <\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Instance-level database settings that affect trust boundaries *\/ \nSELECT \n    name AS database_name, \n    SUSER_SNAME(owner_sid) AS database_owner, \n    is_trustworthy_on, \n    is_db_chaining_on, \n    containment_desc \nFROM sys.databases \nWHERE database_id &gt; 4 \nORDER BY name;<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-recommended-fixes-and-mitigations-nbsp\">Recommended fixes and mitigations&nbsp;<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Separate deployment permissions from runtime permissions. The account that deploys schema changes should not be the same account used by the application during normal operations.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Replace&nbsp;<code>db_owner<\/code>&nbsp;with custom roles that grant only the required <code>SELECT<\/code>, <code>INSERT<\/code>, <code>UPDATE<\/code>, <code>DELETE<\/code>, and <code>EXECUTE<\/code> permissions.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Make elevated access temporary, approved, and auditable. Remove it after the deployment or support task is complete.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Keep <code>TRUSTWORTHY<\/code> OFF unless there is a documented, reviewed, and tested requirement. Do not use <code>TRUSTWORTHY<\/code> as a shortcut for cross-database access.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Use dedicated low-use database owner logins. Avoid making user databases owned by&nbsp;<a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/why-disabling-the-sql-server-sa-account-still-matters-in-2026\/\" target=\"_blank\" rel=\"noreferrer noopener\">sa<\/a>, domain administrators, or application runtime accounts.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Build a permission baseline and alert on drift, rather than relying on occasional manual reviews.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-impersonation-and-module-execution-context-why-execute-as-can-defeat-your-assumptions-nbsp\">Impersonation and module execution context: why EXECUTE AS can defeat your assumptions&nbsp;<\/h2>\n\n\n\n<p><strong><a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/sql-server-security-primer\/#:~:text=in%20your%20design.-,EXECUTE%20AS,-The%20command%20EXECUTE\" target=\"_blank\" rel=\"noreferrer noopener\">Impersonation<\/a> is a legitimate SQL Server feature. It allows a module to run under another database or server&nbsp;principal&nbsp;so users can perform a controlled task without receiving broad direct permissions. <\/strong><\/p>\n\n\n\n<p>The danger appears when impersonation is used as a blanket elevation mechanism, especially through <code>EXECUTE AS OWNER<\/code> or <code>EXECUTE AS&nbsp;dbo<\/code>, or when dynamic SQL is executed inside an impersonated module.&nbsp;<\/p>\n\n\n\n<p>DBAs sometimes assume an explicit <code>DENY<\/code> is a reliable backstop. That assumption can fail if the principal can later run the operation under a different execution context. <\/p>\n\n\n\n<p>For example, a user who can become&nbsp;<code>dbo<\/code>, directly or indirectly, can perform actions as&nbsp;<code>dbo<\/code>&nbsp;<em>even when those actions would be denied to the original user<\/em>. This is one reason&nbsp;<code>db_owner<\/code>&nbsp;membership is so sensitive: it changes what the user can become, not only what the user can do directly.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-are-the-warning-signs-a-dba-should-look-for\">What are the warning signs a DBA should look for?<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Modules created with <code> EXECUTE AS OWNER<\/code>, <code>EXECUTE AS&nbsp;dbo<\/code>, or an unusually privileged user.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Dynamic SQL inside impersonated <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/40-problems-sql-server-stored-procedure\/\" target=\"_blank\" rel=\"noreferrer noopener\">stored procedures<\/a>, functions, or triggers.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Explicit <code>IMPERSONATE<\/code> permissions granted to application, vendor, support, or developer accounts.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Databases with <code>TRUSTWORTHY<\/code> ON, and modules that&nbsp;execute&nbsp;as owner or&nbsp;dbo.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Users manually&nbsp;running&nbsp;<code>EXECUTE AS USER<\/code> or <code>EXECUTE AS LOGIN<\/code> <em>outside<\/em> of approved administrative workflows.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-to-nbsp-monitor-and-how-nbsp\">What to&nbsp;monitor, and how&nbsp;<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Area<\/strong>&nbsp;<\/td><td><strong>What to check<\/strong>&nbsp;<\/td><td><strong>How to monitor<\/strong>&nbsp;<\/td><\/tr><tr><td>Module context&nbsp;<\/td><td>Stored procedures, triggers, functions, and views with&nbsp;<code>execute_as_principal_id<\/code>&nbsp;set.&nbsp;<\/td><td>Query&nbsp;<code>sys.sql_modules<\/code>&nbsp;regularly in each database. <br><br>Review every module using <code>EXECUTE AS<\/code> that is not part of an approved pattern.&nbsp;<\/td><\/tr><tr><td>Impersonation grants&nbsp;<\/td><td>Database and server <code>IMPERSONATE<\/code> permissions, especially on&nbsp;dbo,&nbsp;sa, privileged logins, or service accounts.&nbsp;<\/td><td>Query&nbsp;<code>sys.database_permissions<\/code>and&nbsp;<code>sys.server_permissions<\/code>. <br><br>Alert when <code>IMPERSONATE<\/code> is granted or denied outside approved changes.&nbsp;<\/td><\/tr><tr><td>Runtime use&nbsp;<\/td><td><code>EXECUTE AS USER<\/code><br><code>EXECUTE AS LOGIN<\/code><br><code>REVERT<\/code><br>Other modules running under unexpected principals.&nbsp;<\/td><td>Extended Events:<br><code>audit_database_principal_impersonation_event<\/code>&nbsp; and&nbsp;<code>audit_server_principal_impersonation_event<\/code>. <br><br>SQL Audit for permission and principal changes.&nbsp;<\/td><\/tr><tr><td>Dynamic SQL under elevation&nbsp;<\/td><td><code>EXEC<\/code><br><code>sp_executesql<\/code><br><code>OPENQUERY<\/code><br>linked server execution<br><code>xp_cmdshell<\/code><br>OLE Automation<br>external scripts<\/td><td>Static code review plus XE&nbsp;<code>sql_statement_completed<\/code>&nbsp;and&nbsp;<code>module_end<\/code>&nbsp;for high-risk modules.&nbsp;<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Modules that execute under a different context *\/ \nSELECT \n    DB_NAME() AS database_name, \n    SCHEMA_NAME(o.schema_id) AS schema_name, \n    o.name AS object_name, \n    o.type_desc, \n    m.execute_as_principal_id, \n    USER_NAME(m.execute_as_principal_id) AS execute_as_principal_name, \n    o.create_date, \n    o.modify_date \nFROM sys.sql_modules AS m \nJOIN sys.objects AS o \n    ON m.object_id = o.object_id \nWHERE m.execute_as_principal_id IS NOT NULL \nORDER BY o.modify_date DESC;<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Server-level impersonation grants *\/ \nSELECT \n    grantor.name AS grantor_name, \n    grantee.name AS grantee_name, \n    sp.state_desc, \n    sp.permission_name, \n    sp.class_desc, \n    target_principal.name AS target_principal \nFROM sys.server_permissions AS sp \nLEFT JOIN sys.server_principals AS grantor \n    ON sp.grantor_principal_id = grantor.principal_id \nLEFT JOIN sys.server_principals AS grantee \n    ON sp.grantee_principal_id = grantee.principal_id \nLEFT JOIN sys.server_principals AS target_principal \n    ON sp.major_id = target_principal.principal_id \nWHERE sp.permission_name = N'IMPERSONATE' \nORDER BY grantee.name, target_principal.name;<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-recommended-fixes-and-mitigations-nbsp-0\">Recommended fixes and mitigations&nbsp;<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Avoid <code>EXECUTE AS&nbsp;dbo<\/code>&nbsp;and <code>EXECUTE AS OWNER<\/code> as a default pattern. Use them only when the trust boundary is fully understood.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Prefer certificate-signed modules for controlled privilege elevation. Module signing can grant a specific permission to a specific module without broadly changing the caller\u2019s execution context.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Remove unnecessary <code>IMPERSONATE<\/code> grants. Treat <code>IMPERSONATE<\/code> on privileged principals as equivalent to a high-privilege administrative grant.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Do not allow unparameterized dynamic SQL inside impersonated modules. <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/performance-implications-of-parameterized-queries\/\" target=\"_blank\" rel=\"noreferrer noopener\">Parameterize<\/a>,&nbsp;validate&nbsp;identifiers with <code>QUOTENAME<\/code> where&nbsp;appropriate, and avoid concatenating user-controlled input.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Review modules again after&nbsp;restores, vendor deployments, and application upgrades. These are common moments for elevated execution context to appear unexpectedly.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-msdb-nbsp-and-sql-server-agent-exposure-why-msdb-is-a-server-level-security-boundary\">msdb&nbsp;and SQL Server Agent exposure: why msdb is a server-level security boundary<\/h2>\n\n\n\n<p><strong><a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/databases\/msdb-database?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">msdb<\/a>&nbsp;is not just a place where job history is stored. It&nbsp;contains&nbsp;SQL Server&nbsp;Agent&nbsp;jobs, job steps, schedules, operators, alerts, credentials, proxies, backup and restore history, Database Mail metadata, maintenance plans, and SSIS-related metadata. A user with excessive permissions in&nbsp;msdb&nbsp;may be able to influence how privileged automation runs on the instance.&nbsp;<\/strong><\/p>\n\n\n\n<p>SQL Server Agent is especially important because a job step may run as the SQL Agent service account, as the job owner, or through a proxy credential depending on the subsystem and configuration. <\/p>\n\n\n\n<p>A job that looks like routine maintenance can become a privilege boundary if a lower-privileged user can&nbsp;modify&nbsp;the job,&nbsp;modify&nbsp;objects that the job touches, or control parameters used by the job.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-are-the-warning-signs-a-dba-should-look-for-0\">What are the warning signs a DBA should look for?<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Non-DBA users in&nbsp;<code>SQLAgentUserRole<\/code>,&nbsp;<code>SQLAgentReaderRole<\/code>, or&nbsp;<code>SQLAgentOperatorRole<\/code>.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Jobs owned by&nbsp;sa&nbsp;or sysadmin logins where the job touches databases controlled by non-sysadmin&nbsp;<code>db_owner<\/code>&nbsp;users.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Job steps that execute dynamic <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">T-SQL<\/a>, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/\" target=\"_blank\" rel=\"noreferrer noopener\">PowerShell<\/a>,&nbsp;CmdExec, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/blogs\/ssis-tricks-would-know\/\" target=\"_blank\" rel=\"noreferrer noopener\">SSIS<\/a> packages, replication agents, or external scripts.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Proxies and credentials that are unused, shared across unrelated jobs, or more privileged than needed.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Jobs created or&nbsp;modified&nbsp;by unexpected users, especially outside deployment windows.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Maintenance jobs that run against all databases without accounting for untrusted database owners or triggers.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-to-nbsp-monitor-nbsp-and-how-nbsp\">What to&nbsp;monitor&nbsp;and how&nbsp;<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Area<\/strong>&nbsp;<\/td><td><strong>What to check<\/strong>&nbsp;<\/td><td><strong>How to monitor<\/strong>&nbsp;<\/td><\/tr><tr><td>Agent roles&nbsp;<\/td><td>Membership in:<br><code>msdb&nbsp;SQLAgentUserRole<\/code><br><code>SQLAgentReaderRole<\/code><br><code>SQLAgentOperatorRole&nbsp;<\/code><\/td><td>Query&nbsp;<code>msdb.sys.database_role_members<\/code>. <br><br>Alert when non-DBA accounts are added.&nbsp;<\/td><\/tr><tr><td>Job ownership&nbsp;<\/td><td>Jobs owned by&nbsp;sa, sysadmin logins, disabled logins, vendor accounts, or generic accounts.&nbsp;<\/td><td>Query&nbsp;<code>msdb.dbo.sysjobs<\/code>&nbsp;and&nbsp;<code>sys.server_principals<\/code>. <br><br>Baseline expected owners.&nbsp;<\/td><\/tr><tr><td>Job changes&nbsp;<\/td><td>Job creation, deletion, step changes, schedule changes, proxy changes.&nbsp;<\/td><td>SQL Audit object\/permission changes in&nbsp;msdb.<br><br>Extended Events&nbsp;<br><code>rpc_completed<\/code>&nbsp;and<br><code>sql_batch_completed<\/code>&nbsp;<br>filtered on:<br><code>sp_add_job<\/code><br><code>sp_update_job<\/code><br><code>sp_add_jobstep<\/code><br><code>sp_update_jobstep<\/code>,&nbsp;<br><code>sp_delete_job<\/code><br><code>sp_start_job<\/code>&nbsp;<\/td><\/tr><tr><td>Job execution&nbsp;<\/td><td>Unexpected manual starts, failures, reruns, long-running steps, commands changed before execution.&nbsp;<\/td><td>msdb.dbo.sysjobhistory,&nbsp;sysjobactivity, SQL Agent logs, and Windows event logs.&nbsp;Alert on&nbsp;failure patterns and changes near execution time.&nbsp;<\/td><\/tr><tr><td>Credentials\/proxies&nbsp;<\/td><td>Proxy mappings to subsystems, credential identities, jobs using high-privilege proxies.&nbsp;<\/td><td>Query&nbsp;msdb&nbsp;proxy and credential metadata. Review with Windows\/local admin team for actual OS rights.&nbsp;<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* SQL Agent fixed role membership *\/ \nUSE msdb; \nGO \nSELECT \n    role_principal.name AS role_name, \n    member_principal.name AS member_name, \n    member_principal.type_desc AS member_type \nFROM sys.database_role_members AS drm \nJOIN sys.database_principals AS role_principal \n    ON drm.role_principal_id = role_principal.principal_id \nJOIN sys.database_principals AS member_principal \n    ON drm.member_principal_id = member_principal.principal_id \nWHERE role_principal.name IN \n( \n    N'SQLAgentUserRole', N'SQLAgentReaderRole', N'SQLAgentOperatorRole' \n) \nORDER BY role_principal.name, member_principal.name;<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* SQL Agent jobs, owners, and high-risk subsystems *\/ \nSELECT \n    j.name AS job_name, \n    SUSER_SNAME(j.owner_sid) AS job_owner, \n    CASE WHEN IS_SRVROLEMEMBER('sysadmin', SUSER_SNAME(j.owner_sid)) = 1 \n         THEN 'sysadmin owner' ELSE 'non-sysadmin owner' END AS owner_risk_note, \n    s.step_id, \n    s.step_name, \n    s.subsystem, \n    LEFT(s.command, 4000) AS command_text \nFROM msdb.dbo.sysjobs AS j \nLEFT JOIN msdb.dbo.sysjobsteps AS s \n    ON j.job_id = s.job_id \nWHERE s.subsystem IN (N'TSQL', N'PowerShell', N'CmdExec', N'SSIS', N'ActiveScripting') \n   OR IS_SRVROLEMEMBER('sysadmin', SUSER_SNAME(j.owner_sid)) = 1 \nORDER BY j.name, s.step_id;<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Recent job changes and execution activity *\/ \nSELECT TOP (200) \n    j.name AS job_name, \n    a.start_execution_date, \n    a.stop_execution_date, \n    a.run_requested_date, \n    a.run_requested_source, \n    a.last_executed_step_id, \n    j.date_created,\n    j.date_modified, \n    SUSER_SNAME(j.owner_sid) AS job_owner \nFROM msdb.dbo.sysjobs AS j \nLEFT JOIN msdb.dbo.sysjobactivity AS a \n    ON j.job_id = a.job_id \nORDER BY COALESCE(a.start_execution_date, j.date_modified, j.date_created) DESC;<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-recommended-fixes-and-mitigations-nbsp-1\">Recommended fixes and mitigations&nbsp;<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Restrict SQL Agent role membership. Do not grant&nbsp;<code>SQLAgentOperatorRole<\/code>&nbsp;or&nbsp;<code>SQLAgentReaderRole<\/code>&nbsp;broadly just because a user needs to see job status.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Use dedicated job owner accounts. Avoid using personal DBA accounts as job owners. Avoid&nbsp;<code>sa<\/code>&nbsp;as the default owner <em>unless<\/em> there&nbsp;is&nbsp;a clear reason&nbsp;and compensating monitoring.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Separate job administration from database ownership. A user who controls a database should not automatically be able to influence a sysadmin-owned job that touches that database.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Review job steps for dynamic SQL and external execution.&nbsp;<a href=\"https:\/\/learn.microsoft.com\/en-us\/ssms\/agent\/create-a-cmdexec-job-step\" target=\"_blank\" rel=\"noreferrer noopener\">CmdExec<\/a>, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/\" target=\"_blank\" rel=\"noreferrer noopener\">PowerShell<\/a>, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/other\/ssis-tricks-would-know\/\" target=\"_blank\" rel=\"noreferrer noopener\">SSIS<\/a>, and external scripts deserve special attention because they can cross the database\/host boundary.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Remove unused proxies and credentials. For required proxies, use least-privilege Windows accounts and limit each one to only the necessary subsystems.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Baseline job definitions and alert when a job&nbsp;command, owner, proxy, subsystem, or schedule changes.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-privileged-maintenance-jobs-and-trigger-hijacking-why-maintenance-can-become-an-escalation-path\">Privileged maintenance jobs and trigger hijacking: why maintenance can become an escalation path<\/h2>\n\n\n\n<p>This is the most subtle part of the chain. <a href=\"https:\/\/en.wikipedia.org\/wiki\/Data_manipulation_language\" target=\"_blank\" rel=\"noreferrer noopener\">DML (data manipulation language)<\/a> and <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/theory-and-design\/data-control-language-aka-security\/\" target=\"_blank\" rel=\"noreferrer noopener\">DDL (data definition or declaration language)<\/a> triggers don&#8217;t execute under the permissions of the trigger creator. Instead, they execute when another statement fires them, and the effective context is tied to the caller and the execution path that fired the trigger. If a highly privileged maintenance job performs an operation that fires a trigger in a database controlled by a lower-privileged&nbsp;<code>db_owner<\/code>, the trigger may be able to hijack the permissions of that privileged operation.&nbsp;<\/p>\n\n\n\n<p>This&nbsp;matters for&nbsp;consolidated&nbsp;SQL Server instances where different teams or applications own different databases, but central DBA jobs run maintenance across all databases. The local&nbsp;<code>db_owner<\/code>&nbsp;may not be sysadmin. The maintenance job may be&nbsp;sysadmin. The trigger is the bridge between them.&nbsp;<\/p>\n\n\n\n<p>The risk is not limited to intentionally malicious users. It also affects untrusted <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/backup-restore\/restore-and-recovery-overview-sql-server?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">restores<\/a>, vendor databases, old applications, <a href=\"https:\/\/www.red-gate.com\/blog\/database-migration\/\" target=\"_blank\" rel=\"noreferrer noopener\">databases migrated<\/a> from other servers, and environments where developers or third parties have broad database rights.&nbsp;<\/p>\n\n\n\n<p><strong><em>Important&nbsp;caveat:&nbsp;<\/em><\/strong><em><code>BACKUP DATABASE<\/code> and <code>DBCC CHECKDB<\/code> don&#8217;t fire the same DDL triggers as operations such as <code>ALTER INDEX<\/code> or <code>UPDATE STATISTICS<\/code>.&nbsp;The exposure depends on what the maintenance job&nbsp;actually does.&nbsp;Inventory the job steps before deciding which databases are safe to touch with privileged context.&nbsp;<\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-are-the-warning-signs-a-dba-should-look-for-1\">What are the warning signs a DBA should look for?<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>DDL&nbsp;triggers at&nbsp;database scope, especially triggers listening to broad event groups such as <code>DDL_DATABASE_LEVEL_EVENTS<\/code>.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>DML&nbsp;triggers on&nbsp;tables touched by cleanup, archiving, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/building-an-etl-with-powershell\/\" target=\"_blank\" rel=\"noreferrer noopener\">ETL (extract, transform, load)<\/a>, reporting refresh, replication, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/learn\/introduction-to-change-data-capture-cdc-in-sql-server-2008\/\" target=\"_blank\" rel=\"noreferrer noopener\">change data capture (CDC)<\/a>, or maintenance processes.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Triggers&nbsp;modified&nbsp;shortly before a privileged job runs.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Triggers&nbsp;containing&nbsp;<code>ALTER SERVER ROLE<\/code>, <code>CREATE LOGIN<\/code>, <code>GRANT<\/code>, <code>IMPERSONATE<\/code>,&nbsp;<code>xp_cmdshell<\/code>, <a href=\"https:\/\/en.wikipedia.org\/wiki\/OLE_Automation\" target=\"_blank\" rel=\"noreferrer noopener\">OLE Automation<\/a>, linked server calls, external script execution, or dynamic SQL.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Maintenance jobs running as&nbsp;<code>sa<\/code> or sysadmin across databases owned by application teams, vendors, or restored from external sources.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Databases with <code>TRUSTWORTHY ON<\/code> and&nbsp;sysadmin&nbsp;ownership. This can defeat the protective sandbox that <code>EXECUTE AS USER<\/code> would otherwise provide.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-to-nbsp-monitor-nbsp-and-how-nbsp-0\">What to&nbsp;monitor&nbsp;and how&nbsp;<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Area<\/strong>&nbsp;<\/td><td><strong>What to check<\/strong>&nbsp;<\/td><td><strong>How to monitor<\/strong>&nbsp;<\/td><\/tr><tr><td>Database triggers&nbsp;<\/td><td>DML and DDL triggers, disabled status, create\/modify&nbsp;dates, parent object, event group coverage.&nbsp;<\/td><td>In each database, query the following:<br>&nbsp;<code>sys.triggers<\/code><br><code>sys.trigger_events<\/code><br><code>sys.sql_modules<\/code><br>Also, alert on <br><code>CREATE\/ALTER\/DROP TRIGGER<\/code>.&nbsp;<\/td><\/tr><tr><td>Server triggers&nbsp;<\/td><td>Server-level DDL triggers and logon triggers.&nbsp;<\/td><td>Query&nbsp;the following:<br><code>sys.server_triggers<\/code><br><code>sys.server_trigger_events<\/code><br><code>sys.server_sql_modules<\/code><br>Also, audit <code>SERVER_OBJECT_CHANGE_GROUP<\/code>.&nbsp;<\/td><\/tr><tr><td>Suspicious trigger content&nbsp;<\/td><td>Dynamic SQL, server-role changes, login creation, <code>GRANT\/ALTER AUTHORIZATION<\/code>,&nbsp;<code>xp_cmdshell<\/code>,&nbsp;sp_OA*, external scripts,&nbsp;linked-server&nbsp;execution.&nbsp;<\/td><td>Static code&nbsp;scanning of&nbsp;trigger definitions. XE\/SQL Audit for module execution and privileged statements.&nbsp;<\/td><\/tr><tr><td>Maintenance execution context&nbsp;<\/td><td>Which login owns the job, whether the step runs as a proxy, whether the job uses <code>EXECUTE AS<\/code>, and which databases are touched.&nbsp;<\/td><td>Review&nbsp;msdb&nbsp;job metadata and job history. Capture job start\/stop and command text changes. Baseline all maintenance jobs.&nbsp;<\/td><\/tr><tr><td>Privileged operations fired inside user DBs&nbsp;<\/td><td><code>ALTER INDEX<\/code>, <code>UPDATE STATISTICS<\/code>, schema changes, data cleanup, ETL changes, replication\/CDC maintenance.&nbsp;<\/td><td>XE&nbsp;<code>sql_statement_completed<\/code><br><code>object_altered<\/code><br><code>object_created<\/code><br><code>object_deleted<\/code><br>SQL Audit <code>DATABASE_OBJECT_CHANGE_GROUP<\/code> and <code>SCHEMA_OBJECT_CHANGE_GROUP<\/code>.&nbsp;<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \"> \n\/* Database triggers in the current database *\/ \nSELECT \n    DB_NAME() AS database_name, \n    SCHEMA_NAME(o.schema_id) AS parent_schema, \n    OBJECT_NAME(t.parent_id) AS parent_object, \n    t.name AS trigger_name, \n    t.parent_class_desc, \n    t.is_disabled, \n    t.create_date, \n    t.modify_date, \n    LEFT(m.definition, 4000) AS trigger_definition_sample \nFROM sys.triggers AS t \nLEFT JOIN sys.objects AS o \n    ON t.parent_id = o.object_id \nLEFT JOIN sys.sql_modules AS m \n    ON t.object_id = m.object_id \nORDER BY t.modify_date DESC; <\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Server-level triggers *\/ \nSELECT \n    st.name AS trigger_name, \n    st.parent_class_desc, \n    st.is_disabled, \n    st.create_date, \n    st.modify_date, \n    LEFT(sm.definition, 4000) AS trigger_definition_sample \nFROM sys.server_triggers AS st \nLEFT JOIN sys.server_sql_modules AS sm \n    ON st.object_id = sm.object_id \nORDER BY st.modify_date DESC;<\/pre><\/div>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Trigger event coverage *\/ \nSELECT \n    DB_NAME() AS database_name, \n    t.name AS trigger_name, \n    te.type_desc AS trigger_event, \n    t.parent_class_desc, \n    t.is_disabled, \n    t.modify_date \nFROM sys.triggers AS t \nLEFT JOIN sys.trigger_events AS te \n    ON t.object_id = te.object_id \nORDER BY t.name, te.type_desc;<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-a-safer-maintenance-pattern-nbsp\">A safer maintenance pattern&nbsp;<\/h3>\n\n\n\n<p>When a central maintenance job must run DDL or DML inside databases that may be controlled by non-sysadmin owners, avoid letting the&nbsp;job\u2019s&nbsp;server-level privileges be hijacked by code inside those databases. One defensive pattern is to execute the database-level maintenance under a database user context, commonly&nbsp;<code>dbo<\/code>, so the operation&nbsp;remains&nbsp;inside the database boundary instead of carrying the sysadmin token into trigger execution.<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Pattern for a carefully reviewed maintenance step *\/ \nUSE [ApplicationDatabase]; \nGO \nEXECUTE AS USER = 'dbo'; \nGO \nALTER INDEX ALL ON dbo.SomeTable REBUILD; \nGO \nREVERT; \nGO<\/pre><\/div>\n\n\n\n<p>This pattern is useful because a database-level <code>EXECUTE AS<\/code> context does <em>not<\/em> automatically carry server-level privileges. However, it&#8217;s not&nbsp;magic. If the database is <code>TRUSTWORTHY ON<\/code> and owned by a sysadmin, or if unsafe cross-database ownership chains exist, the protection may be weakened or bypassed.<strong> Always review database owner, <code>TRUSTWORTHY<\/code>, <code>DB_CHAINING<\/code>, and module execution context before relying on this pattern.<\/strong>&nbsp;<\/p>\n\n\n\n<p>If you use <a href=\"https:\/\/ola.hallengren.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Ola Hallengren\u2019s Maintenance Solution<\/a>, review&nbsp;the @ExecuteAsUser option for maintenance commands that run inside user databases. This can help reduce the risk of server-level permission hijacking during index and statistics maintenance, provided the database trust settings are also safe.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-recommended-fixes-and-mitigations-nbsp-2\">Recommended fixes and mitigations&nbsp;<\/h3>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Don&#8217;t run all maintenance across all databases as an unrestricted sysadmin context <em>without<\/em> considering database trust boundaries.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Inventory triggers before onboarding a database into central maintenance, especially after&nbsp;restore, migration, vendor deployment, or application upgrade.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Wrap database-local DDL\/DML maintenance in a database-level <code>EXECUTE AS<\/code> context where&nbsp;appropriate&nbsp;and tested.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Use dedicated maintenance accounts and job owners. Avoid personal accounts. Avoid using&nbsp;<code>sa<\/code>&nbsp;as a convenience default for every job.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Disable or remove unexpected triggers. Require change approval for any trigger that can fire during privileged maintenance.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Keep <code>TRUSTWORTHY OFF<\/code> and avoid sysadmin-owned user databases unless explicitly&nbsp;required&nbsp;and documented.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li>Consider running maintenance for untrusted or vendor databases in separate jobs with&nbsp;additional&nbsp;logging and a more restrictive execution context.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-a-practical-monitoring-implementation-nbsp-for-sql-server-what-you-should-do\">A practical monitoring implementation&nbsp;for SQL Server &#8211; what you should do<\/h2>\n\n\n\n<p>A good <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/security\/what-to-monitor-for-sql-server-security\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Server monitoring<\/a> design combines configuration checks, runtime telemetry, and change <a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/the-complete-guide-to-auditing-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">auditing<\/a>. None of these controls&nbsp;is&nbsp;enough on its own, though. You&#8217;ll also benefit from catalog queries, which tell you what exists now, and <a href=\"https:\/\/learn.microsoft.com\/en-us\/sql\/relational-databases\/security\/auditing\/sql-server-audit-database-engine?view=sql-server-ver17\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Audit<\/a>, covering who changed permissions and objects. <\/p>\n\n\n\n<p>There&#8217;s also <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/extended-events-data-collection\/\" target=\"_blank\" rel=\"noreferrer noopener\">Extended Events<\/a>, which&nbsp;helps&nbsp;capture runtime behavior, and <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/database-administration-sql-server\/setting-up-your-sql-server-agent-correctly\/\" target=\"_blank\" rel=\"noreferrer noopener\">SQL Agent<\/a> history explains when automation&nbsp;actually&nbsp;ran.&nbsp;<\/p>\n\n\n\n<div id=\"callout-block_c0c33656316ee743cdf2349e7ac8ef1a\" class=\"callout alignnone\">\n    <div class=\"child-last:mb-0 child-first:mt-0 bg-gray-50 dark:bg-gray-950 p-4xl my-3xl\">\n\n<p><strong>You may also be interested in&#8230;<\/strong><\/p>\n\n\n\n<p><a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/the-complete-guide-to-auditing-sql-server\/\" target=\"_blank\" rel=\"noreferrer noopener\">The complete guide to auditing SQL Server<\/a><\/p>\n\n<\/div>\n<\/div> \n\n\n<h3 class=\"wp-block-heading\" id=\"h-recommended-sql-audit-coverage-nbsp\">Recommended SQL Audit coverage&nbsp;<\/h3>\n\n\n\n<p>The exact audit configuration depends on edition, compliance requirements, storage location, and performance tolerance, but the following action groups are a practical starting point for this topic:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><code>DATABASE_ROLE_MEMBER_CHANGE_GROUP<\/code> &#8211; changes to database role membership, including&nbsp;<code>db_owner<\/code>&nbsp;and SQL Agent fixed database roles in&nbsp;msdb.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><code>SERVER_ROLE_MEMBER_CHANGE_GROUP<\/code> &#8211; changes to sysadmin and other fixed server roles.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><code>DATABASE_PERMISSION_CHANGE_GROUP<\/code> and <code>SERVER_PERMISSION_CHANGE_GROUP<\/code> &#8211; permission grants, denies, and revokes.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><code>DATABASE_PRINCIPAL_CHANGE_GROUP<\/code> and <code>SERVER_PRINCIPAL_CHANGE_GROUP<\/code> &#8211; user and login creation, alteration, and deletion.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><code>DATABASE_OBJECT_CHANGE_GROUP<\/code> and <code>SCHEMA_OBJECT_CHANGE_GROUP<\/code> &#8211; creation, alteration, or deletion of procedures, triggers, schemas, and other database objects.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><code>SERVER_OBJECT_CHANGE_GROUP<\/code> &#8211; changes to server-level objects, including server-level triggers.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><code>SCHEMA_OBJECT_ACCESS_GROUP<\/code> &#8211; optional, higher-volume auditing for execution\/access to specific high-risk procedures or schemas.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-recommended-extended-events-coverage-nbsp\">Recommended Extended Events coverage&nbsp;<\/h3>\n\n\n\n<p>Use Extended Events for targeted telemetry. Avoid capturing every statement on every production server without filters. Start with a small, high-signal session and expand only where needed.&nbsp;<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"lang:tsql decode:true \">\/* Example XE session for high-risk administrative activity. \n   Adjust filters and targets for your environment before production use. *\/ \nCREATE EVENT SESSION [DBA_Security_Exposure_Watch] \nON SERVER \nADD EVENT sqlserver.object_created, \nADD EVENT sqlserver.object_altered, \nADD EVENT sqlserver.object_deleted, \nADD EVENT sqlserver.rpc_completed \n( \n    ACTION \n    ( \n        sqlserver.client_app_name, \n        sqlserver.client_hostname, \n        sqlserver.database_name, \n        sqlserver.server_principal_name, \n        sqlserver.session_id, \n        sqlserver.sql_text \n    ) \n    WHERE \n    ( \n        sqlserver.database_name = N'msdb' \n        OR sqlserver.like_i_sql_unicode_string(sqlserver.sql_text, N'%sp_add_job%') \n        OR sqlserver.like_i_sql_unicode_string(sqlserver.sql_text, N'%sp_update_job%') \n        OR sqlserver.like_i_sql_unicode_string(sqlserver.sql_text, N'%CREATE TRIGGER%') \n        OR sqlserver.like_i_sql_unicode_string(sqlserver.sql_text, N'%ALTER TRIGGER%') \n        OR sqlserver.like_i_sql_unicode_string(sqlserver.sql_text, N'%EXECUTE AS%') \n    ) \n) \nADD TARGET package0.event_file \n( \n    SET filename = N'C:\\Temp\\DBA_Security_Exposure_Watch.xel', \n        max_file_size = 100, \n        max_rollover_files = 10 \n); \nGO \nALTER EVENT SESSION [DBA_Security_Exposure_Watch] \nON SERVER STATE = START; \nGO <\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-operational-checklist-nbsp-for-monitoring-sql-server\">Operational checklist&nbsp;for monitoring SQL Server<\/h2>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>Daily<\/strong>: review changes to sysadmin membership,&nbsp;<code>db_owner<\/code>&nbsp;membership, SQL Agent roles, job owners, job steps, and trigger definitions.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>Weekly<\/strong>: compare permissions, database settings, module execution contexts, and SQL Agent proxies against the approved baseline.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>Before maintenance onboarding<\/strong>: inspect the target database for triggers, <code>TRUSTWORTHY<\/code>, <code>DB_CHAINING<\/code>, owner, CLR assemblies, Service Broker activation, and <code>EXECUTE AS<\/code> modules.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>After&nbsp;restore&nbsp;or vendor deployment<\/strong>: treat the database as untrusted until programmable objects, owners, permissions, triggers, and jobs are reviewed.&nbsp;<\/li>\n<\/ul>\n<\/div>\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li><strong>After incident or suspicious change<\/strong>: preserve SQL Audit files, XE files, SQL Agent logs, default trace data if still available, Windows event logs, and&nbsp;msdb&nbsp;job history before cleanup.&nbsp;<br><\/li>\n<\/ul>\n<\/div>\n\n\n<section id=\"my-first-block-block_62e002f0a1bcbb0b29286740513f8765\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Future-proof database monitoring with Redgate Monitor<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Multi-platform database observability for your entire estate. Optimize performance, ensure security, and mitigate potential risks with fast deep-dive analysis, intelligent alerting, and AI-powered insights.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/products\/redgate-monitor\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Learn more &amp; try for free: Future-proof database monitoring with Redgate Monitor\">Learn more &amp; try for free<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-a-prioritized-sql-server-hardening-plan-nbsp\">A prioritized SQL Server hardening plan&nbsp;<\/h2>\n\n\n\n<p>If you need to turn this guidance into an action plan, start with the changes that reduce the largest number of attack paths without requiring application&nbsp;redesign.&nbsp;<\/p>\n\n\n<div class=\"block-core-list\">\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Inventory and remove permanent&nbsp;<code>db_owner<\/code>&nbsp;from application, support, vendor, and reporting accounts.&nbsp;<br><\/li>\n<\/ol>\n<\/div>\n\n<div class=\"block-core-list\">\n<ol start=\"2\" class=\"wp-block-list\">\n<li>Review&nbsp;<code>msdb<\/code>&nbsp;role membership and SQL&nbsp;Agent&nbsp;job ownership. Move jobs to dedicated owner accounts and remove unnecessary operators.&nbsp;<br><\/li>\n<\/ol>\n<\/div>\n\n<div class=\"block-core-list\">\n<ol start=\"3\" class=\"wp-block-list\">\n<li>Baseline SQL&nbsp;Agent&nbsp;jobs and alert on job command, owner, proxy, subsystem, and schedule changes.&nbsp;<br><\/li>\n<\/ol>\n<\/div>\n\n<div class=\"block-core-list\">\n<ol start=\"4\" class=\"wp-block-list\">\n<li>Inventory DDL and DML&nbsp;triggers&nbsp;across all user databases and server scope. Investigate recently modified triggers first.&nbsp;<br><\/li>\n<\/ol>\n<\/div>\n\n<div class=\"block-core-list\">\n<ol start=\"5\" class=\"wp-block-list\">\n<li>Review all modules using <code>EXECUTE AS OWNER<\/code>, <code>EXECUTE AS&nbsp;dbo<\/code>, or privileged execution context.&nbsp;<br><\/li>\n<\/ol>\n<\/div>\n\n<div class=\"block-core-list\">\n<ol start=\"6\" class=\"wp-block-list\">\n<li>Keep <code>TRUSTWORTHY OFF<\/code> and avoid sysadmin-owned user databases unless there is a documented exception.&nbsp;<br><\/li>\n<\/ol>\n<\/div>\n\n<div class=\"block-core-list\">\n<ol start=\"7\" class=\"wp-block-list\">\n<li>Implement SQL Audit for role, permission, principal, and object changes. Add targeted XE sessions for impersonation, trigger changes, and SQL Agent procedure calls.&nbsp;<br><\/li>\n<\/ol>\n<\/div>\n\n<div class=\"block-core-list\">\n<ol start=\"8\" class=\"wp-block-list\">\n<li>Change maintenance jobs so database-local operations run in a safer database-level context where&nbsp;appropriate&nbsp;and tested.&nbsp;<br><\/li>\n<\/ol>\n<\/div>\n\n<div class=\"block-core-list\">\n<ol start=\"9\" class=\"wp-block-list\">\n<li>Create a restore intake process for untrusted or vendor databases <em>before<\/em> they&#8217;re introduced to production maintenance jobs.&nbsp;<\/li>\n<\/ol>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion-nbsp\">Conclusion&nbsp;<\/h2>\n\n\n\n<p><strong>SQL Server security is not only about disabling obvious risky features. Instead, it&#8217;s more about understanding&nbsp;where&nbsp;trust crosses boundaries.<\/strong> <\/p>\n\n\n\n<p>A&nbsp;<code>db_owner<\/code>&nbsp;user, for example, is not merely a user who can write data. <code>msdb<\/code>&nbsp;is not merely a history database. SQL Agent is not merely a scheduler. A trigger is not merely application logic. When these features interact, routine administration can become an attack path.&nbsp;<\/p>\n\n\n\n<p>DBAs are in the best position to reduce this risk because the exposure is usually visible in the metadata: role membership, job ownership, proxy usage, trigger definitions, module execution context, database ownership, and trust settings. As such, finding the data is easy. Treating the data as a security boundary and continuously monitoring it, however, is more difficult.<\/p>\n\n\n\n<p>Moving from <em>secure by default<\/em> to just <em>secure<\/em> means verifying every privilege that can change code, every job that runs with elevated rights, and every database that central automation touches. Once those trust relationships are visible, they can be reduced,&nbsp;monitored, and controlled.<\/p>\n\n\n\n<section id=\"my-first-block-block_d29573fa65a7feaf5e514f5746202659\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Simple Talk is brought to you by Redgate Software<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Take control of your databases with the trusted Database DevOps solutions provider. Automate with confidence, scale securely, and unlock growth through AI.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/solutions\/overview\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Discover how Redgate can help you: Simple Talk is brought to you by Redgate Software\">Discover how Redgate can help you<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: How to stop SQL Server maintenance becoming an attack path<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. Is SQL Server secure by default?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal\">Not entirely. Default installations and default permissions still leave several attack surfaces open, including broad public role grants, legacy compatibility features, and administrative conveniences. &#8220;Secure by default&#8221; doesn&#8217;t mean a DBA can skip reviewing role membership, permissions, and trust settings.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. What is trigger hijacking in SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal\">Trigger hijacking happens when a DML or DDL trigger created by a lower-privileged user fires during an operation run by a highly privileged process, such as a sysadmin-owned maintenance job. Because the trigger&#8217;s effective context is tied to the caller that fired it, the trigger can inherit and misuse that caller&#8217;s elevated permissions.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. Why is db_owner a security risk for application accounts?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal\">A db_owner member can create and alter objects, define triggers, change permissions, and build modules with elevated execution context, effectively controlling the database&#8217;s security boundary. This is far more access than most runtime application or support accounts need, and it becomes especially dangerous when combined with privileged maintenance jobs or impersonation.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">4. Why does msdb matter for SQL Server security?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal\">msdb stores SQL Server Agent jobs, schedules, credentials, proxies, and job history, and job steps can run under the SQL Agent service account, the job owner, or a proxy. A user with excessive permissions in msdb can potentially influence how privileged automation executes on the instance, making it a server-level security boundary rather than just a metadata store.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">5. Can EXECUTE AS OWNER or EXECUTE AS dbo bypass an explicit DENY?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal\">Yes, in some cases. If a principal can run code under a different execution context \u2014 such as becoming dbo through an impersonated module \u2014 actions that would normally be denied to that user can still execute under the impersonated identity. This is one reason db_owner membership and broad IMPERSONATE grants are treated as high-risk.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">6. How can a DBA check whether their SQL Server instance is exposed to this kind of risk?<\/h3>\n            <div class=\"faq-answer\">\n                <p class=\"font-claude-response-body break-words whitespace-normal\">Start by querying system catalog views such as sys.database_role_members, sys.database_permissions, sys.sql_modules, and sys.triggers to baseline role membership, permissions, execution context, and trigger definitions. Pair this with SQL Audit (role, permission, and object change groups) and targeted Extended Events sessions to catch drift and suspicious activity over time.<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>How trigger hijacking, msdb exposure, and over-privileged database users create SQL Server attack paths \u2014 plus how to detect and monitor them.&hellip;<\/p>\n","protected":false},"author":65554,"featured_media":108045,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143523,53,143530,46,143524],"tags":[4168,159408,5765,4150,4151],"coauthors":[6809],"class_list":["post-111232","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-databases","category-featured","category-security","category-data-security-privacy-compliance","category-sql-server","tag-database","tag-fasqlsecurity","tag-security-and-compliance","tag-sql","tag-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111232","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/65554"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=111232"}],"version-history":[{"count":11,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111232\/revisions"}],"predecessor-version":[{"id":111850,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/111232\/revisions\/111850"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/108045"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=111232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=111232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=111232"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=111232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}