I previously wrote about how the underlying technology for Fabric mirroring changed with SQL Server 2025. The latest version of mirroring that uses the SQL Server Change Feed is reading from the database transaction logs and pushing the data to a landing zone in OneLake. The data is then merged into the Delta tables for the Fabric mirrored database. In this blog post, we will look at how to monitor this process, both in SQL Server and in Fabric.

Monitoring in the Fabric Portal

The item page for the mirrored database in the Fabric portal shows replication status for the database overall as well as for each table. The per-table status includes:
  • rows replicated: the cumulative count of replicated rows, including all inserts, updates, and deletes applied to the target table
  • last completed: the last time the mirrored table was refreshed from the source
  • delay (seconds): the time between when a change was committed at the source and when it was successfully applied to the destination
Screenshot of the Monitor replication page in the Fabric portal showing a database status of Running and a table-level grid with columns for Name, Status, Rows replicated, Last completed, and Delay (seconds). Five tables are visible, all with a Running status, with row counts ranging from 1 to 762 and delays between 23 and 38 seconds.
It is possible for the overall database status to show “Running” while a specific table has an issue. The last completed date will typically reflect the last time data in the table changed — if nothing has changed, nothing is sent and the date does not update. The delay is tracked on the database side and sent to Fabric for display on the monitoring page.

Monitoring in the Database

When you configure Fabric mirroring, it enables the change feed on the database and the tables you have selected to mirror. Once everything is configured, there are some system views and stored procedures you can access to see what’s going on.

sys.dm_change_feed_log_scan_sessions

This DMV is your primary window into change feed health and activity. It returns one row per log scan session, plus an aggregate row where session_id = 0 that summarizes all sessions since the instance last started. The aggregate row is useful for understanding overall health since the instance last started; the individual session rows show you what’s happening in discrete scans. You want to see sessions completing without errors, with tran_count incrementing and currently_processed_lsn or currently_processed_commit_lsn advancing over time. If sessions are stalling at the same batch_processing_phase repeatedly, or error_count is climbing, something needs attention. The schema_change_count column tracks DDL-related log records processed in a session. Note that this is not a 1:1 mapping with the number of DDL operations — some operations, like adding, altering, or dropping a column, generate two log records per operation, so the count may be higher than you expect.

sys.dm_change_feed_errors

When error_count in the sessions DMV is non-zero, this is where you find out what went wrong. Some errors are transient and will resolve on their own; others won’t. Repeated errors of the same type are a signal to investigate rather than wait it out.

sys.sp_help_change_feed

This stored procedure gives you a configuration-level view of which tables are enrolled in mirroring and their current state. It’s useful after initial setup to confirm everything was picked up correctly, and when a table stops replicating and you want to verify it’s still enrolled.

sys.databases

Unlike CDC, the change feed doesn’t write to change tables in the source database, so it won’t directly cause the transaction log to grow. However, it does hold log truncation until changes are successfully replicated to Fabric. You can use is_data_lake_replication_enabled = 1 to filter sys.databases to only the databases where Fabric mirroring is enabled. A value of REPLICATION for log_reuse_wait_desc means mirroring is currently holding log truncation — not necessarily something that needs immediate action, but if log usage is also growing, that’s a signal that mirroring may not be keeping pace and warrants investigation. If the log hits its size limit, writes to the database will fail.

Extended Events

Microsoft also provides an extended events session you can use for deeper troubleshooting. The session captures change feed activity including errors, snapshots, performance, and scheduler events. Because it can be verbose, Microsoft recommends only running it when you’re actively troubleshooting a problem rather than leaving it on all the time. You can find the session definition in the Troubleshoot Fabric Mirrored Databases from SQL Server documentation.

Files in OneLake

You can use Azure Storage Explorer to browse the files for your Fabric mirrored database. The Monitoring folder contains two files:
  • tables.json shows each mirrored table along with its status, any error message, and metrics.
  • replicator.json shows the overall database replication status along with any error message.
The Manifest_1.json file in the LandingZone folder is a newline-delimited JSON log that records everything the change feed has published to OneLake — initial snapshots, incremental change batches, DDL events, and table creation notifications. It’s not something you’d monitor routinely, but it’s useful for troubleshooting — for example, to confirm that a snapshot completed and how many rows were captured, to see whether incremental batches are being written, or to trace when a DDL change occurred and verify that a re-snapshot was triggered as a result. Within each table’s subfolder you’ll find a TableSchema.json file with the table’s schema definition, a FullCopyData folder where the initial snapshot parquet files are written, and a ChangeData folder where incremental CSV change files land after the snapshot completes. If you’re troubleshooting a data discrepancy or want to confirm that specific changes have made it to OneLake, you can browse these folders directly to check whether the expected files are present.

Knowing Where to Look

For day-to-day health checks, the portal monitoring page and sys.dm_change_feed_log_scan_sessions will cover most of what you need. If you see errors or stalled sessions, sys.dm_change_feed_errors and sys.sp_help_change_feed are the next stop. The OneLake files and extended events are there when you need to dig deeper into a specific problem. Mirroring continues to get new features and changes, so keep an eye on the Microsoft Fabric Mirrored Databases from SQL Server documentation for updates. The post Monitoring Fabric Mirroring for SQL 2025 first appeared on Data Savvy.

Mirroring of SQL Server databases in Microsoft Fabric was first released in public preview in March 2024. Mirrored databases promise near-real-time replication without the need to manage and orchestrate pipelines, copy jobs, or notebooks. John Sterrett blogged about them last year here. But since that initial release, the mechanism under the hood has evolved significantly. Let's talk about Fabric Mirroring: Change Feed vs CDC Explained.

How Fabric Mirroring Works in SQL Server 2016–2022 (CDC)

When mirroring was first released for Azure SQL Database, it used Change Data Capture (CDC). That is still what is used to mirror SQL Server 2016 – 2022.

CDC works by asynchronously scanning the transaction log to find changes related to tracked tables, then writing those changes to dedicated change tables — one per tracked source table, in the format cdc.schema_tablename_CT. A SQL Server Agent job (sys.sp_cdc_scan) is responsible for that log scan and write. Fabric’s replication layer then polls those change tables and pulls changes into OneLake, where they’re converted to Delta Parquet format. The result is a two-hop process: log → change table → OneLake. That intermediary write step is where the overhead lives. If you have a busy SQL Server, this overhead may not be acceptable.

Setting up CDC for SQL Server 2016–2022 requires the fabric_login principal to be a member of the sysadmin server role, at least temporarily, and any future CDC maintenance also requires sysadmin membership. For security-conscious organizations, that’s a significant ask. You can drop the login from sysadmin after CDC is configured, but having to elevate it in the first place causes friction.

 
In SQL Server 2016–2022, if a table’s schema changes after CDC is enabled, the mirrored table schema no longer matches the source, and mirroring fails. Getting replication back on track requires manually disabling and re-enabling CDC on the affected tables.

 
SQL Server 2025 Fabric Mirroring: The Change Feed Explained

SQL Server 2025 has a much better solution: the change feed. Rather than routing changes through change tables, the change feed scans the transaction log at a high frequency and publishes committed changes directly to a landing zone in OneLake. Fabric’s replicator engine then merges those files into the target Delta tables. The intermediary write step is gone.

Because the change feed doesn’t write data back into the source database, it carries lower overhead than CDC. There are no change tables to maintain, no SQL Server Agent jobs to keep healthy, and no cleanup jobs running in the background purging old change records. For busy OLTP systems where CDC’s performance overhead was a concern, this is a meaningful improvement.

DDL changes are handled better, too. Rather than failing when a schema change is detected, the change feed triggers a full re-snapshot of the affected table and reseeds the data automatically. That re-snapshot has a cost if the table is large, but it’s self-healing. You won’t come in Monday morning to find mirroring has been broken since Friday’s deployment.

The permissions model is also cleaner. Rather than requiring sysadmin elevation to configure CDC, SQL Server 2025 mirroring uses a system-assigned managed identity to handle outbound authentication to Fabric. You still create a dedicated login with minimal permissions on the source database, but sysadmin is never required.

SQL Server 2025 Fabric Mirroring Change Feed vs CDC architecture diagram

How to Plan Your Fabric Mirroring Migration: CDC vs Change Feed

CDC-based mirroring is still what SQL Server 2016–2022 uses, and it works. Just go in with eyes open about the performance overhead, the sysadmin requirement, and the DDL limitations.

If you’re already on SQL Server 2025 or planning to upgrade, the change feed makes mirroring a more attractive option than it was under CDC — particularly for busy OLTP systems where the overhead of change table writes was a concern.

Before you enable mirroring on SQL Server 2025, there are a few constraints worth knowing. The source database must be set to the full recovery model (simple recovery is not supported). The change feed is also mutually exclusive with CDC: if CDC is already enabled on a database, you cannot enable Fabric mirroring on that same database. If you’re running CDC today for other consumers, you have a decision to make. You’ll need to decide whether to remove CDC and consolidate on the change feed or keep CDC and find another path for getting that data into Fabric.

Note: As of March 11, 2026, SQL Server 2025 mirroring is supported for on-premises instances only. It is not supported for SQL Server 2025 running in an Azure Virtual Machine or on Linux. It also requires the instance to be connected to Azure Arc with the Azure Extension for SQL Server installed.

Mirroring is still evolving, so it’s worth keeping an eye on the Fabric Mirroring roadmap. And of course, stay tuned for announcements from FABCON next week!

The post How Fabric Mirroring Transformed with SQL Server 2025 first appeared on Data Savvy.

SQL Server Dynamic Data Masking (DDM) is one of those SQL Server features that is commonly misused as a primary security feature used in production. Since it landed in SQL Server 2016, I’ve seen teams throw it at compliance requirements and call it a day, only to find out later that their “masked” data was completely readable by anyone willing to spend 20 minutes in SSMS.

Let me show you some data masking limitations in SQL Server when used for security, and what you should be doing instead (or alongside it) if you’re serious about Zero Trust data security.

TL;DR SQL Server Dynamic Data Masking is a presentation layer feature, not a security feature.


 

SQL Server Dynamic Data Masking Security?

Dynamic Data Masking intercepts query results at the engine level and replaces sensitive column values with masked versions for users who lack the UNMASK permission. The actual data in the database is unchanged — DDM is purely a presentation-layer filter. Again, DDM is a presentation-layer filer.


Security Pitfall 1: Inferring Masked Values with WHERE Clause Attacks

This is the big one. SQL Server Dynamic Data Masking masks the output of a query. It does nothing to prevent a user from using the masked column in a WHERE clause.

Here’s what that looks like in practice:

-- The user can't see the salary column directly (it's masked)
SELECT EmployeeID, Salary FROM HR.Employees;
-- Returns: 1, xxxx

-- But they CAN do this:
SELECT EmployeeID FROM HR.Employees WHERE Salary BETWEEN 95000 AND 105000;
-- Returns rows — meaning they just learned who earns in that range

By running enough range queries, a patient attacker can binary-search their way to the exact value of any masked numeric column. For salary data, SSNs, account numbers, or any structured numeric field, this completely defeats the purpose of masking the data.

Microsoft’s own documentation acknowledges this. It’s not a bug — it’s a documented architectural constraint. DDM was never designed to stop a determined insider.

Here is another example, from start to finish, showing why you should encrypt sensitive data. Let’s look at using predicates to extract Social Security numbers (SSNs). The following demo uses fictitious generated data for people.

SQL Server Dynamic Data Masking Security – SSN Demo


CREATE DATABASE [demo]
GO

USE [demo]
go

CREATE TABLE dbo.Person (
    PersonID  int   NOT NULL
      CONSTRAINT PK_dboPerson 
           PRIMARY KEY CLUSTERED  identity
    ,FirstName  varchar(50)  NULL
    ,LastName  varchar(50) NULL
    ,SSN  varchar(11)  NULL
)
GO

INSERT INTO dbo.Person (FirstName, LastName, SSN)
VALUES
    ('Patricia', 'Smith', '760-36-4013'),
    ('Linda', 'Jones', '755-14-8936'),
    ('John', 'Lee', '433-05-0489'),
    ('John', 'Miller', '239-65-9864'),
    ('James', 'Martin', '204-92-8929'),
    ('Jessica', 'Davis', '460-76-4558'),
    ('James', 'Garcia', '715-55-5575'),
    ('William', 'Jones', '221-98-5515'),
    ('Patricia', 'Williams', '390-13-5882'),
    ('Susan', 'Perez', '271-06-7528'),
    ('Karen', 'Brown', '388-11-9045'),
    ('Barbara', 'Perez', '883-47-9460'),
    ('Michael', 'Williams', '047-85-3734'),
    ('Barbara', 'Williams', '876-30-1655'),
    ('Richard', 'Martinez', '465-82-5978'),
    ('Jennifer', 'Wilson', '364-27-4375'),
    ('John', 'Perez', '651-22-8752'),
    ('Linda', 'Garcia', '474-49-4423'),
    ('Karen', 'Davis', '702-42-0917'),
    ('Linda', 'Johnson', '825-41-6573');
    GO

-- Nothing is masked, can see SSN's 
select * from dbo.Person
If we are users with UNMASK access, here is what we would see.

Fictitious SSN data that will be exposed through SQL Server Dynamic Data Masking

Fictitious SSN data that will be exposed through SQL Server Dynamic Data Masking

Let’s now enable SQL Server Dynamic Data Masking to mask Social Security numbers (SSNs).  We will create a MaskedUser for testing purposes to see the results from a masked user’s perspective.

/* Mask SSN's */
ALTER TABLE dbo.Person
ALTER COLUMN SSN
ADD MASKED WITH (FUNCTION = 'default()')
GO

/* Create testing user */
CREATE USER [MaskedUser] WITHOUT LOGIN WITH DEFAULT_SCHEMA=[dbo]
GO
GRANT SELECT ON dbo.Person TO [MaskedUser];
GO

EXECUTE AS USER = 'MaskedUser';
GO
SELECT * FROM dbo.Person
GO

SQL Server Dynamic Data Masking at work. SSN's are masked for this user.

SQL Server Dynamic Data Masking at work. SSNs are masked for this user.

Okay, let’s now take a look at leveraging predicates to learn the data, even if it’s masked. Remember, DDM masks the output, not the input, in this case, the predicates. In the next example, we’re going to use predicates to check whether the SSNs match the xxx-xx-xxxx or xxxxxxxxx format.

EXECUTE AS USER = 'MaskedUser';
GO
SELECT COUNT(*) FROM dbo.Person
SELECT COUNT(*) FROM dbo.Person WHERE CHARINDEX('-', SSN) = 4
SELECT COUNT(*) FROM dbo.Person WHERE CHARINDEX('-', SSN, 5) = 7
REVERT;

SQL Server Dynamic Data Masking doesn't mask predicates just the output results

SQL Server Dynamic Data Masking doesn’t mask predicates, just the output results

Finally, here is where things get interesting. We can build table variables for each possible numeric value for a digit, filter the SSN masked column on each digit, and select the ones that match. This is how you can see SSNs even when you are the user who should see them masked.


SET NOCOUNT ON
GO
EXECUTE AS USER = 'MaskedUser';
GO
DECLARE @SSN1 TABLE (
    SSN1 char(3) PRIMARY KEY CLUSTERED
);
DECLARE 
    @SSN1Loop1        int = 0
    ,@SSN1Loop2        int = 0
    ,@SSN1Loop3        int = 0
WHILE @SSN1Loop1 < 10
BEGIN
    SELECT @SSN1Loop2 = 0
    WHILE @SSN1Loop2 < 10
    BEGIN
        SELECT @SSN1Loop3 = 0
        WHILE @SSN1Loop3 < 10
        BEGIN
            INSERT INTO @SSN1 (SSN1)
            SELECT CONVERT(char(1),@SSN1Loop1) 
                    + CONVERT(char(1),@SSN1Loop2) 
                    + CONVERT(char(1),@SSN1Loop3)
            SELECT @SSN1Loop3 += 1
        END
        SELECT @SSN1Loop2 += 1
    END
    SELECT @SSN1Loop1 += 1
END
--SELECT * FROM @SSN1
DECLARE @SSN2 TABLE (
    SSN2 char(2) PRIMARY KEY CLUSTERED
)
DECLARE 
    @SSN2Loop1        int = 0
    ,@SSN2Loop2        int = 0
WHILE @SSN2Loop1 < 10
BEGIN
    SELECT @SSN2Loop2 = 0
    WHILE @SSN2Loop2 < 10
    BEGIN
        INSERT INTO @SSN2 (SSN2)
        SELECT CONVERT(char(1),@SSN2Loop1) 
                  + CONVERT(char(1),@SSN2Loop2)
        SELECT @SSN2Loop2 += 1
    END
    SELECT @SSN2Loop1 += 1
END
--SELECT * FROM @SSN2
DECLARE @SSN3 TABLE (
    SSN3 char(4) PRIMARY KEY CLUSTERED
)
DECLARE 
    @SSN3Loop1        int = 0
    ,@SSN3Loop2        int = 0
    ,@SSN3Loop3        int = 0
    ,@SSN3Loop4        int = 0
WHILE @SSN3Loop1 < 10
BEGIN
    SELECT @SSN3Loop2 = 0
    WHILE @SSN3Loop2 < 10
    BEGIN
        SELECT @SSN3Loop3 = 0
        WHILE @SSN3Loop3 < 10
        BEGIN
            SELECT @SSN3Loop4 = 0
            WHILE @SSN3Loop4 < 10
            BEGIN
                INSERT INTO @SSN3 (SSN3)
                SELECT CONVERT(char(1),@SSN3Loop1)  
                     + CONVERT(char(1),@SSN3Loop2) 
                     + CONVERT(char(1),@SSN3Loop3) 
                     + CONVERT(char(1),@SSN3Loop4)
                SELECT @SSN3Loop4 += 1
            END
            SELECT @SSN3Loop3 += 1
        END
        SELECT @SSN3Loop2 += 1
    END
    SELECT @SSN3Loop1 += 1
END
SELECT
    P.PersonID
    ,P.FirstName
    ,P.LastName
    ,P.SSN
    ,T1.SSN1
    ,T2.SSN2
    ,T3.SSN3
FROM dbo.Person P
    LEFT JOIN @SSN1 T1
        ON SUBSTRING(P.SSN,1,3) = T1.SSN1
    LEFT JOIN @SSN2 T2
        ON SUBSTRING(P.SSN,5,2) = T2.SSN2
    LEFT JOIN @SSN3 T3
        ON SUBSTRING(P.SSN,8,4) = T3.SSN3
ORDER BY P.PersonID;
GO
REVERT
GO

SQL Server Dynamic Data Masking predicate bypass attack showing SSN exposure

SQL Server Dynamic Data Masking predicate bypass attack showing SSN exposure

What to do instead: If users shouldn’t be able to filter on a column, they shouldn’t have SELECT on that table at all. Row-Level Security (RLS) combined with column-level encryption or Always Encrypted is the right tool here, not Dynamic Data Masking.


Security Pitfall 2: Anyone with ALTER TABLE Can Remove the Mask Entirely

Okay, let’s assume pitfall #1 didn’t stop you in your tracks from wanting to use SQL Server Dynamic Data Masking for security purposes outside the presentation layer (it should have).

Here’s one that gets overlooked in permission audits. Any user with ALTER TABLE or ALTER ANY MASK permission can simply drop it like it’s hot, the masking function from a column:

-- Requires ALTER TABLE permission (not UNMASK):
ALTER TABLE HR.Employees
ALTER COLUMN Salary DROP MASKED;

After that, everyone can read the data — no UNMASK needed. The mask is gone permanently until someone re-adds it. So users, could drop it, see what they need to see, and add it back.

If your people have db_ddladmin or elevated schema permissions (which many do in smaller shops), they can silently unmask entire tables. And because DDM changes don’t appear in the default audit logs unless you’ve explicitly set up an audit specification for it, this can go undetected.
What to do instead: Audit who has ALTER TABLE permissions. Create a SQL Server Audit specification that captures DATABASE_OBJECT_CHANGE_GROUP events to catch mask additions and removals. Treat DDM schema changes as security events, not as schema changes.

 


Security Pitfall 3: Metadata Is Fully Visible to Every User

Even a user with only VIEW ANY DEFINITION permissions can see exactly which columns are masked. This where Young Joc would say, “I know you see…”

SELECT 
    OBJECT_NAME(object_id) AS TableName,
    name AS ColumnName,
    masking_function
FROM sys.masked_columns;
This tells an attacker exactly which data is considered sensitive, which masking function is applied (which reveals the data type and format), and where to focus their inference attacks.
For something like a credit card number masked with partial(0,’XXXX-XXXX-XXXX-‘,4), the masking function itself reveals the exact format the attacker is trying to reconstruct.
What to do instead: There’s no way to hide this metadata — it’s in system views. Accept it as a constraint of Dynamic Data Masking and compensate by limiting who has any database access. Don’t rely on obscurity. All sensitive data should be encrypted and audited.

 


Security Pitfall 4: DDM Doesn’t Survive Data Movement

This is the one that kills compliance reviews. SQL Server Dynamic Data Masking is defined at the column level in the source table. If Cardi B was a DBA she would say, “Make the Data Move.”

The moment that data moves anywhere else, the mask disappears:

  • SELECT INTO / INSERT SELECT: The mask function is not copied to the destination table.
  • OPENROWSET / linked server queries: DDM is not enforced on remote result sets.
  • BCP / bulk export: Exports the unmasked data if run by a privileged account.
  • SSRS, Power BI, or any reporting tool: If the service account has UNMASK (or DDM isn’t configured in the cloud copy), the reports show real data.
  • Database backups: Backups contain the actual unmasked data. If the backup is restored by someone with elevated permissions, DDM means nothing.

Teams that use DDM to “protect” data flowing into a staging database or a data warehouse are providing exactly zero protection for that downstream data.

Here is an example of seeing that SELECT INTO doesn’t mask data in the destination table.


EXECUTE AS USER = 'MaskedUser';
GO
SELECT * 
FROM dbo.Person2
GO
REVERT
GO

Fictitious SSN data that will be exposed through SQL Server Dynamic Data Masking

Fictitious SSN data that will be exposed through SQL Server Dynamic Data Masking

What to do instead: Treat data movement as a security event. Apply encryption for data that needs to stay protected during movement. DDM belongs only in the presentation layer, not as a data-pipeline security control.


Security Pitfall 5: The UNMASK Permission A Security Vulnerability Sledgehammer

When Dynamic Data Masking was released with SQL Server 2016, you could either grant UNMASK  at the database level.  Just like O-town would sing, you can either Mask it All or Nothing at All.
With SQL Server 2022, Microsoft introduced granular UNMASK permissions (you can grant UNMASK on a specific schema, table, or column). On SQL Server 2019 and below, UNMASK is a database-level permission.
That means: if you grant a developer UNMASK so they can debug a production issue, they can now see unmasked data in every masked column across the entire database.
-- Granting this one permission unlocks everything:
GRANT UNMASK TO [DevUser];

This isn’t a theoretical risk. Dev teams routinely need production access for troubleshooting. If your escalation playbook involves handing out UNMASK, you’ve just handed them a master key.

What to do instead: Upgrade your permission model. If you’re on SQL Server 2022 or Azure SQL, use column-level UNMASK grants. If you’re on older versions, use static data masking with sanitized data for dev access rather than granting UNMASK on production. Again, I wouldn’t use Dynamic Data Masking for security purposes.


So Should You Use DDM at All?

Yes,but in the right context. DDM is legitimately useful for:

  • Reducing casual exposure in reporting environments where users cannot probe the data.
  • Limiting blast radius if a low-privilege account is compromised (they see masked output, not raw PII)
  • Satisfying compliance documentation requirements as only one layer in a defense-in-depth stack.

The problem is treating Dynamic Data masking as your primary security control. In a Zero Trust model, every layer needs to hold up under adversarial conditions. DDM does not by itself. It’s a trip wire, not a vault.

Your actual Zero Trust data security stack in SQL Server should look like this:

Layer Tool
Authentication Active Directory Domain Services / Entra ID + MFA
Column-level protection Always Encrypted or Column Level Encryption.
Row-level access Row-Level Security (RLS)
Presentation-layer masking Dynamic Data Masking (DDM)
Audit and detection SQL Server Audit + Microsoft Defender for SQL

DDM goes in the “presentation layer” bucket. The moment you understand that distinction, you’ll stop over-relying on it.

FAQ: Dynamic Data Masking in SQL Server

Here are some common questions that come up with SQL Server Dynamic Data Masking:

Does SQL Server Dynamic Data Masking protect against SQL injection?

No, Dynamic Data Masking in SQL Server provides zero protection against SQL injection attacks. SQL injection exploits vulnerabilities in how an application constructs queries, allowing an attacker to execute arbitrary SQL against your database.

Is Dynamic Data Masking HIPAA compliant?

Dynamic Data Masking in SQL Server alone is not sufficient for HIPAA compliance, but it can be one contributing layer in a compliant architecture.

What is the difference between DDM and Always Encrypted?

These two features operate at completely different layers of the stack and solve fundamentally different problems. At a high level, Dynamic Data Masking works at the presentation layer, and Always Encrypted encrypts the data.


The Takeaway

Dynamic Data Masking is a convenience feature with security side effects. Dynamic Data Masking is not a security feature with convenience side effects. If you’re using it as your main line of defense against unauthorized data access, you’re one curious person or one clever WHERE clause away from an data exposure incident.

 


John Sterrett is a Principal at ProcureSQL, a Microsoft Data Platform consultancy specializing in SQL Server performance, security, and Azure migrations. Follow him on YouTube and Instagram for weekly SQL Server content.

Questions or war stories about DDM? Drop them in the comments.

This is the last post in a three-part series exploring the mechanics of Materialized Lake Views (MLVs). The goal is to help you understand how they work and whether they make sense for your environment. What they are, when they help, and when they fall short.