一、背景
有一天我發現SQL Server服務器的錯誤日志中包括非常多關於sa用戶的登陸錯誤信息:“Login failed for user 'sa'. 原因: 評估密碼時出錯。[客戶端: XX.XX.XX.XX]”。可是我很久之前就已經禁用了sa用戶,怎么還會有那么多的sa用戶登陸信息呢?我猜想是有人在暴力破解我們數據庫的sa用戶的密碼;關於這種攻擊,大家有沒好的解決方案呢?
我查找了一些資料,暫時沒有找到好的解決方案。我只想到一個暫時緩解壓力的辦法,那就是從錯誤信息中統計出登陸sa用戶的客戶端IP地址,再設置防火牆把這些IP過濾掉。
那現在如何解決IP的統計呢?使用SSMS是根本無法進行統計,因為錯誤日志的記錄太多了。SSMS打開錯誤日志的方式如下圖Figure1和Figure2所示;
(Figure1:SQL Server 日志)
(Figure2:sa登陸信息)
經過資料的查找,發現有兩種方式可以對錯誤日志進行過濾:
1. 利用SQL Server系統存儲過程xp_readerrorlog進行過濾;
2. 利用默認跟蹤(Default Trace)進行過濾;
二、xp_readerrorlog實現錯誤日志過濾
(一) 關於錯誤日志的基本操作可以參考:SQL Server錯誤日志收縮(ERRORLOG)。首先了解錯誤日志文件的路徑和大小,可以通過Figure3的方式找到文件,查看大小。
(Figure3:SQL Server ErrorLog文件信息)
除了Figure3直接找到錯誤日志的方式之外,我們還可以通過執行存儲過程EXEC xp_enumerrorlogs返回表的形式進行查看信息,如Figure4所示。xp_enumerrorlogs存儲過程還提供參數,默認值為1(如果沒有提供參數表示傳入的參數為1),2的時候表示查詢SQL Server 代理錯誤日志列表,如Figure13所示。
--Script1:獲取[SQL Server]錯誤日志列表 EXEC xp_enumerrorlogs EXEC xp_enumerrorlogs 1
(Figure4:SQL Server 錯誤日志列表)
(二) 接下來了解系統存儲過程:xp_readerrorlog,它一共有7個參數,分別是:
1. 存檔編號(0~99)
2. 日志類型(1為SQL Server日志,2為SQL Server Agent日志)
3. 查詢包含的字符串
4. 查詢包含的字符串
5. LogDate開始時間
6. LogDate結束時間
7. 結果排序,按LogDate排序(Desc、Asc)
(三) 接着講解xp_readerrorlog系統存儲過程的運用:
1. 如果你想查詢當前SQL Server錯誤日志文件(當前正在寫入錯誤信息的文件)的內容,請執行SQL腳本:EXEC xp_readerrorlog,存檔編號的默認值為0,它相當於打開文件ERRORLOG(路徑可參考Figure3),如果想讀取其它的歷史錯誤日志文件,直接填寫對應的存檔編號就可以了(存檔編號可以參考Figure4),下面3條SQL語句的執行效果是一樣的:
--Script2:查詢當前SQL Server日志信息 EXEC xp_readerrorlog EXEC xp_readerrorlog 0 EXEC xp_readerrorlog 0,1
(Figure5:當前SQL Server錯誤日志)
查詢存檔編號為n(n Between 0 And 99)的SQL Server日志信息:Exec xp_readerrorlog n,n為什么只能0~99?可參考:SQL Server錯誤日志收縮(ERRORLOG)
2. 我們繼續學習其它參數的使用,查看SQL Server日志歷史存檔為1文件中,發生的時間為2013-05-09至2013-05-10之間的錯誤,排序方式為時間的倒排序,為了滿足上面的要求,執行下面的SQL腳本:
--Script3:查看SQL Server日志存檔為1,時間范圍為XX,按照時間反排序 EXEC xp_readerrorlog 1,1,NULL,NULL,'2013-05-09','2013-05-10','DESC'
(Figure6:錯誤日志時間過濾)
3. 查看SQL Server日志歷史存檔為1文件中,錯誤內容里面包含字符串:'Login failed for user ''sa''',並且包括字符串:'192.168.1.5',發生的時間為2013-05-09至2013-05-10之間的錯誤,排序方式為時間的倒排序,為了滿足上面的要求,執行下面的SQL腳本:
--Script4:查看SQL Server日志存檔為,包含XX字符串,並且包含%%字符串,時間范圍為XX,按照時間反排序 EXEC xp_readerrorlog 1,1,'Login failed for user ''sa''','192.168.1.5','2013-05-09','2013-05-10','DESC'
(Figure7:錯誤日志字符串+日期過濾)
三、Default Trace實現錯誤日志過濾
1. 關於默認跟蹤(Default Trace)基礎知識可以參考: SQL Server 默認跟蹤(Default Trace)
2. 要過濾錯誤日志,那首先就要知道在默認跟蹤中那個類型trace_event_id是代表錯誤日志的,在Read Default Trace中描述了關於trace_event_id的信息:If you are interested in what the default trace has been setup to capture you can run this (Note you cannot edit the default trace!)。
--Script5:trace_event SELECT * FROM fn_trace_geteventinfo(1) tg INNER JOIN sys.trace_events te ON tg.[eventid] = te.[trace_event_id] INNER JOIN sys.trace_columns tc ON tg.[columnid] = tc.[trace_column_id] WHERE te.name like '%login%'
(Figure8:trace_event_id信息)
通過上面Script5的SQL腳本,我們知道需要監控EventName為:Audit Login Failed,trace_event_id為20的信息,如Figure8所示。另外查看方式:sp_trace_setevent,在這里你也可以找到關於Login Failed描述所對應的Event number。
3. 下面我們來實現使用fn_trace_gettable讀取log.trc文件的方式來過濾錯誤日志:
--Script6:返回登陸錯誤信息 -- ============================================= -- Author: <聽風吹雨> -- Create date: <2013.05.03> -- Description: <讀取、過濾log.trc文件> -- Blog: <http://www.cnblogs.com/gaizai/> -- ============================================= DECLARE @tracefile NVARCHAR(MAX) SET @tracefile = (SELECT LEFT([path],LEN([path])-CHARINDEX('\',REVERSE([path])))+ '\log.trc' FROM sys.traces WHERE [is_default] = 1) SELECT gt.[ServerName] ,gt.[DatabaseName] ,gt.[SPID] ,gt.[StartTime] ,gt.[ObjectName] ,gt.[objecttype] [ObjectTypeID]--http://msdn.microsoft.com/en-us/library/ms180953.aspx ,sv.[subclass_name] [ObjectType] ,e.[category_id] [CategoryID] ,c.[Name] [Category] ,gt.[EventClass] [EventID] ,e.[Name] [EventName] ,gt.[LoginName] ,gt.[ApplicationName] ,gt.[TextData] FROM fn_trace_gettable(@tracefile, DEFAULT) gt LEFT JOIN sys.trace_subclass_values sv ON gt.[eventclass] = sv.[trace_event_id] AND sv.[subclass_value] = gt.[objecttype] INNER JOIN sys.trace_events e ON gt.[eventclass] = e.[trace_event_id] INNER JOIN sys.trace_categories c ON e.[category_id] = c.[category_id] WHERE gt.[spid] > 50 AND gt.[databasename] = 'master' AND e.category_id = 8 --category 8表示安全 AND gt.[starttime] >= '2013-05-13 12:00:00' AND gt.[starttime] <= '2013-05-14' AND e.trace_event_id = 20 ORDER BY StartTime DESC
(Figure9:Default Trace返回的錯誤日志)
4. 為了讓默認跟蹤Default Trace和xp_readerrorlog返回的數據進行對比,我們再次執行xp_readerrorlog,使用參數盡量與Default Trace保持一致。
--Script7:對比 EXEC xp_readerrorlog 0,1,'Login failed for user ''sa''','','2013-05-13 12:00:00','2013-05-14','DESC'
(Figure10:xp_readerrorlog返回的錯誤日志)
對比Figure9與Figure10的信息,發現返回的記錄數基本上是相同的。
四、補充說明
1. xp_enumerrorlogs和xp_readerrorlog除了可以查看【SQL Server錯誤日志】還可以查看【SQL Server 代理錯誤日志】。在SSMS中可以直接查看代理錯誤日志,如Figure11所示,它的路徑和文件如Figure12所示,xp_enumerrorlogs值為2的時候表示查詢SQL Server 代理錯誤日志列表,如Figure13所示。
--Script5:獲取[SQL Server 代理]錯誤日志列表 EXEC xp_enumerrorlogs 2
(Figure11:SQL Server Agent日志)
(Figure12:SQL Server Agent ErrorLog文件信息)
(Figure13:SQL Server 代理錯誤日志列表)
值得注意的是【SQL Server 代理錯誤日志】並沒有SQLAGENT.0這個文件,Figure13中的當前 = Figure14中的SQLAGENT.OUT = Figure15中的存檔#10記錄;如果要確認這一點,你可以通過日志文件的內容和文件的大小進行確認。
2. 由於我的數據庫需要外網訪問,所以沒有辦法做很多的改動,比如換數據庫的端口,已經有太多的運用程序在使用了。(這個可以通過解決方案設計解決:SQL Server 數據庫帳號密碼安全設計)
3. 由於是sa無法在【是否允許連接到數據庫引擎】選擇【拒絕】選項,如Figure14所示。有沒好的辦法可以防止sa的暴力破解呢?雖然這樣不會造成數據庫創建鏈接,但是這樣會造成:消耗網絡流量;消耗數據庫服務器的性能;消耗SQL Server日志容量;消耗ERRORLOG日志文件帶來的磁盤IO(雖然可以通過Figure15那樣設置不記錄登陸信息,但是這樣日志記錄就沒有意義了);
(Figure14:帳號設置)
(Figure15:設置日志記錄)
北京-宋沄劍提到把sa修改掉,修改后只是提示信息變為:“Login failed for user 'sa'. 原因: 找不到與所提供的名稱相匹配的登錄名。 [客戶端: 60.190.118.153]”,問題還是存在。(修改之前錯誤信息是:Login failed for user 'sa'. 原因: 評估密碼時出錯。 [客戶端: 60.190.118.153])
4. 使用sp_helptext是無法找到系統存儲過程xp_enumerrorlogs和xp_readerrorlog的源代碼的,這個是為什么呢?
--查看存儲過程或者視圖源碼 exec sp_helptext 'xp_enumerrorlogs' exec sp_helptext 'xp_readerrorlog'
(Figure16:sp_helptext)
5. 關於ERROR文件記錄內容的詳細解析可以參考:SQLSERVER errorlog講解
6. 如果不想在log.trc中看到有關Login failed for user 'sa'.的錯誤日志,可以修改默認跟蹤嗎?在sp_trace_setevent (Transact-SQL)中提到:
1) 如果將 on 設置為 1,並且 column_id 為 NULL,則將事件設置為 ON 並清除所有列。如果 column_id 不為 NULL,則將該事件的列設置為 ON。
2) 如果將 on 設置為 0,並且 column_id 為 NULL,則將事件設置為 OFF 並清除所有列。如果 column_id 不為 NULL,則將列設置為 OFF。
(Figure17:修改默認跟蹤錯誤)
7. 關於使用xp_readerrorlog讀取ERROR文件記錄內容,有沒方法一次性讀取所有的ERROR文件的數據呢?使用UNION ALL?
五、參考文獻
sql server xp_readerrorlog SQL語句查看錯誤日志
在SQL Server 2005中使用xp_ReadErrorLog讀取錯誤日志
在sql server 2008中使用xp_ReadErrorLog讀取錯誤日志
Using xp_ReadErrorLog in SQL Server 2005
SQL Server 存儲過程 sp_helptext的不足以及解決方案
fn_trace_gettable (Transact-SQL)