Learn more about SQL Server tools



solving sql server problems for millions of dbas and developers since 2006 attend our next webcast













































   Got a SQL tip?
            We want to know!

How to Audit Login Changes on a SQL Server

MSSQLTips author K. Brian Kelley By:   |   Read Comments (2)   |   Related Tips: More > Security
Problem

I've been charged with coming up with a quick solution to audit login changes on my SQL Servers. However, there's no budget so I'm going to have to come up with basic scripts and the like to do the work. Is this tip we cover a solution for you to audit login changes.

Solution

Developing a solution to detect simple changes to logins is fairly easy and straight forward. For instance, if you're just charged with detecting what logins have been created, deleted, or modified since the last audit, a purely T-SQL solution can be implemented quickly and easily. We'll tackle something that provides slightly more information in this tip.

First, the basic tables we'll need to do the auditing. We'll need a table to store the current scan information as well as a table to store our scan history. We'll actually compare the two tables in order to determine what has changed between audits.  We'll also need a table to track when we did our scans.

Here are the three tables:

CREATE TABLE dbo.AuditScan (
 AuditScanID INT IDENTITY(1,1) NOT NULL,
 ScanStartDate datetime NOT NULL,
 ScanEndDate datetime NULL,
 CONSTRAINT PK_AuditScan PRIMARY KEY CLUSTERED (AuditScanID)
);
GO 

CREATE TABLE dbo.CurrentLoginScan ( [sid] VARBINARY(85) NOT NULL, [name] sysname NOT NULL, [type] CHAR(1) NOT NULL, create_date DATETIME, modify_date DATETIME, AuditScanID INT NOT NULL, CONSTRAINT PK_CurrentLoginScan PRIMARY KEY CLUSTERED ([sid]), CONSTRAINT FK_CurrentLoginScan_AuditScan FOREIGN KEY (AuditScanID) REFERENCES dbo.AuditScan (AuditScanID) ); GO
CREATE TABLE dbo.LoginScanRecord ( [ScanLogID] INT NOT NULL IDENTITY(1,1), [AuditScanID] INT NOT NULL, [sid] VARBINARY(85) NOT NULL, [name] sysname NOT NULL, [type] CHAR(1) NOT NULL, create_date DATETIME NOT NULL, modify_date DATETIME NOT NULL, CONSTRAINT PK_LoginSCanRecord PRIMARY KEY CLUSTERED (ScanLogID), CONSTRAINT FK_LoginScanRecord_AuditScan FOREIGN KEY (AuditScanID) REFERENCES dbo.AuditScan (AuditScanID) ); GO

Now we'll need a couple of stored procedures to mark the start and stop of each audit.

CREATE PROC dbo.StartAuditScan 
  @AuditScanID INT OUTPUT
AS
BEGIN
  SET NOCOUNT ON;

DECLARE @AuditScanIDTable TABLE(AuditScanID INT);
INSERT INTO dbo.AuditScan (ScanStartDate) OUTPUT INSERTED.AuditScanID INTO @AuditScanIDTable VALUES (GETDATE());
SET @AuditScanID = (SELECT AuditScanID FROM @AuditScanIDTable); END; GO
CREATE PROC dbo.EndAuditScan @AuditScanID INT AS BEGIN SET NOCOUNT ON;
UPDATE dbo.AuditScan SET ScanEndDate = GETDATE() WHERE AuditScanID = @AuditScanID; END; GO

And finally, we'll need the stored procedure that does the audit itself. It's going to do the following:

  1. Start the audit.
  2. Retrieve the pertinent information from the sys.server_principals view into the CurrentLoginScan table.
  3. Compare to see what new logins have been created since the last audit.
  4. Compare to see what logins have been deleted since the last audit.
  5. Compare to see what logins show a change since the last audit.
  6. End the audit.
  7. Report the findings.

Here's the stored procedure that accomplishes the above.

CREATE PROC dbo.ExecuteLoginScan
AS
BEGIN
  SET NOCOUNT ON;

DECLARE @CurrentScanID INT; DECLARE @PreviousScanID INT;
SET @PreviousScanID = (SELECT MAX(AuditScanID) FROM dbo.AuditScan); EXEC dbo.StartAuditScan @CurrentScanID OUTPUT;
EXEC dbo.EndAuditScan @CurrentScanID;
INSERT INTO dbo.CurrentLoginScan ([sid], [name], [type], create_date, modify_date, AuditScanID) SELECT [sid], [name], [type], create_date, modify_date, @CurrentScanID FROM master.sys.server_principals;
CREATE TABLE #LoginScanReport ( Change VARCHAR(20) NOT NULL, [sid] VARBINARY(85) NOT NULL, [Current_Name] sysname NULL, [Old_Name] sysname NULL, [Current_Type] CHAR(1) NULL, [Old_Type] CHAR(1) NULL, [Current_Create_Date] DATETIME NULL, [Old_Create_Date] DATETIME NULL, [Current_Modify_Date] DATETIME NULL, [Old_Modify_Date] DATETIME NULL );
-- Find new logins INSERT INTO #LoginScanReport (Change, [sid], Current_Name, Current_Type, Current_Create_Date, Current_Modify_Date) SELECT 'Added', Curr.[sid], Curr.[name], Curr.[type], Curr.create_date, Curr.modify_date FROM dbo.CurrentLoginScan Curr LEFT JOIN (SELECT [sid], [name], [type], create_date, modify_date FROM dbo.LoginScanRecord WHERE AuditScanID = @PreviousScanID) Old ON Curr.[sid] = Old.[sid] WHERE Old.[sid] IS NULL;
-- Find deleted logins INSERT INTO #LoginScanReport (Change, [sid], Old_Name, Old_Type, Old_Create_Date, Old_Modify_Date) SELECT 'Deleted', Old.[sid], Old.[name], Old.[type], Old.create_date, Old.modify_date FROM dbo.CurrentLoginScan Curr RIGHT JOIN (SELECT [sid], [name], [type], create_date, modify_date FROM dbo.LoginScanRecord WHERE AuditScanID = @PreviousScanID) Old ON Curr.[sid] = Old.[sid] WHERE Curr.[sid] IS NULL;
-- Find Changed Logins INSERT INTO #LoginScanReport (Change, [sid], Current_Name, Current_Type, Current_Create_Date, Current_Modify_Date, Old_Name, Old_Type, Old_Create_Date, Old_Modify_Date) SELECT 'Modified', Curr.[sid], Curr.[name], Curr.[type], Curr.create_date, Curr.modify_date, Old.[name], Old.[type], Old.create_date, Old.modify_date FROM dbo.CurrentLoginScan Curr JOIN (SELECT [sid], [name], [type], create_date, modify_date FROM dbo.LoginScanRecord WHERE AuditScanID = @PreviousScanID) Old ON Curr.[sid] = Old.[sid] WHERE (Curr.[name] <> Old.[name]) OR (Curr.[type] <> Old.[type]) OR (Curr.create_date <> Old.create_date) OR (Curr.modify_date <> Old.modify_date);
INSERT INTO dbo.LoginScanRecord (AuditScanID, [sid], [name], [type], create_date, modify_date) SELECT AuditScanID, [sid], [name], [type], create_date, modify_date FROM dbo.CurrentLoginScan;
DELETE FROM dbo.CurrentLoginScan;
SELECT Change, [sid], Current_Name, Old_Name, Current_Type, Old_Type, Current_Create_Date, Old_Create_Date, Current_Modify_Date, Old_Modify_Date FROM #LoginScanReport; END; GO
Next Steps


Last Update: 7/30/2012


About the author
MSSQLTips author K. Brian Kelley
K. Brian Kelley is a SQL Server author and columnist focusing primarily on SQL Server security.

View all my tips
Related Resources


print tip Print  
Become a paid author





join MSSQLTips for free SQL Server tips     



Learn more about SQL Server tools
Comments and Feedback:
Friday, August 03, 2012 - 3:46:54 AM - Mike Read The Tip

Good  article..


Monday, December 23, 2013 - 8:24:26 AM - Konstantin Read The Tip

It doesn't track properly track only new changes related to system roles membership, but it doesn't track for example if I map my login to a database as db_owner or writer



Post a Comment or Question

Keep it clean and stay on the subject or we may delete your comment.
Your email address is not published. Required fields are marked with an asterisk (*)

*Name   *Email Notify for updates



Comments
Get free SQL tips:

*Enter Code refresh code


 
Sponsor Information







Copyright (c) 2006-2014 Edgewood Solutions, LLC All rights reserved
privacy | disclaimer | copyright | advertise | about
authors | contribute | feedback | giveaways | free t-shirt | user groups | community | events | first timer?
Some names and products listed are the registered trademarks of their respective owners.