經過登錄觸發器與防火牆限制登錄IP

查看數據庫日誌發現好多非法登錄失敗的記錄,雖然以前已經更改了服務器和數據庫的管理員帳戶的用戶名,可是爲了保險起見,仍是再加上一道措施。建立登陸觸發器,只容許指定的IP訪問,服務器設置白名單,可是這隻適用於訪問用戶的IP固定不會常常變動的狀況。而咱們的開發服務器卻不符合這種狀況,只能想辦法實施一個動態的管理方案。html

聲明:本文爲Willem(Mongo)原創,轉載請註明原文連接https://segmentfault.com/a/11...sql

爲節省您的寶貴時間,請直接看觸發器2.0 + SQL變動入站規則便可,有問題可先參照問題彙總。若有其餘問題請在評論中留言,一塊兒探討。謝謝!shell

網上有文章(點擊查看) 寫的很詳細,這裏就不贅述了。下面的SQL是建立數據庫觸發器時給的默認模板,根據本身的狀況進行修改:數據庫

--====================================
--  Create database trigger template
--====================================
USE <database_name, sysname, AdventureWorks>
GO
IF EXISTS(
  SELECT *
    FROM sys.triggers
   WHERE name = N'<trigger_name, sysname, table_alter_drop_safety>'
     AND parent_class_desc = N'DATABASE'
)
            DROP TRIGGER <trigger_name, sysname, table_alter_drop_safety> ON DATABASE
GO
CREATE TRIGGER <trigger_name, sysname, table_alter_drop_safety> ON DATABASE
            FOR <data_definition_statements, , DROP_TABLE, ALTER_TABLE>
AS
IF IS_MEMBER ('db_owner') = 0
BEGIN
   PRINT 'You must ask your DBA to drop or alter tables!'
   ROLLBACK TRANSACTION
END
GO

登錄觸發器的建立腳本 v1.0

下面這個腳本只能DBA本身先向IP管理表插入容許訪問數據庫的IP,由於登錄觸發器只有身份驗證經過時才能觸發,因此暫時沒去記錄非法登錄失敗的IP:segmentfault

/**登陸觸發器將在登陸的身份驗證階段完成以後且用戶會話實際創建以前激發。若是身份驗證失敗,將不激發登陸觸發器。**/
USE [master]
GO
/****** Object:  Table [dbo].[ManagerIP]    Script Date: 2016年10月13日11:31:22 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--將數據庫回滾到原始配置狀態,而後刪除
IF DB_ID('LoginIP') IS NOT NULL
  ALTER DATABASE LoginIP SET SINGLE_USER WITH ROLLBACK IMMEDIATE
  DROP DATABASE LoginIP
GO
--建立數據庫
CREATE DATABASE [LoginIP]
GO
USE [LoginIP]
GO
--建立IP管理表
CREATE TABLE [dbo].[ManagerIP](
            [IP] [nvarchar](15) NOT NULL,
            [BlockState] [bit] NOT NULL,
            [FalseCount] [int] NOT NULL,
            [UpdateTime] [datetime] NULL,
            [TotalTimes] [int] NOT NULL,
 CONSTRAINT [PK_ManagerIP] PRIMARY KEY CLUSTERED
(
            [IP] 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
--插入容許經過的IP
INSERT INTO dbo.ManagerIP
        ( IP ,
          BlockState ,
          FalseCount ,
          UpdateTime ,
          TotalTimes
        )
VALUES  ( N'<local machine>' , -- IP - nvarchar(15)
          0 , -- BlockState - bit
          0 , -- FalseCount - int
          GETDATE() , -- UpdateTime - datetime
          0  -- TotalTimes - int
        )
INSERT INTO dbo.ManagerIP
        ( IP ,
          BlockState ,
          FalseCount ,
          UpdateTime ,
          TotalTimes
        )
VALUES  ( N'221.227.108.132' , -- IP - nvarchar(15)
          0 , -- BlockState - bit
          0 , -- FalseCount - int
          GETDATE() , -- UpdateTime - datetime
          0  -- TotalTimes - int
        )
GO
USE master
GO
--刪除觸發器(注意:登錄觸發器是存放在sys.server_triggers ,而不是sys.triggers)
IF EXISTS(SELECT * FROM sys.server_triggers WHERE name = 'check_login_ip') --AND parent_class_desc = N'LoginIP')
  DROP TRIGGER check_login_ip ON ALL SERVER
GO
--建立觸發器
--CREATE TRIGGER trigger_name ON LoginIP
CREATE TRIGGER check_login_ip ON ALL SERVER
            FOR LOGON
AS
IF IS_SRVROLEMEMBER ('sysadmin') = 1
BEGIN
   DECLARE @ip NVARCHAR(15);
   --只有直接在 DDL 或登陸觸發器內部引用 EVENTDATA 時,EVENTDATA 纔會返回數據。 若是 EVENTDATA 由其餘例程調用(即便這些例程由 DDL 或登陸觸發器進行調用),將返回 NULL。
   SET @ip = (SELECT EVENTDATA().value('(/EVENT_INSTANCE/ClientHost)[1]','NVARCHAR(15)'));
   IF NOT EXISTS(SELECT IP FROM [LoginIP].[dbo].[ManagerIP] WHERE IP = @ip)
   ROLLBACK TRANSACTION;
END
GO

相關連接:
點擊查看關於「EVENTDATA() 」的說明;
點擊查看「Sql Server中判斷表或者數據庫是否存在 」
點擊查看「IS_SRVROLEMEMBER('sysadmin') 」的詳細說明
點擊查看「登陸觸發器」的詳細說明
點擊查看「sys.server_triggers」的詳細說明
點擊查看「sys.triggers」的詳細說明安全

上述腳本複製到SQL Server直接執行就能夠。
腳本功能說明:自動刪除重名數據庫,而後建立;自動建立登錄IP管理表;自動刪除重名的登錄觸發器,而後從新建立。服務器

登錄觸發器的建立腳本 v2.0

上面提到了關於容許訪問數據庫的IP的管理問題,1.0 版本只能手動操做,而不能動態的自行管理IP,這樣就形成了不夠靈活的問題。
問題產生的情景:
若是我在表中添加了家裏和公司的IP,光是這就很麻煩,由於要去統計開發人員家中的IP地址,更況且一旦到客戶現場演示時,IP又沒法添加,只能讓已添加的IP進入數據庫手動添加,實在有些麻煩。可是又不得不去設置,由於查看一下數據庫記錄就不難看到,天天都會有不少外界的IP來光顧,雖然還未成功,但難保哪天被黑了。app

數據庫日誌

需求:來訪IP記入IP管理表,連續登錄失敗超過設定的次數就將該IP設置爲黑名單,能夠防止其暴力破解數據庫密碼。若是未超過設定次數登錄成功,則將失敗次數清0。聽起來跟輸入銀行密碼的感受差很少。下面是最終的邏輯圖:less

思惟導圖

又通過了一天半的時間,產生了下面最終的腳本,能夠動態控制IP,這裏的動態是相對前面的靜態而言;dom

/**登陸觸發器將在登陸的身份驗證階段完成以後且用戶會話實際創建以前激發。若是身份驗證失敗,將不激發登陸觸發器。**/
USE [master]
GO
/****** Object:  Table [dbo].[ManagerIP]    Script Date: 2016年10月13日11:31:22 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--建立IP管理表
IF OBJECT_ID (N'dbo.ManagerIP', N'U') IS NULL
BEGIN
            CREATE TABLE [dbo].[ManagerIP](
                        [IP] [nvarchar](15) NOT NULL,
                        [LockState] [bit] NOT NULL,
                        [FalseCount] [int] NOT NULL,
                        [UpdateTime] [datetime] NULL,
                        [TotalTimes] [int] NOT NULL,
             CONSTRAINT [PK_ManagerIP] PRIMARY KEY CLUSTERED
            (
                        [IP] ASC
            )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
            ) ON [PRIMARY];
END
GO
--插入容許經過的IP
IF (SELECT COUNT(*) FROM dbo.ManagerIP) = 0
BEGIN
            INSERT INTO dbo.ManagerIP
                                    ( IP ,
                                      LockState ,
                                      FalseCount ,
                                      UpdateTime ,
                                      TotalTimes
                                    )
            VALUES  ( N'<local machine>' , -- IP - nvarchar(15)
                                      0 , -- BlockState - bit
                                      0 , -- FalseCount - int
                                      GETDATE() , -- UpdateTime - datetime
                                      0  -- TotalTimes - int
                                    );
END
GO
--刪除觸發器(注意:登錄觸發器是存放在sys.server_triggers ,而不是sys.triggers)
IF EXISTS(SELECT * FROM sys.server_triggers WHERE name = 'check_login_ip')
BEGIN
            DROP TRIGGER check_login_ip ON ALL SERVER
END
GO
--建立觸發器
--CREATE TRIGGER trigger_name ON LoginIP
CREATE TRIGGER check_login_ip ON ALL SERVER
            FOR LOGON
AS
IF IS_SRVROLEMEMBER ('sysadmin') = 1
BEGIN
            DECLARE @ip NVARCHAR(15);
            --只有直接在 DDL 或登陸觸發器內部引用 EVENTDATA 時,EVENTDATA 纔會返回數據。 若是 EVENTDATA 由其餘例程調用(即便這些例程由 DDL 或登陸觸發器進行調用),將返回 NULL。
            SET @ip = (SELECT EVENTDATA().value('(/EVENT_INSTANCE/ClientHost)[1]','NVARCHAR(15)'));
            IF (SELECT COUNT(*) FROM [master].[dbo].[ManagerIP] WHERE IP = @ip AND LockState = 1) > 0
            BEGIN
                        ROLLBACK;
            END
            ELSE IF (SELECT COUNT(*) FROM [master].[dbo].[ManagerIP] WHERE IP = @ip AND LockState = 0) > 0
            BEGIN
                        UPDATE [master].[dbo].[ManagerIP] SET UpdateTime = GETDATE() WHERE IP = @ip;
                        SET NOEXEC ON;
            END
            --刪除臨時表
            ELSE IF OBJECT_ID(N'tempdb..#ErrorLog') IS NOT NULL
            BEGIN
                        DROP TABLE #ErrorLog;
            END
            --建立臨時表
            CREATE TABLE #ErrorLog(
                        [LogDate] [datetime],
                        [ProcessInfo] [nvarchar](200),
                        [Text] [NVARCHAR](1000)
            );
            --讀取當前日誌插入到臨時表
            INSERT INTO #ErrorLog EXEC sp_readerrorlog 0,1,'匹配',@ip;
            -- @p1 = 0, -- int 0爲當前日誌,1-9爲對應編號日誌
            --         @p2 = 1, -- int 1爲服務器日誌,2爲代理日誌
            --         @p3 = N'', -- nvarchar(4000) 包含的第一個字段
            --         @p4 = N'' -- nvarchar(4000) 包含的第二個字段
            DECLARE @TotalFalse int
            SET @TotalFalse = (SELECT COUNT(*) FROM #ErrorLog);
            IF @TotalFalse < 5
            BEGIN
                        INSERT INTO [dbo].[ManagerIP] ([IP], [LockState], [FalseCount], [UpdateTime], [TotalTimes])
                                    VALUES (@ip, 0, 0, GETDATE(), 0);
                        SET NOEXEC ON;
            END
            ELSE
            BEGIN
                        INSERT INTO [dbo].[ManagerIP] ([IP], [LockState], [FalseCount], [UpdateTime], [TotalTimes])
                                     VALUES (@ip, 1, 0, GETDATE(), 0);
            END
END
GO

參考資料:
點擊查看「捕獲登陸觸發器事件數據」;
點擊查看「GETDATE」;
點擊查看「OBJECT_ID」;

將當前日誌中已存在的非法IP導入數據庫

SQL以下:

USE master
GO
--設置容許錯誤密碼的最大次數
DECLARE @MaxFalse int;
SET @MaxFalse = 10;
--刪除日誌臨時表
IF OBJECT_ID(N'tempdb..#LogTemp') IS NOT NULL
            BEGIN
                        DROP TABLE #LogTemp;
            END
--建立日誌臨時表
CREATE TABLE #LogTemp(
            [LogDate] [datetime],
            [ProcessInfo] [nvarchar](200),
            [Text] [NVARCHAR](1000)
);
--刪除錯誤日誌臨時表
IF OBJECT_ID(N'tempdb..#ErrorLog') IS NOT NULL
            BEGIN
                        DROP TABLE #ErrorLog;
            END
--建立錯誤日誌臨時表
CREATE TABLE #ErrorLog(
            [ClientIP] [NVARCHAR](150),
            [TotalFalse] [int]
);
--從日誌臨時表篩選出登錄錯誤的日誌記錄
INSERT INTO #LogTemp EXEC sp_readerrorlog 0,1,'匹配','客戶端';
--對錯誤日誌進行統計
INSERT INTO #ErrorLog SELECT el.Text,COUNT(*) FROM #LogTemp AS el GROUP BY el.Text
DROP TABLE #LogTemp
DECLARE @clientiptext nvarchar(150)
DECLARE @total INT
DECLARE @ipstr nvarchar(15)
WHILE EXISTS ( SELECT ClientIP FROM #ErrorLog WHERE TotalFalse > @MaxFalse)
BEGIN
            SELECT TOP 1 @clientiptext = logs.ClientIP, @total = logs.TotalFalse FROM #ErrorLog AS logs WHERE logs.TotalFalse > @MaxFalse;
            DELETE #ErrorLog WHERE ClientIP = @clientiptext;
            SET @ipstr = SUBSTRING(@clientiptext,CHARINDEX('客戶端',@clientiptext) + 5,LEN(@clientiptext) - CHARINDEX('客戶端',@clientiptext) - 5);
            IF NOT EXISTS ( SELECT * FROM dbo.ManagerIP WHERE IP = @ipstr )
            BEGIN
                        INSERT INTO dbo.ManagerIP
                                ( IP ,
                                  LockState ,
                                  FalseCount ,
                                  UpdateTime ,
                                  TotalTimes
                                )
                        VALUES  ( @ipstr , -- IP - nvarchar(15)
                                  1 , -- LockState - bit
                                  @total , -- FalseCount - int
                                  GETDATE() , -- UpdateTime - datetime
                                  0  -- TotalTimes - int
                                )
            END
            ELSE IF @total < (SELECT FalseCount FROM dbo.ManagerIP)
            BEGIN
                        UPDATE dbo.ManagerIP SET FalseCount += @total WHERE IP = @ipstr
            END
            ELSE
            BEGIN
                        UPDATE dbo.ManagerIP SET FalseCount = @total WHERE IP = @ipstr
            END
END
DROP TABLE #ErrorLog
GO

依然存在的問題

這樣看起來是沒問題了,可是這樣作並沒能防止其餘人暴力破解密碼,只是破解以後不能經過該IP訪問數據庫而已,緣由以前也提到了,登陸觸發器只能在驗證經過後才能激活,反應到實際操做中就是經過驗證和未經過驗證的提示是不一樣的,只要發現問題提示發生的變化就能夠根據這個來判斷帳戶和密碼是否正確,固然最好的作法就是不要對外開放數據庫。不過這樣作就會讓開發人員麻煩些。如今的問題就是如何能夠將定爲非法的IP阻止在驗證以前。這裏想到的一個作法是將該IP放到防火牆阻止列表中,這樣的話沒法經過防火牆,那麼就不會訪問數據庫,也就不會進行驗證了。

由於前面的操做都配合觸發器、計劃任務或者維護計劃進行了自動化,因此這樣也要作到自動向防火牆中添加IP,這樣就須要腳原本進行操做了。而能夠操做防火牆的指令:

C:\Windows\system32>netsh advfirewall firewall add rule ?
用法: add rule name=<string>
      dir=in|out
      action=allow|block|bypass
      [program=<program path>]
      [service=<service short name>|any]
      [description=<string>]
      [enable=yes|no (default=yes)]
      [profile=public|private|domain|any[,...]]
      [localip=any|<IPv4 address>|<IPv6 address>|<subnet>|<range>|<list>]
      [remoteip=any|localsubnet|dns|dhcp|wins|defaultgateway| <IPv4 address>|<IPv6 address>|<subnet>|<range>|<list>]
      [localport=0-65535|<port range>[,...]|RPC|RPC-EPMap|IPHTTPS|any (default=any)]
      [remoteport=0-65535|<port range>[,...]|any (default=any)]
      [protocol=0-255|icmpv4|icmpv6|icmpv4:type,code|icmpv6:type,code| tcp|udp|any (default=any)]
      [interfacetype=wireless|lan|ras|any]
      [rmtcomputergrp=<SDDL string>]
      [rmtusrgrp=<SDDL string>]
      [edge=yes|deferapp|deferuser|no (default=no)]
      [security=authenticate|authenc|authdynenc|authnoencap|notrequired (default=notrequired)]

備註:
      - 將新的入站或出站規則添加到防火牆策略。
      - 規則名稱應該是惟一的,且不能爲 "all"。
      - 若是已指定遠程計算機或用戶組,則 security 必須爲  authenticate、authenc、authdynenc 或 authnoencap。
      - 爲 authdynenc 設置安全性可容許系統動態協商爲匹配 給定 Windows 防火牆規則的通訊使用加密。 根據現有鏈接安全規則屬性協商加密。選擇此選項後,只要入站 IPSec 鏈接已設置安全保護, 但未使用 IPSec 進行加密,計算機就可以接收該入站鏈接的第一個 TCP 或 UDP 包。一旦處理了第一個數據包,服務器將從新協商鏈接並對其進行升級,以便所 有後續通訊都徹底加密。
      - 若是 action=bypass,則 dir=in 時必須指定遠程計算機組。
      - 若是 service=any,則規則僅應用到服務。
      - ICMP 類型或代碼能夠爲 "any"。
      - Edge 只能爲入站規則指定。
      - AuthEnc 和 authnoencap 不能同時使用。
      - Authdynenc 僅當 dir=in 時有效。
      - 設置 authnoencap 後,security=authenticate 選項就變成可選參數。

示例:
      爲不具備封裝的 messenger.exe 添加入站規則:
      netsh advfirewall firewall add rule name="allow messenger" dir=in program="c:\programfiles\messenger\msmsgs.exe" security=authnoencap action=allow
      爲端口 80 添加出站規則:
      netsh advfirewall firewall add rule name="allow80" protocol=TCP dir=out localport=80 action=block
      爲 TCP 端口 80 通訊添加須要安全和加密的入站規則:
      netsh advfirewall firewall add rule name="Require Encryption for Inbound TCP/80" protocol=TCP dir=in localport=80 security=authdynenc action=allow
      爲 messenger.exe 添加須要安全的入站規則:
      netsh advfirewall firewall add rule name="allow messenger" dir=in program="c:\program files\messenger\msmsgs.exe" security=authenticate action=allow
      爲 SDDL 字符串標識的組 acmedomain\scanners 添加通過身份驗證的防火牆跳過規則:
      netsh advfirewall firewall add rule name="allow scanners" dir=in rmtcomputergrp=<SDDL string> action=bypass security=authenticate
      爲 udp- 的本地端口 5000-5010 添加出站容許規則
      Add rule name="Allow port range" dir=out protocol=udp localport=5000-5010 action=allow

經過幫助信息,咱們能夠了解到其中各個參數的含義及用途。而咱們所須要達到的目的是:防止某IP訪問該服務器上的數據庫。對照上面翻譯成簡單的腳本就是:

netsh advfirewall firewall add rule name=BlockIP dir=in action=block description=阻止訪問服務器數據庫,甚至全部程序。 enable=yes remoteip=115.29.77.97

而咱們須要把全部須要阻止的IP都要加入該規則中的 remoteip 中。不過在執行過程當中出現了權限限制的問題,退而求其次,將bat命令存儲爲bat文件。

SQL輸出bat文件

這種作法比較噁心,由於還要計劃任務去調用執行,並且保存的文件還有問題,由於複製其中的命令到新建的bat文件中能夠正常執行,可是直接執行該文件則有問題。不推薦此種方法,請查看下一種方法。

USE master
GO
--設置容許錯誤密碼的最大次數
DECLARE @MaxFalse int;
SET @MaxFalse = 66;
--刪除日誌臨時表
IF OBJECT_ID(N'tempdb..#LogTemp') IS NOT NULL
            BEGIN
                        DROP TABLE #LogTemp;
            END
--建立日誌臨時表
CREATE TABLE #LogTemp(
            [LogDate] [datetime],
            [ProcessInfo] [nvarchar](200),
            [Text] [NVARCHAR](1000)
);
--刪除錯誤日誌臨時表
IF OBJECT_ID(N'tempdb..#ErrorLog') IS NOT NULL
            BEGIN
                        DROP TABLE #ErrorLog;
            END
--建立錯誤日誌臨時表
CREATE TABLE #ErrorLog(
            [ClientIP] [NVARCHAR](150),
            [TotalFalse] [int]
);
--從日誌臨時表篩選出登錄錯誤的日誌記錄
INSERT INTO #LogTemp EXEC sp_readerrorlog 0,1,'匹配','客戶端';
--對錯誤日誌進行統計
INSERT INTO #ErrorLog SELECT el.Text,COUNT(*) FROM #LogTemp AS el GROUP BY el.Text
DROP TABLE #LogTemp
DECLARE @clientiptext nvarchar(150)
DECLARE @total int
DECLARE @ipstr nvarchar(15)
DELETE FROM ManagerIP WHERE FalseCount < @MaxFalse AND LockState = 1
WHILE EXISTS ( SELECT ClientIP FROM #ErrorLog WHERE TotalFalse > @MaxFalse)
BEGIN
            SELECT TOP 1 @clientiptext = logs.ClientIP, @total = logs.TotalFalse FROM #ErrorLog AS logs WHERE logs.TotalFalse > @MaxFalse;
            DELETE #ErrorLog WHERE ClientIP = @clientiptext;
            SET @ipstr = SUBSTRING(@clientiptext,CHARINDEX('客戶端',@clientiptext) + 5,LEN(@clientiptext) - CHARINDEX('客戶端',@clientiptext) - 5);
            IF NOT EXISTS ( SELECT * FROM dbo.ManagerIP WHERE IP = @ipstr )
            BEGIN
                        INSERT INTO dbo.ManagerIP
                                ( IP ,
                                  LockState ,
                                  FalseCount ,
                                  UpdateTime ,
                                  TotalTimes
                                )
                        VALUES  ( @ipstr , -- IP - nvarchar(15)
                                  1 , -- LockState - bit
                                  @total , -- FalseCount - int
                                  GETDATE() , -- UpdateTime - datetime
                                  0  -- TotalTimes - int
                                )
            END
END
DROP TABLE #ErrorLog;
EXEC sys.sp_configure @configname = 'show advanced options', -- varchar(35)
    @configvalue = 1; -- int
GO
RECONFIGURE;
GO
EXEC sys.sp_configure @configname = 'Ole Automation Procedures', -- varchar(35)
    @configvalue = 1; -- int
GO
RECONFIGURE;
GO
DECLARE @blockips nvarchar(MAX) = '';
DECLARE @tempip nvarchar(15) = '';
IF OBJECT_ID(N'tempdb..#ForFirewall') IS NOT NULL
            BEGIN
                        DROP TABLE #ForFirewall
            END
CREATE TABLE #ForFirewall(
            BlockIP NVARCHAR(15)
);
INSERT INTO #ForFirewall SELECT IP FROM dbo.ManagerIP WHERE LockState = 1
WHILE EXISTS (SELECT TOP 1 BlockIP FROM #ForFirewall ORDER BY BlockIP)
BEGIN
            SET @tempip = (SELECT TOP 1 BlockIP FROM #ForFirewall ORDER BY BlockIP);
            SET @blockips = @blockips + @tempip + ',';
            DELETE  FROM #ForFirewall WHERE BlockIP = @tempip;
END
SET @blockips = 'netsh advfirewall firewall delete rule name = BlockIP & netsh advfirewall firewall add rule name = BlockIP dir = in action = block enable = yes remoteip = ' + SUBSTRING(@blockips, 0, LEN(@blockips) - 1);
--EXEC master..xp_cmdshell @blockips;
DECLARE @TEXT VARBINARY(MAX)
SET @TEXT = CAST(@blockips AS VARBINARY(max))
 
DECLARE @ObjectToken INT
EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT
EXEC sp_OASetProperty @ObjectToken, 'Type', 1
EXEC sp_OAMethod @ObjectToken, 'Open'
EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @TEXT
EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, 'E:\Microsoft SQL Server Backup Log\DynamicIPControl\BlockIP.bat', 2
EXEC sp_OAMethod @ObjectToken, 'Close'EXEC sp_OADestroy @ObjectToken
GO

SQL變動入站規則

在SQL中直接執行cmd命令沒法繞過管理員權限,嘗試轉爲管理員權限可是並未成功。後來在服務器上執行下面的SQL則沒有出現權限問題,能夠順利更改防火牆入站規則。須要注意的是該腳本包含了從日誌導入非法IP,因此在建立計劃任務的時候只使用該腳本就能夠,不要再單獨執行導入非法IP的腳本了。

USE master
GO
    IF OBJECT_ID('ManagerIP') IS NULL
    BEGIN
    CREATE TABLE [dbo].[ManagerIP](
        [IP] [nvarchar](15) NOT NULL,
        [LockState] [bit] NOT NULL,
        [FalseCount] [int] NOT NULL,
        [UpdateTime] [datetime] NULL,
        [TotalTimes] [int] NOT NULL,
     CONSTRAINT [PK_ManagerIP] PRIMARY KEY CLUSTERED 
    (
        [IP] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    END
    GO
--設置容許錯誤密碼的最大次數
DECLARE @MaxFalse int;
SET @MaxFalse = 66;
--刪除日誌臨時表
IF OBJECT_ID(N'tempdb..#LogTemp') IS NOT NULL
            BEGIN
                        DROP TABLE #LogTemp;
            END
--建立日誌臨時表
CREATE TABLE #LogTemp(
            [LogDate] [datetime],
            [ProcessInfo] [nvarchar](200),
            [Text] [NVARCHAR](1000)
);
--刪除錯誤日誌臨時表
IF OBJECT_ID(N'tempdb..#ErrorLog') IS NOT NULL
            BEGIN
                        DROP TABLE #ErrorLog;
            END
--建立錯誤日誌臨時表
CREATE TABLE #ErrorLog(
            [ClientIP] [NVARCHAR](150),
            [TotalFalse] [int]
);
--從日誌臨時表篩選出登錄錯誤的日誌記錄
INSERT INTO #LogTemp EXEC sp_readerrorlog 0,1,'匹配','客戶端';
--對錯誤日誌進行統計
INSERT INTO #ErrorLog SELECT el.Text,COUNT(*) FROM #LogTemp AS el GROUP BY el.Text
DROP TABLE #LogTemp
DECLARE @clientiptext NVARCHAR(150)
DECLARE @total INT
DECLARE @ipstr NVARCHAR(15)
DECLARE @falsecount INT
DELETE FROM ManagerIP WHERE FalseCount < @MaxFalse AND LockState = 1
WHILE EXISTS ( SELECT ClientIP FROM #ErrorLog WHERE TotalFalse > @MaxFalse)
BEGIN
            SELECT TOP 1 @clientiptext = logs.ClientIP, @total = logs.TotalFalse FROM #ErrorLog AS logs WHERE logs.TotalFalse > @MaxFalse;
            DELETE #ErrorLog WHERE ClientIP = @clientiptext;
            SET @ipstr = SUBSTRING(@clientiptext,CHARINDEX('客戶端',@clientiptext) + 5,LEN(@clientiptext) - CHARINDEX('客戶端',@clientiptext) - 5);
                                    SET @falsecount = (SELECT TOP 1 FalseCount FROM dbo.ManagerIP);
            IF NOT EXISTS ( SELECT * FROM dbo.ManagerIP WHERE IP = @ipstr )
            BEGIN
                        INSERT INTO dbo.ManagerIP
                                ( IP ,
                                  LockState ,
                                  FalseCount ,
                                  UpdateTime ,
                                  TotalTimes
                                )
                        VALUES  ( @ipstr , -- IP - nvarchar(15)
                                  1 , -- LockState - bit
                                  @total , -- FalseCount - int
                                  GETDATE() , -- UpdateTime - datetime
                                  0  -- TotalTimes - int
                                )
            END
            ELSE IF @total < @falsecount
            BEGIN
                        UPDATE dbo.ManagerIP SET FalseCount = @falsecount + @total WHERE IP = @ipstr
            END
            ELSE
            BEGIN
                        UPDATE dbo.ManagerIP SET FalseCount = @total WHERE IP = @ipstr
            END
END
DROP TABLE #ErrorLog;
EXEC sys.sp_configure @configname = 'show advanced options', -- varchar(35)
    @configvalue = 1; -- int
GO
RECONFIGURE;
GO
EXEC sys.sp_configure @configname = 'xp_cmdshell', -- varchar(35)
    @configvalue = 1; -- int
GO
RECONFIGURE;
GO
DECLARE @blockips01 varchar(8000) = '';
DECLARE @blockips02 varchar(8000) = '';
DECLARE @tempip nvarchar(15) = '';
--DECLARE @addrule NVARCHAR(1000);
IF OBJECT_ID(N'tempdb..#ForFirewall') IS NOT NULL
            BEGIN
                        DROP TABLE #ForFirewall
            END
CREATE TABLE #ForFirewall(
            BlockIP NVARCHAR(15)
);
INSERT INTO #ForFirewall SELECT IP FROM dbo.ManagerIP WHERE LockState = 1
WHILE EXISTS (SELECT TOP 1 BlockIP FROM #ForFirewall ORDER BY BlockIP)
BEGIN
            SET @tempip = (SELECT TOP 1 BlockIP FROM #ForFirewall ORDER BY BlockIP);
            IF LEN(@blockips01) > 3500
                BEGIN
                    SET @blockips02 = @blockips02 + @tempip + ',';
                END
            ELSE
                BEGIN
                    SET @blockips01 = @blockips01 + @tempip + ',';
                END
            DELETE  FROM #ForFirewall WHERE BlockIP = @tempip;
END
IF @blockips01 != ''
BEGIN
    SET @blockips01 = '@netsh advfirewall firewall delete rule name = BlockIP01 & netsh advfirewall firewall add rule name = BlockIP01 dir = in action = block enable = yes remoteip = ' + SUBSTRING(@blockips01, 0, LEN    (@blockips01) - 1);
    EXEC master..xp_cmdshell @blockips01;
END
IF @blockips02 != ''
BEGIN
    SET @blockips02 = '@netsh advfirewall firewall delete rule name = BlockIP02 & netsh advfirewall firewall add rule name = BlockIP02 dir = in action = block enable = yes remoteip = ' + SUBSTRING(@blockips02, 0, LEN    (@blockips02) - 1);
    EXEC master..xp_cmdshell @blockips02;
END
SELECT @blockips01
SELECT @blockips02
GO

EXEC sys.sp_configure @configname = 'xp_cmdshell', -- varchar(35)
    @configvalue = 0; -- int
GO
RECONFIGURE;
GO
EXEC sys.sp_configure @configname = 'show advanced options', -- varchar(35)
    @configvalue = 0; -- int
GO
RECONFIGURE;
GO

執行結果以下:

SQL執行結果

此時再查看防火牆入站規則中的做用域就會發現裏面多了不少的遠程IP地址。

防火牆入站規則

接下來要作的就簡單多了,建立代理做業,將上面的代碼拷貝粘貼進代理做業要執行的SQL區,讓做業循環進行就能夠了。這樣當惡意訪問次數操做指定次數(代碼裏能夠修改MaxFalse)就會被放到數據庫黑名單,同時加入防火牆阻止名單。

相關資料:
點擊查看xp_cmdshell
點擊查看Ole Automation Procedures

最終版本以下:

USE master;
GO
-- 建立IP管理表
IF OBJECT_ID('ManagerIP') IS NULL
BEGIN
    CREATE TABLE [dbo].[ManagerIP]
    (
        [IP] [NVARCHAR](15) NOT NULL,
        [LockState] [BIT] NOT NULL,
        [FalseCount] [INT] NOT NULL,
        [UpdateTime] [DATETIME] NULL,
        [TotalTimes] [INT] NOT NULL,
        CONSTRAINT [PK_ManagerIP]
            PRIMARY KEY CLUSTERED ([IP] ASC)
            WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
                  ALLOW_PAGE_LOCKS = ON
                 ) ON [PRIMARY]
    ) ON [PRIMARY];
END;
GO

IF NOT EXISTS (SELECT IP FROM dbo.ManagerIP WHERE IP = 'localhost')
BEGIN
    INSERT INTO dbo.ManagerIP
    (
        IP,
        LockState,
        FalseCount,
        UpdateTime,
        TotalTimes
    )
    VALUES
    (   N'localhost', -- IP - nvarchar(15)
        0,            -- LockState - bit
        0,            -- FalseCount - int
        GETDATE(),    -- UpdateTime - datetime
        0             -- TotalTimes - int
        );
END;
GO

--刪除日誌臨時表
IF OBJECT_ID(N'TempLog') IS NOT NULL
BEGIN
    DROP TABLE dbo.TempLog;
END;
--建立日誌臨時表
CREATE TABLE dbo.TempLog
(
    [LogDate] [DATETIME] NOT NULL,
    [ProcessInfo] [NVARCHAR](200) NULL,
    [Text] [NVARCHAR](1000) NULL
);
--刪除錯誤日誌臨時表
IF OBJECT_ID(N'ErrorLog') IS NOT NULL
BEGIN
    DROP TABLE dbo.ErrorLog;
END;
--建立錯誤日誌臨時表
CREATE TABLE dbo.ErrorLog
(
    [ClientIP] [NVARCHAR](150) NOT NULL,
    [TotalFalse] [INT] NOT NULL
);
--從日誌臨時表篩選出登錄錯誤的日誌記錄
INSERT INTO dbo.TempLog
(
    LogDate,
    ProcessInfo,
    Text
)
EXEC sp_readerrorlog 0, 1, N'匹配', N'客戶端';
--對錯誤日誌進行統計
INSERT INTO dbo.ErrorLog
(
    ClientIP,
    TotalFalse
)
SELECT Text ClientIP,
       COUNT(*) TotalFalse
FROM dbo.TempLog
GROUP BY Text;
GO

-- 整理非法訪問的IP
DECLARE @clientIP NVARCHAR(MAX);
DECLARE @totalFalse INT;
DECLARE @ipStr NVARCHAR(20);
DECLARE @falseCount INT;
DECLARE @MaxFalse INT;
-- 設置容許錯誤密碼的最大次數
SET @MaxFalse = 66;

DELETE FROM dbo.ManagerIP
WHERE FalseCount < @MaxFalse
      AND LockState = 1;

DECLARE LogCursor CURSOR FORWARD_ONLY READ_ONLY LOCAL FOR
SELECT ClientIP,
       TotalFalse
FROM dbo.ErrorLog
WHERE TotalFalse > @MaxFalse;

OPEN LogCursor;
FETCH NEXT FROM LogCursor
INTO @clientIP,
     @totalFalse;
WHILE @@FETCH_STATUS = 0
BEGIN
    DELETE FROM dbo.ErrorLog
    WHERE ClientIP = @clientIP;
    SET @ipStr
        = SUBSTRING(@clientIP, CHARINDEX('客戶端', @clientIP) + 5, LEN(@clientIP) - CHARINDEX('客戶端', @clientIP) - 5);
    SET @falseCount =
    (
        SELECT TOP (1) FalseCount FROM dbo.ManagerIP ORDER BY FalseCount
    );
    IF NOT EXISTS (SELECT IP FROM dbo.ManagerIP WHERE IP = @ipStr)
    BEGIN
        INSERT INTO dbo.ManagerIP
        (
            IP,
            LockState,
            FalseCount,
            UpdateTime,
            TotalTimes
        )
        VALUES
        (   @ipStr,      -- IP - nvarchar(15)
            1,           -- LockState - bit
            @totalFalse, -- FalseCount - int
            GETDATE(),   -- UpdateTime - datetime
            0            -- TotalTimes - int
            );
    END;
    ELSE IF @totalFalse < @falseCount
    BEGIN
        UPDATE dbo.ManagerIP
        SET FalseCount = @falseCount + @totalFalse
        WHERE IP = @ipStr;
    END;
    ELSE
    BEGIN
        UPDATE dbo.ManagerIP
        SET FalseCount = @totalFalse
        WHERE IP = @ipStr;
    END;
    FETCH NEXT FROM LogCursor
    INTO @clientIP,
         @totalFalse;
END;
CLOSE LogCursor;
DEALLOCATE LogCursor;

-- 操做防火牆
EXEC sys.sp_configure @configname = 'show advanced options', -- varchar(35)
                      @configvalue = 1;                      -- int
GO

RECONFIGURE;
GO
EXEC sys.sp_configure @configname = 'xp_cmdshell', -- varchar(35)
                      @configvalue = 1;            -- int
GO
RECONFIGURE;
GO
DECLARE @blockips01 VARCHAR(8000) = '';
DECLARE @blockips02 VARCHAR(8000) = '';
DECLARE @tempip NVARCHAR(15) = '';
--DECLARE @addrule NVARCHAR(1000);
IF OBJECT_ID(N'ForFirewall') IS NOT NULL
BEGIN
    DROP TABLE dbo.ForFirewall;
END;
CREATE TABLE dbo.ForFirewall
(
    BlockIP NVARCHAR(15) NOT NULL
);
INSERT INTO dbo.ForFirewall
(
    BlockIP
)
SELECT IP
FROM dbo.ManagerIP
WHERE LockState = 1;
WHILE EXISTS (SELECT TOP (1) BlockIP FROM dbo.ForFirewall ORDER BY BlockIP)
BEGIN
    SET @tempip =
    (
        SELECT TOP (1) BlockIP FROM dbo.ForFirewall ORDER BY BlockIP
    );
    IF LEN(@blockips01) > 3500
    BEGIN
        SET @blockips02 = @blockips02 + @tempip + ',';
    END;
    ELSE
    BEGIN
        SET @blockips01 = @blockips01 + @tempip + ',';
    END;
    DELETE FROM dbo.ForFirewall
    WHERE BlockIP = @tempip;
END;
IF @blockips01 <> ''
BEGIN
    SET @blockips01
        = '@netsh advfirewall firewall delete rule name = BlockIP01 & netsh advfirewall firewall add rule name = BlockIP01 dir = in action = block enable = yes remoteip = '
          + SUBSTRING(@blockips01, 0, LEN(@blockips01));
    EXEC master..xp_cmdshell @blockips01;
END;
IF @blockips02 <> ''
BEGIN
    SET @blockips02
        = '@netsh advfirewall firewall delete rule name = BlockIP02 & netsh advfirewall firewall add rule name = BlockIP02 dir = in action = block enable = yes remoteip = '
          + SUBSTRING(@blockips02, 0, LEN(@blockips02));
    EXEC master..xp_cmdshell @blockips02;
END;
SELECT @blockips01 Firewalls_CMD_STR_01;
SELECT @blockips02 Firewalls_CMD_STR_02;
GO

EXEC sys.sp_configure @configname = 'xp_cmdshell', -- varchar(35)
                      @configvalue = 0;            -- int
GO
RECONFIGURE;
GO
EXEC sys.sp_configure @configname = 'show advanced options', -- varchar(35)
                      @configvalue = 0;                      -- int
GO
RECONFIGURE;
GO

IF OBJECT_ID(N'TempLog') IS NOT NULL
BEGIN
    DROP TABLE dbo.TempLog;
END;
IF OBJECT_ID(N'ErrorLog') IS NOT NULL
BEGIN
    DROP TABLE dbo.ErrorLog;
END;
IF OBJECT_ID(N'ForFirewall') IS NOT NULL
BEGIN
    DROP TABLE dbo.ForFirewall;
END;
GO

問題彙總

  • 小問題

在沒有將本機添加到表中的狀況下建立了登錄觸發器,會讓本機也沒法鏈接數據庫,以下面所述。
在調試SQL以前,我並無向表中添加數據,結果可想而知,重連數據庫就會跳出這麼個東西來。

Windows身份驗證登錄

SQL Server身份驗證登錄

圖中分別用了「Windows 身份驗證」和「SQL Server 身份驗證」,都沒法鏈接,頓時感受本身真逗,把本身鎖外面了,還沒帶鑰匙。不過我相信SQL Server會兼容我這種智商的存在,網上查了下,果然能夠。具體作法:

打開命令行,Ctrl + Rcmd回車;
經過DAC登陸到服務器(mongo爲主機名):sqlcmd -A -S mongo,而後會出現這種提示符就說明登錄成功:1>
輸入「DROP TRIGGER [check_login_ip] ON ALL SERVER」回車(「check_login_ip」爲觸發器名);
輸入「go」回車;

刪除登錄觸發器

而後再去鏈接數據庫試試吧,問題解決了。接下來就是把本身的IP插入到數據庫,而後作進一步的測試。

  • 中問題

SQLServer 錯誤: 15404,沒法獲取有關 Windows NT 組/用戶 NQAdministrator 的信息,錯誤代碼 0534。
很明顯是帳戶問題,原由:數據庫安裝完成以後,更改過計算機管理員帳戶名,可是數據庫這邊的用戶並未作同步設置,仍是用的原用戶名。
解決方法:鏈接數據庫,而後在【安全性】-【登陸名】下找到原管理員用戶名,若是是服務器通常都是帶有「Administrator」的那一個,右鍵重命名,改爲如今的以後重啓SQL Server訪問就能夠了。

數據庫管理員帳戶改名

  • 大問題

請求的操做須要提高(做爲管理員運行)。

這個問題發生在用SQL經過「xp_cmdshell」執行「EXEC master..xp_cmdshell '@netsh advfirewall firewall delete rule name = BlockIP'」時提示:請求的操做須要提高(做爲管理員運行)。 之因此說它是大問題是由於網上的答案要麼不適合我遇到的問題,要麼無效,總之不對症。近期由於開發部同事須要調用cmd老是失敗,才聯想到cmd的安全權限問題。解決方案:將「SQL Server (SQLSERVER)」服務的登陸用戶更改成管理員用戶,並將管理員用戶添加到cmd.exe的安全權限裏;或者是新建一個用戶,並將「SQL Server (SQLSERVER)」服務的登陸用戶更改成新用戶,並將新用戶添加到cmd.exe的安全權限裏。如此一來,SQL Server就有調用cmd.exe的權限了。

原由:我此次要操做的是master數據庫,而我並未將管理員用戶映射到改數據庫。

解決方法:選擇要映射的數據庫,在【安全性】-【用戶】下查看是否有管理員用戶,若是沒有將其添加進來便可。添加方法,就是到全局的【安全性】-【登陸名】下找到管理員用戶名,而後右鍵,選擇屬性,選擇用戶映射,勾選要映射的數據庫並選擇數據庫角色成員身份,這裏要勾上「db_owner」。

指定數據庫下的用戶帳戶管理

用戶映射數據庫

Willem 更新於 2016年10月21日10:29:37

相關文章
相關標籤/搜索