前段時間公司又一輪安全審查,要求對各項目進行安全掃描,排查漏洞並修複,手上有幾個歷史項目,要求在限定的時間內全部修複並提交安全報告,也不清楚之前是如何做的漏洞修複,這次使用工具掃描出來平均每個項目都還有大概100來個漏洞。這些漏洞包括SQL語句註入,C#後端代碼,XML文件,以及前端HTML,JS代 ...
前段時間公司又一輪安全審查,要求對各項目進行安全掃描,排查漏洞並修複,手上有幾個歷史項目,要求在限定的時間內全部修複並提交安全報告,也不清楚之前是如何做的漏洞修複,這次使用工具掃描出來平均每個項目都還有大概100來個漏洞。這些漏洞包括SQL語句註入,C#後端代碼,XML文件,以及前端HTML,JS代碼幾個方面,由於一些項目比較老舊,限定的時間又短,做大的改動如果測試不到位,很難保證不出什麼問題,所以做了一些應及處理,不過這些都不失為一種手段,下麵就來對這次安全漏洞的處理做個總結。
公司的漏洞掃描分為兩個階段,第一個階段是用Fortify這個工具來掃描,檢查出漏洞,修複並出報告,第二個階段是用APPSCAN對線上代碼掃描,我們先來說說第一個階段Fortify工具掃描出來的漏洞如何處理,至於第二階段,後期做了再來補上。
1.SQL註入
這一類漏洞主要是針對一些SQL語句做動態拼接時,傳入一個特殊的非法字元,如 SELECT id,name FROM User WHERE deparment=1000 and {ConditionalExpression} 其中 {ConditionalExpression} 作為參數本想在查詢頁面做一些動態條件的拼接,這樣就會帶來SQL註入的風險,如果有人通過手段將 {ConditionalExpression} 參數的值 改成這樣呢 1=1 OR 2 >1 又或者 1=1 ; drop table deparment 呢這就是一個重大的安全事故了。
載SQL註入一般可以從這幾方面預防:
1.系統中連接資料庫的帳號分配合適的許可權,一般業務系統中資料庫操作帳號,不要分配對數庫結構產生改變的許可權如 CREATE TABLE ,DROP XXXX 等
2.對複雜的查詢使用存儲過程,預先定義好參數,在存儲過程中拼接SQL語句
3.儘量使用例如 SqlParmater 參數化傳值使之成為規範
4.對SQL語句或參數的值做特殊關鍵詞過濾
5.使用如MyBATIS,Hibernate ,等支持 SQL MAPPER 的 ORM框架
6.儘量避免SQL語句動態拼接 或用動態LINQ 替代
公司的項目大部分都用的MyBatis ORM框架做Mapper 映射,這次漏洞掃描 SQL註入方面還好,基本沒有在代碼中拼接SQL的,但有一點有個別幾處代碼 用的是ADO.NET 讀寫資料庫,其中在實例化Connection 對象的地方掃描出connectString 未做加密處理。後面改用項目中現有的資料庫操作類庫來操作就沒再報漏洞了。
2.Path Manipulation 路徑篡改
這次在安全漏洞篩查和處理過程中出現最多的就是 Path Manipulation 路徑篡改,手上幾個項目中其中就有兩個項目用到靜態頁面生成,涉及到大量的文件操作,如果不做處理會報很多漏洞。首先來說說我對Path Manipulation 漏洞的認識:通過代碼對系統上文件的操作如果不設置白名單,黑名單的過濾檢查,是一種安全隱患,比如某個公共方法中用到了,System.IO.File.Delete(path) .NET 提供的文件刪除這個方法,path是通過參數傳遞的,如果不做檢查,在一些調用的地方,被人改成了系統某個關鍵文件,可能直接系統崩潰,這就是一個天大的事情了。
對此類漏洞的修複措施一般做法如下:
1.設置白名單或黑名單
通常做法是設置白名單,危險不可枚舉,我們可以認為哪些是安全的,把它們列入允許操作的清單
2.設置文件夾安全許可權
只對允許操作的文件夾設置讀寫許可權。切不可將整個站點關件夾許可權設置可寫
處理Path Manipulation 路徑篡改 漏洞的.Net 示例代碼:
private static Dictionary<string,string> CreateFortifyDictionary() { Dictionary<string, string> fortifyDictionary = new Dictionary<string, string>(); for(char c1 = 'a'; c1 <= 'z'; c1++) { fortifyDictionary.Add(c1.ToString(), c1.ToString()); } for (char c2 = 'A'; c2 <= 'Z'; c2++) { fortifyDictionary.Add(c2.ToString(), c2.ToString()); } for (int c3 = 0; c3 < 10; c3++) { fortifyDictionary.Add(c3.ToString(), c3.ToString()); } fortifyDictionary.Add(".", "."); fortifyDictionary.Add(":", ":"); fortifyDictionary.Add("/", "/"); fortifyDictionary.Add(Separator, Separator); return fortifyDictionary; } public static string SecurityPathFilter(string path) { path = path.ToLower(); path = path.Replace("c:"+ Separator + "windows", ""); path = path.Replace("c:" + Separator + "program files", ""); path = path.Replace("c:" + Separator + "", ""); char[] characters = path.ToCharArray(); StringBuilder resultStringBuilder = new StringBuilder(); var dictionary = CreateFortifyDictionary(); foreach (Char character in characters) { string value = string.Empty; if (dictionary.TryGetValue(character.ToString(), out value)) { resultStringBuilder.Append(value); } } return resultStringBuilder.ToString(); }
3.Cross-site Scripting:Persistent 跨站腳本攻擊
引用 XSS 的定義:傳送到 Web 瀏覽器的惡意內容通常採用 JavaScript 代碼片段的形式,但也可能會包含一些 HTML、Flash 或者其他任意一種可以被瀏覽器執行的代碼。基於 XSS 的攻擊手段花樣百出,幾乎是無窮無盡的,但通常它們都會包含傳輸給攻擊者的私人數據(如 Cookie 或者其他會話信息)。在攻擊者的控制下,指引受害者進入惡意的網路內容;或者利用易受攻擊的站點,對用戶的機器進行其他惡意操作。大致意思是說 由於頁面在接收參數的過程中,沒有進行參數的校驗,可能存在 參數中存在可執行代碼的漏洞。
處理辦法一般是對參數進行轉義加碼 .NET中 引用System.Web.HttpUtility.DLL 程式集,調用下麵方法:
string str= System.Web.HttpUtility.HtmlEncode(html)
用到的地方需要 反轉義解碼:
string html= System.Web.HttpUtility.HtmlDecode(str);
4.System Information Leak 系統信息泄露
顧名思義就是系統的內部信息在泄漏了,給系統帶來安全隱患,什麼意思呢?下麵是摘抄的一段話:
當系統數據或調試信息通過套接字或網路連接使程式流向遠程機器時,就會發生外部信息泄露。外部信息泄露會暴露有關操作系統、完整路徑名、現有用戶名或配置文件位置的特定數據,從而使攻擊者有機可乘,它比內部信息(攻擊者更難訪問)泄露更嚴重
就是我們系統的調試信息和異常捕獲信息不能暴露出來,比如這段代碼Fortify 直接會檢測出漏洞。
try { // } catch(Exception ex) { System.Console.WriteLine(ex.Message); }
我們或可改成這樣來解決
try { // } catch(Exception ex) { //System.Console.WriteLine(ex.Message); logger.Logger("系統異常:"+ex.Message,ex); }
錯誤信息不應直接拋給終端,交由日誌去記錄。
引申一下在系統信息泄露這方面一般採用措施:
1.IIS上發佈站點時關閉Debug 遠程調試模式
2.定義錯誤頁面規範錯誤提示信息
3.自定義客戶端異常信息類,消化內部異常信息,記錄日誌,過濾處理後拋給客戶端允許可到的異常信息
參考如下示例:
<compilation debug="false" /> <customErrors defaultRedirect="GenericError.htm" mode="RemoteOnly" xdt:Transform="Replace"> <error statusCode="500" redirect="InternalError.htm"/> <error statusCode="403" redirect="NoAccess.htm" /> <error statusCode="404" redirect="FileNotFound.htm" /> </customErrors>
其中mode 說明如下:
1. On 表示在本地和遠程用戶都會看到自定義錯誤信息。
2. Off 禁用自定義錯誤信息,本地和遠程用戶都會看到詳細的錯誤信息。
3. RemoteOnly 表示本地用戶將看到詳細錯誤信息,而遠程用戶將會看到自定義錯誤信息
string message = ""; try { // } catch(WTSError ee) { // message = ee.OutMessage; logger.Logger("系統異常:" + ee.Message, ee); } catch(Exception ex) { message = "系統異常,錯誤類型未知"; //System.Console.WriteLine(ex.Message); logger.Logger("系統異常:"+ex.Message,ex); }
5. Non-Serializable Object Stored in Session 寫入Session 的對象不可被序列化
這次安全描過程中也報了很多這類漏洞,不清楚寫入Session 會話中的對象為什麼都要可被序列化,“在session中保存的對象最好是序列化,不然很容易導致類轉換的時候發生異常”這是我找到最簡短的回答,根據我的理解大概是,Session對象在存儲和讀取的時候會自動序列化和反序列化,對於非類似於 Int,String等非一般數據類型複雜的數據結構對象存儲和讀取屬性和狀態時數據轉換容易出錯。
對於這類問題的處理可以參考如下:
例如下麵這段會報漏洞的代碼:
Session["UserToken"] = "sessionObjectValue";
可以自定義一個Session存儲對象,標為可序例化,並實現ISerializable 介面
[Serializable] public class SessionObject : ISerializable { [OptionalField] private string data; public string Data { get { return data; } set { data = value; } } public SessionObject(string data) { this.data = data; } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Data", data); Type basetype = this.GetType().BaseType; MemberInfo[] mi = FormatterServices.GetSerializableMembers(basetype, context); for (int i = 0; i < mi.Length; i++) { //由於AddValue不能添加重名值,為了避免子類變數名與基類變數名相同,將基類序列化的變數名加上基類類名 info.AddValue(basetype.FullName + "+" + mi[i].Name, ((FieldInfo)mi[i]).GetValue(this)); } } }
然後在Session寫入和讀取時寫成這樣
1 //Session["UserToken"] = new SessionObject("sessionObjectValue"); 2 Session["UserToken"] = new SessionObject("sessionObjectValue"); 3 4 //string sessionObjectValue = Session["UserToken"].ToString(); 5 string sessionObjectValue = ((SessionObject)Session["UserToken"]).Data;
6.File Separator 文件分割符
不可避免描述中也出現了這些漏洞,一般在Window環境下,不會出現這類漏洞帶來的問題,如果你的系統可以要佈署在Linux或Unix 環境下就有可能出現Bug了,這類問題是指在不同操作系統環境下文件路徑的分割符號不一樣。如果這樣一個路徑:“C:\tmp\test.txt” Windows 環境是:“C:\tmp\test.txt ” Linux 環境是:"/tmp/test.txt" 為了兼顧不同操作系統環境建議使用 System.IO.Path.DirectorySeparatorChar 對象對於上面的路徑這樣組合:
"C:" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar + "test.txt"
類似的還有換行符:windows 下是 \r\n Linux 等其它系統下是 \n 在.NET 中使用 System.Environment.NewLine 對象。
7.Insecure Randomness 安全隨機數
引用網路上的解釋:
不安全的隨機數:電腦是一種具有確定性的機器,因此不可能產生真正的隨機性。偽隨機數生成器 (PRNG) 近似於隨機演算法,始於一個能計算後續數值的種子。
PRNG 包括兩種類型:統計學的 PRNG 和密碼學的 PRNG。統計學的 PRNG 可提供有用的統計資料,但其輸出結果很容易預測,因此數據流容易複製。若安全性取決於生成數值的不可預測性,則此類型不適用。密碼學的 PRNG 通過可產生較難預測的輸出結果來應對這一問題。為了使加密數值更為安全,必須使攻擊者根本無法、或極不可能將它與真實的隨機數加以區分。通常情況下,如果並未聲明 PRNG 演算法帶有加密保護,那麼它有可能就是一個統計學的 PRNG,不應在對安全性要求較高的環境中使用,其中隨著它的使用可能會導致嚴重的漏洞(如易於猜測的密碼、可預測的加密密鑰、會話劫持攻擊和 DNS 欺騙)。
就是我們一般使用的隨機函數並不是真正的隨機產生,具有一定的可推測性,可能帶來一些安全性問題,說實話此類問題不好處理涉及到密碼學。如果感興趣話可找找這方面的資料也可以先看看這篇文章 https://www.cnblogs.com/asxinyu/p/4301554.html,個人認為一般系統可以用GUID 的方案。
以上就是這次Fortify 安全掃描中遇到的幾種漏洞類型,通過上述的方法基本都已解決。記得其中還遇到一個獲取電腦域帳號調用C++ 類庫方法非安全代碼執行漏洞,還好找到另一種不用調用C++類庫的方法去替換,如果避免不了要調用C++類庫里的方法,如果報此類漏洞不知能有什麼好的辦法來處理,希望有這方面經驗的園友不吝告之。
本文參考和引用的地址如下:
1. https://www.cnblogs.com/eyesmoon/p/7421477.html Fortify掃描漏洞解決方案
2. https://blog.csdn.net/abcxy12336/article/details/52335490 針對Fortify工具掃描出幾大漏洞的解決辦法總結--1
3. https://www.cnblogs.com/asxinyu/p/4301554.html 開源Math.NET基礎數學類庫使用(14)C#生成安全的隨機數