افزایش امنیت SQL Server: مدیریت پیشرفته سیاستهای رمز عبور با Enforce password policy
SQL Server بهطور پیشفرض، مدیریت ورودهای مبتنی بر احراز هویت ویندوز را با رعایت سیاستهای رمز عبور سیستم عامل انجام میدهد. با این حال، برای کاربرانی که از احراز هویت استاندارد SQL Server استفاده میکنند، اعمال سیاستهای پیچیده رمز عبور ممکن است چالشبرانگیز باشد، مگر اینکه از SQL Server 2012 یا نسخههای جدیدتر با گزینه “Enforce password policy” استفاده شود. اما حتی این گزینه نیز تنها بخشی از نیازها را پوشش میدهد و نمیتواند سیاستهای سفارشی یا پیچیدهتر را پیادهسازی کند. ابزار EnforcePass راه حلی رایگان و قدرتمند برای این مشکل ارائه میدهد.
EnforcePass یک ابزار مبتنی بر SQL Server است که امکان پیادهسازی و اعمال سیاستهای جامع رمز عبور را برای تمامی ورودهای استاندارد SQL Server فراهم میکند. این ابزار به شما کمک میکند تا امنیت پایگاه داده خود را با فراتر رفتن از محدودیتهای پیشفرض، به میزان قابل توجهی افزایش دهید.
چرا به EnforcePass نیاز دارید؟
در بسیاری از سناریوها، نیاز به سیاستهای رمز عبور وجود دارد که فراتر از آنچه SQL Server یا حتی سیستمعامل ویندوز ارائه میدهند، باشد. فرض کنید میخواهید:
- تاریخچه رمز عبور را نگهداری کنید تا کاربران نتوانند رمزهای عبور قبلی خود را مجدداً استفاده کنند.
- حداقل طول رمز عبور را برای همه کاربران SQL Server تعیین کنید.
- الزاماتی برای ترکیب رمز عبور (حروف بزرگ، حروف کوچک، اعداد، نمادها) تعیین کنید.
- از استفاده کلمات رایج یا نام کاربری در رمز عبور جلوگیری کنید.
- انقضای رمز عبور را مدیریت کرده و کاربران را مجبور به تغییر رمز عبور به صورت دورهای کنید.
- قفل شدن حساب کاربری را پس از چندین تلاش ناموفق برای ورود، پیادهسازی کنید.
EnforcePass تمام این قابلیتها و بیشتر را به شما ارائه میدهد و مدیریت امنیت رمز عبور در SQL Server را به سطحی جدید ارتقاء میبخشد.
نحوه عملکرد EnforcePass
این ابزار بر اساس مجموعهای از جداول، رویهها، توابع و تریگرها در یک پایگاه داده مجزا (به نام enforcepass) کار میکند. این معماری تضمین میکند که سیاستها به طور مؤثر و بدون تداخل با پایگاه دادههای کاربردی شما اعمال شوند.
جداول مورد نیاز
EnforcePass از سه جدول اصلی برای ذخیرهسازی سیاستها، تاریخچه رمز عبور و اطلاعات ورودهای ناموفق استفاده میکند.
این کد اسکریپت ایجاد جداول لازم برای ابزار EnforcePass را نشان میدهد:
CREATE DATABASE enforcepass
GO
USE enforcepass
GO
CREATE TABLE [dbo].[PasswordPolicy](
[LoginName] [sysname] NOT NULL,
[MinLength] [int] NULL,
[MinUpperCase] [int] NULL,
[MinLowerCase] [int] NULL,
[MinNumeric] [int] NULL,
[MinSymbol] [int] NULL,
[NotUserName] [bit] NULL,
[BlackList] [xml] NULL,
[HistoryCount] [int] NULL,
[ExpirationDays] [int] NULL,
[AllowChangePassword] [bit] NULL,
[AllowLogin] [bit] NULL,
[LockoutThreshold] [int] NULL,
[LockoutDurationMinutes] [int] NULL,
[LastSetDate] [datetime] NULL,
[LastChangePasswordDate] [datetime] NULL,
CONSTRAINT [PK_PasswordPolicy] PRIMARY KEY CLUSTERED
(
[LoginName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[PasswordHistory](
[LoginName] [sysname] NOT NULL,
[PasswordHash] [varbinary](128) NOT NULL,
[SetDate] [datetime] NOT NULL,
CONSTRAINT [PK_PasswordHistory] PRIMARY KEY CLUSTERED
(
[LoginName] ASC,
[SetDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[FailedLogins](
[LoginName] [sysname] NOT NULL,
[FailedCount] [int] NOT NULL,
[LastFailedLoginDate] [datetime] NULL,
[LockedOut] [bit] NOT NULL,
[LockOutDate] [datetime] NULL,
CONSTRAINT [PK_FailedLogins] PRIMARY KEY CLUSTERED
(
[LoginName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
مدیریت سیاستها
Stored Procedure PasswordPolicy_Set به شما امکان میدهد تا سیاستهای رمز عبور را برای یک کاربر خاص تنظیم کنید. این روال پارامترهای مختلفی را برای تعیین حداقل طول، الزامات کاراکتر، تاریخچه، بلکلیست و غیره میپذیرد.
مثالی از نحوه تنظیم یک سیاست جدید:
USE [enforcepass]
GO
EXEC [dbo].[PasswordPolicy_Set]
@LoginName = N'TestUser',
@MinLength = 8,
@MinUpperCase = 1,
@MinLowerCase = 1,
@MinNumeric = 1,
@MinSymbol = 1,
@NotUserName = 1,
@BlackList = N'password123456',
@HistoryCount = 5,
@ExpirationDays = 90,
@AllowChangePassword = 1,
@AllowLogin = 1,
@LockoutThreshold = 5,
@LockoutDurationMinutes = 30
GO
روال PasswordPolicy_Get اطلاعات سیاست رمز عبور را برای یک LoginName خاص بازمیگرداند.
اعمال و اعتبارسنجی رمز عبور
قلب EnforcePass در مجموعهای از توابع و رویههایی است که رمز عبور جدید را در برابر سیاستهای تعریف شده اعتبارسنجی میکنند.
اعتبارسنجی عمومی رمز عبور
روال Password_Validate یک رمز عبور را در برابر تمام سیاستهای تعریف شده اعتبارسنجی میکند و در صورت عدم رعایت هر یک از سیاستها، یک پیام خطا بازمیگرداند. این روال برای استفاده در تریگرهای تغییر رمز عبور ایدهآل است.
این بخش از کد روال Password_Validate را نشان میدهد:
CREATE PROCEDURE [dbo].[Password_Validate]
@LoginName sysname,
@Password nvarchar(128)
AS
BEGIN
-- Check policy exists
IF NOT EXISTS (SELECT 1 FROM dbo.PasswordPolicy WHERE LoginName = @LoginName)
BEGIN
-- No policy for this login. Success
RETURN 0
END
-- Get policy
DECLARE @MinLength int, @MinUpperCase int, @MinLowerCase int, @MinNumeric int, @MinSymbol int, @NotUserName bit, @BlackList xml, @HistoryCount int, @ExpirationDays int, @AllowChangePassword bit, @AllowLogin bit, @LastSetDate datetime
SELECT @MinLength = MinLength, @MinUpperCase = MinUpperCase, @MinLowerCase = MinLowerCase, @MinNumeric = MinNumeric, @MinSymbol = MinSymbol, @NotUserName = NotUserName, @BlackList = BlackList, @HistoryCount = HistoryCount, @ExpirationDays = ExpirationDays, @AllowChangePassword = AllowChangePassword, @AllowLogin = AllowLogin, @LastSetDate = LastSetDate
FROM dbo.PasswordPolicy WHERE LoginName = @LoginName
-- Validate complexity
EXEC dbo.Password_ValidateComplexity @LoginName = @LoginName, @Password = @Password, @MinLength = @MinLength, @MinUpperCase = @MinUpperCase, @MinLowerCase = @MinLowerCase, @MinNumeric = @MinNumeric, @MinSymbol = @MinSymbol, @NotUserName = @NotUserName, @BlackList = @BlackList
-- Validate history
EXEC dbo.Password_ValidateHistory @LoginName = @LoginName, @Password = @Password, @HistoryCount = @HistoryCount
-- Check if login is allowed
IF @AllowLogin = 0
BEGIN
RAISERROR ('Login is not allowed to login', 16, 1)
RETURN 1
END
RETURN 0
END
GO
اعتبارسنجی پیچیدگی
Password_ValidateComplexity رویهای است که تمام جنبههای پیچیدگی رمز عبور (طول، ترکیب کاراکتر، نام کاربری، بلکلیست) را بررسی میکند. این روال از توابع اسکالر مجزا برای بررسی وجود حروف بزرگ، کوچک، اعداد و نمادها استفاده میکند.
این کد روال Password_ValidateComplexity را نشان میدهد:
CREATE PROCEDURE [dbo].[Password_ValidateComplexity]
@LoginName sysname,
@Password nvarchar(128),
@MinLength int,
@MinUpperCase int,
@MinLowerCase int,
@MinNumeric int,
@MinSymbol int,
@NotUserName bit,
@BlackList xml
AS
BEGIN
-- Minimum Length Check
IF @MinLength IS NOT NULL AND LEN(@Password) < @MinLength
BEGIN
RAISERROR ('Password does not meet minimum length policy', 16, 1)
RETURN 1
END
-- Minimum Uppercase Check
IF @MinUpperCase IS NOT NULL AND dbo.udf_HasUpperCase(@Password) < @MinUpperCase
BEGIN
RAISERROR ('Password does not meet minimum uppercase policy', 16, 1)
RETURN 1
END
-- Minimum Lowercase Check
IF @MinLowerCase IS NOT NULL AND dbo.udf_HasLowerCase(@Password) < @MinLowerCase
BEGIN
RAISERROR ('Password does not meet minimum lowercase policy', 16, 1)
RETURN 1
END
-- Minimum Numeric Check
IF @MinNumeric IS NOT NULL AND dbo.udf_HasNumeric(@Password) < @MinNumeric
BEGIN
RAISERROR ('Password does not meet minimum numeric policy', 16, 1)
RETURN 1
END
-- Minimum Symbol Check
IF @MinSymbol IS NOT NULL AND dbo.udf_HasSymbol(@Password) < @MinSymbol
BEGIN
RAISERROR ('Password does not meet minimum symbol policy', 16, 1)
RETURN 1
END
-- Not User Name Check
IF @NotUserName = 1 AND @LoginName LIKE '%' + @Password + '%'
BEGIN
RAISERROR ('Password cannot contain your login name', 16, 1)
RETURN 1
END
-- Black List Check
IF @BlackList IS NOT NULL AND EXISTS (
SELECT 1 FROM @BlackList.nodes('/words/word') AS T(C)
WHERE LOWER(@Password) LIKE '%' + LOWER(T.C.value('.', 'nvarchar(50)')) + '%'
)
BEGIN
RAISERROR ('Password contains a blacklisted word', 16, 1)
RETURN 1
END
RETURN 0
END
GO
توابع اعتبارسنجی کاراکتر
چندین تابع اسکالر تعریف شدهاند که تعداد حروف بزرگ، کوچک، اعداد یا نمادها را در یک رشته بازمیگردانند. این توابع در Password_ValidateComplexity برای بررسی الزامات ترکیب کاراکترها استفاده میشوند.
مثال تابع برای بررسی حروف کوچک:
CREATE FUNCTION [dbo].[udf_HasLowerCase]
(
@string nvarchar(128)
)
RETURNS int
AS
BEGIN
DECLARE @len int = LEN(@string)
DECLARE @i int = 1
DECLARE @count int = 0
WHILE @i <= @len
BEGIN
IF SUBSTRING(@string, @i, 1) LIKE '[a-z]'
SET @count = @count + 1
SET @i = @i + 1
END
RETURN @count
END
GO
توابع مشابهی برای حروف بزرگ (udf_HasUpperCase)، اعداد (udf_HasNumeric) و نمادها (udf_HasSymbol) نیز وجود دارند که از منطق مشابهی برای شمارش انواع کاراکترها استفاده میکنند.
اعتبارسنجی تاریخچه رمز عبور
Password_ValidateHistory بررسی میکند که آیا رمز عبور جدید قبلاً توسط کاربر استفاده شده است یا خیر. این روال از جدول PasswordHistory برای مقایسه هش رمز عبور استفاده میکند.
CREATE PROCEDURE [dbo].[Password_ValidateHistory]
@LoginName sysname,
@Password nvarchar(128),
@HistoryCount int
AS
BEGIN
IF @HistoryCount IS NOT NULL AND @HistoryCount > 0
BEGIN
-- Check history for password
IF EXISTS (
SELECT 1 FROM dbo.PasswordHistory
WHERE LoginName = @LoginName AND PasswordHash = HASHBYTES('SHA2_512', @Password)
)
BEGIN
RAISERROR ('Password has been used recently', 16, 1)
RETURN 1
END
END
RETURN 0
END
GO
مدیریت ورودهای ناموفق
روال Login_Failed برای ثبت تلاشهای ناموفق برای ورود و قفل کردن حساب کاربری پس از رسیدن به آستانه تعیین شده استفاده میشود.
CREATE PROCEDURE [dbo].[Login_Failed]
@LoginName sysname
AS
BEGIN
-- Update failed login count
UPDATE dbo.FailedLogins SET FailedCount = FailedCount + 1, LastFailedLoginDate = GETDATE()
WHERE LoginName = @LoginName
IF @@ROWCOUNT = 0
BEGIN
INSERT dbo.FailedLogins (LoginName, FailedCount, LastFailedLoginDate, LockedOut)
VALUES (@LoginName, 1, GETDATE(), 0)
END
-- Check if account needs to be locked
DECLARE @LockoutThreshold int, @LockoutDurationMinutes int
SELECT @LockoutThreshold = LockoutThreshold, @LockoutDurationMinutes = LockoutDurationMinutes
FROM dbo.PasswordPolicy WHERE LoginName = @LoginName
IF @LockoutThreshold IS NOT NULL AND @LockoutDurationMinutes IS NOT NULL
BEGIN
IF (SELECT FailedCount FROM dbo.FailedLogins WHERE LoginName = @LoginName) >= @LockoutThreshold
BEGIN
UPDATE dbo.FailedLogins SET LockedOut = 1, LockOutDate = GETDATE()
WHERE LoginName = @LoginName
END
END
END
GO
بازنشانی وضعیت قفل
روال Login_ResetLockout حساب کاربری قفل شده را بازنشانی میکند.
CREATE PROCEDURE [dbo].[Login_ResetLockout]
@LoginName sysname
AS
BEGIN
UPDATE dbo.FailedLogins SET FailedCount = 0, LockedOut = 0, LockOutDate = NULL
WHERE LoginName = @LoginName
END
GO
تریگرها: اعمال سیاستها در زمان واقعی
تریگرها کلید اعمال خودکار سیاستهای EnforcePass هستند. دو تریگر اصلی وجود دارد:
تریگر Logon (trg_Logon_EnforcePass)
این تریگر در سطح سرور (ALL SERVER) فعال میشود و ورودهای استاندارد SQL Server را رهگیری میکند. این تریگر وظایف زیر را انجام میدهد:
- بررسی میکند که آیا کاربر قفل شده است.
- بررسی میکند که آیا رمز عبور منقضی شده است.
- در صورت نیاز، تلاشهای ناموفق برای ورود را ثبت میکند.
تریگر logon با استفاده از تابع EVENTDATA() اطلاعات مربوط به رویداد ورود را استخراج میکند و سپس سیاستهای قفل حساب و انقضای رمز عبور را بررسی میکند. اگر کاربری قفل شده باشد یا رمز عبور او منقضی شده باشد و قادر به تغییر آن نباشد، ورود او با خطا مواجه خواهد شد.
این کد تریگر Logon را نشان میدهد:
CREATE TRIGGER [trg_Logon_EnforcePass]
ON ALL SERVER WITH EXECUTE AS 'sa'
FOR LOGON
AS
BEGIN
DECLARE @LoginName sysname = EVENTDATA().value('(/EVENT_INSTANCE/LoginName)[1]', 'sysname')
DECLARE @EventType sysname = EVENTDATA().value('(/EVENT_INSTANCE/EventType)[1]', 'sysname')
-- Skip Windows logins or if no policy for this login
IF SUSER_SNAME() LIKE '%\\%' OR NOT EXISTS (SELECT 1 FROM enforcepass.dbo.PasswordPolicy WHERE LoginName = @LoginName)
BEGIN
RETURN
END
-- Check if login is locked out
IF EXISTS (
SELECT 1 FROM enforcepass.dbo.FailedLogins fl
JOIN enforcepass.dbo.PasswordPolicy pp ON fl.LoginName = pp.LoginName
WHERE fl.LoginName = @LoginName AND fl.LockedOut = 1
AND (pp.LockoutDurationMinutes IS NULL OR fl.LockOutDate >= DATEADD(MINUTE, -pp.LockoutDurationMinutes, GETDATE()))
)
BEGIN
RAISERROR ('Login is currently locked out.', 16, 1)
ROLLBACK
RETURN
END
-- Check if password has expired
DECLARE @ExpirationDays int, @LastSetDate datetime, @AllowChangePassword bit
SELECT @ExpirationDays = ExpirationDays, @LastSetDate = LastSetDate, @AllowChangePassword = AllowChangePassword
FROM enforcepass.dbo.PasswordPolicy WHERE LoginName = @LoginName
IF @ExpirationDays IS NOT NULL AND @LastSetDate IS NOT NULL
BEGIN
IF @LastSetDate < DATEADD(DAY, -@ExpirationDays, GETDATE())
BEGIN
IF @AllowChangePassword = 0
BEGIN
RAISERROR ('Password has expired. Please contact your administrator to reset it.', 16, 1)
ROLLBACK
RETURN
END
ELSE
BEGIN
RAISERROR ('Password has expired. Please change it now.', 16, 1)
-- Allow login but force change password
END
END
END
-- Reset failed login count on successful login
EXEC enforcepass.dbo.Login_ResetLockout @LoginName = @LoginName
END
GO
محاسبه انقضای رمز عبور به سادگی با بررسی اینکه تاریخ آخرین تنظیم رمز عبور قبل از تاریخ فعلی منهای تعداد روزهای انقضا است، انجام میشود. این منطق به صورت زیر بیان میشود:
(@LastSetDate < DATEADD(DAY, -@ExpirationDays, GETDATE()))
اگر این شرط برقرار باشد، رمز عبور منقضی شده تلقی میشود.
تریگر Change Password (trg_ChangePassword_EnforcePass)
این تریگر نیز در سطح سرور (ALL SERVER) تعریف شده و هنگامی که یک کاربر رمز عبور خود را تغییر میدهد، فعال میشود. وظیفه اصلی آن اعتبارسنجی رمز عبور جدید با استفاده از روال Password_Validate و ثبت آن در جدول تاریخچه است.
CREATE TRIGGER [trg_ChangePassword_EnforcePass]
ON ALL SERVER WITH EXECUTE AS 'sa'
FOR ALTER_LOGIN
AS
BEGIN
DECLARE @LoginName sysname = EVENTDATA().value('(/EVENT_INSTANCE/LoginName)[1]', 'sysname')
DECLARE @EventType sysname = EVENTDATA().value('(/EVENT_INSTANCE/EventType)[1]', 'sysname')
DECLARE @SQLCommand nvarchar(max) = EVENTDATA().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'nvarchar(max)')
IF @SQLCommand LIKE '%PASSWORD =%'
BEGIN
DECLARE @Password nvarchar(128)
-- Extract password from the command for validation (careful with this in production for logging sensitive data)
-- Example: ALTER LOGIN [TestUser] WITH PASSWORD = 'NewStrongPassword'
SET @Password = SUBSTRING(@SQLCommand, CHARINDEX('PASSWORD = ''', @SQLCommand) + LEN('PASSWORD = '''), CHARINDEX('''', @SQLCommand, CHARINDEX('PASSWORD = ''', @SQLCommand) + LEN('PASSWORD = ''')) - (CHARINDEX('PASSWORD = ''', @SQLCommand) + LEN('PASSWORD = ''')))
-- Validate the new password
EXEC enforcepass.dbo.Password_Validate @LoginName = @LoginName, @Password = @Password
-- Store password hash in history
INSERT enforcepass.dbo.PasswordHistory (LoginName, PasswordHash, SetDate)
VALUES (@LoginName, HASHBYTES('SHA2_512', @Password), GETDATE())
-- Update last set date
UPDATE enforcepass.dbo.PasswordPolicy SET LastSetDate = GETDATE() WHERE LoginName = @LoginName
END
END
GO
این تریگر رمز عبور جدید را از فرمان ALTER LOGIN استخراج میکند (با هشدار در مورد مسائل امنیتی در محیطهای تولیدی). سپس از Password_Validate برای بررسی انطباق با سیاستها استفاده کرده و در صورت موفقیتآمیز بودن، هش رمز عبور را به تاریخچه اضافه میکند و تاریخ آخرین تنظیم رمز عبور را به روز میکند.
نتیجهگیری
ابزار EnforcePass یک راهکار جامع و رایگان برای اعمال سیاستهای پیچیده و سفارشی رمز عبور در SQL Server است. با استفاده از این ابزار، میتوانید امنیت ورودهای استاندارد SQL Server خود را به طور قابل توجهی افزایش دهید، که برای محیطهای نیازمند امنیت بالا حیاتی است. این ابزار به مدیران پایگاه داده امکان میدهد تا کنترل بیشتری بر روی رمزهای عبور کاربران داشته باشند و از آسیبپذیریهای ناشی از رمزهای عبور ضعیف یا قدیمی جلوگیری کنند. با پیادهسازی EnforcePass، نه تنها به الزامات امنیتی میرسید، بلکه با ارائه یک چارچوب قوی، از دادههای حساس خود محافظت میکنید.