序言 這個AOP要從我們公司的一個事故說起,前段時間公司的系統突然在烏雲中出現,數據被泄露的一覽無餘,烏雲上顯示是SQL註入攻擊。呵,多麼貼近生活的一個露洞,可謂是人盡皆知啊。然而卻華麗麗的給拉我們一記耳光。 那麼問題既然來啦,我們.net組有40-50個項目之多吧,怎麼去一一補救這一過失呢?什麼又 ...
序言
這個AOP要從我們公司的一個事故說起,前段時間公司的系統突然在烏雲中出現,數據被泄露的一覽無餘,烏雲上顯示是SQL註入攻擊。呵,多麼貼近生活的一個露洞,可謂是人盡皆知啊。然而卻華麗麗的給拉我們一記耳光。
那麼問題既然來啦,我們.net組有40-50個項目之多吧,怎麼去一一補救這一過失呢?什麼又是SQL註入呢?再有就是常見的Web漏洞有哪些呢?我們怎麼更好的監控我們的系統呢?
那麼大家不要嫌我啰嗦,本篇我將對常見的網站攻擊與防禦,與.Net中AOP實現橫切關註點做一些介紹與演示。
常見的web攻擊與防禦
據數據統計:全球70%的web應用攻擊都是來自XSS攻擊和SQL註入攻擊。此外還有常見的跨站攻擊CSRF,Session劫持,文件上傳等手段。
XSS攻擊
XSS攻擊即跨站點腳本攻擊(Cross Site Script),看下全稱縮寫,本應該是CSS無奈,已被樣式表占用,只能用個更牛逼的XXX代替,XSS,哈哈,蛋扯完啦,說下什麼是XSS,他是攻擊者在網頁中嵌入惡意程式腳本,當用戶打開網頁時,腳本程式便開始執行,竊取客戶端cookie,用戶名,密碼,下載執行病毒木馬程式等等,牛逼的一塌糊塗,好像你的網站系統成啦他自己的一樣。
那麼怎麼註入的呢?舉個例子啊,假如發表個說說,或者微博,發表的內容是 "/><script>alert('123');</script><!- ,那麼在某種情況下你可能會讓他載入到你的 <input type="text" value="" />中,你再看下你的代碼成什麼樣子啦,就會運行alert();我這裡僅僅是一個alert();黑客,就黑的不著邊際的黑你啊。
XSS防範:
1、將用戶輸入的特殊符號如:<,>,'',""轉義為<,>,&,"等。
2、對Cookie添加HttpOnly屬性,他不能對抗XSS攻擊,但可以防止竊取cookie。
CSRF攻擊
CSRF攻擊即跨站請求攻擊(cross site request forgery)。攻擊者通過跨站請求,以合法用戶的身份進行非法操作,如轉賬,發表評語等。具體流程如下:
舉例說明:假設你在中國銀行網站進行轉賬業務,首先你登陸啦中國銀行,進行啦轉賬,這是假設你的轉賬連接為http:www.zhongguoyinhang/zz/1000.那麼你轉完帳後並沒有關閉頁面。而是訪問啦另外一個網站,另外一個網站的一個圖片或者連接為攻擊者布好的連接:http:www.zhongguoyinhang/zz/1000 。那麼很不幸,你又一次進行啦轉賬。
當然,中國銀行會有各種舉措。不過這確實是攻擊者的一種手段。
CSRF防範:
1、對Cookie添加HttpOnly屬性。
2、增加token驗證,驗證碼驗證,表單token等。
3、通過Referer識別,來源網站。
SQL註入攻擊
SQL註入相信每個開發者都耳熟能詳啦。不多說,就是通過sql拼接,讓你的sql執行別人想要執行的語句。甚至可惡的update,delete,等等等等!!
SQL註入防範:1、使用orm框架。2、使用預編譯語句。3、避免明文存放密碼。4、處理好響應的異常,因為異常中會包含關於伺服器版本,資料庫版本,編程語言甚至資料庫連接地址,用戶名密碼等信息。
文件上傳漏洞
文件上傳也好理解:就是攻擊者上傳啦惡意可執行文件或者腳本,並通過腳本獲取伺服器響應的權利,多可怕,如果他上傳個格式化你伺服器硬碟的程式,可想而知。怕不怕!!
防範:1、驗證尾碼名。2、驗證魔數。魔數:很多類型的文件,其實的幾個位元組內容是固定,因此根據這幾個位元組就能確認文件的類型。這幾個位元組也稱為魔數。3、部署獨立的文件伺服器。
其實攻擊手段還有很多,DDOS,CC,DNS功能變數名稱劫持,cdn回源攻擊等等,大家可以在網上搜搜查查,瞭解一下。
AOP解決40-50個項目中的sql註入漏洞
這裡的aop其實是.Net中的透明代理與真是代理的實現。他讓一般開發者不用關心某個橫切點,比如介面性能的的記錄,日誌記錄,過濾危險字元,事物提交回滾等等。
首先我們的項目有的用的orm框架,有的沒有用,有的用的參數化拼接,有的直接字元串拼接。那麼我們這麼多項目怎麼一下子進行清理盤查呢。我們想拉個辦法就是我們的項目都有業務邏輯層去連接數據讀寫層的。那麼我們只要把業務邏輯層這一層的方法參數給過濾一下危險字元,不就可以拉。那麼工程開始啦。
namespace A.Helper.Client.Action { public class SqlVerifyProxy :RealProxy { MarshalByRefObject _target = null; public SqlVerifyProxy(Type type, MarshalByRefObject target) : base(type) { this._target = target; } //覆寫Invoke,處理RealProxy截獲的各種消息, //此種方式最簡捷,但不能截獲遠程對象的激活,好在我們並不是真的要Remoting public override IMessage Invoke(IMessage msg) { IMethodCallMessage call = (IMethodCallMessage)msg; IConstructionCallMessage ctr = call as IConstructionCallMessage; IMethodReturnMessage back = null; //構造函數,只有ContextBoundObject(Inherit from MarshalByRefObject)對象才能截獲構造函數 if (ctr != null) { RealProxy defaultProxy = RemotingServices.GetRealProxy(_target); //如果不做下麵這一步,_target還是一個沒有直正實例化被代理對象的透明代理, //這樣的話,會導致沒有直正構建對象。 defaultProxy.InitializeServerObject(ctr); //本類是一個RealProxy,它可通過GetTransparentProxy函數得到透明代理 back = EnterpriseServicesHelper.CreateConstructionReturnMessage(ctr, (MarshalByRefObject)GetTransparentProxy()); } //MarshalByRefObject對象就可截獲普通的調用消息, //MarshalByRefObject對象告訴編譯器,不能將其內部簡單的成員函數優化成內聯代碼, //這樣才能保證函數調用都能截獲。 else { IDictionary<string, object> dic = new Dictionary<string, object>(); dic = actionContext.ActionArguments; if (dic != null && dic.Count > 0) { foreach (var m in dic) { string o = m.Value.ToJson(); Utils.Filter(o); } } back = RemotingServices.ExecuteMessage(_target, call); } return back; } } }
namespace A.Helper.Client.Action { //從ProxyAttribute繼承,自動實現RealProxy植入 [AttributeUsage(AttributeTargets.Class)] class SqlVerifyProxyAttribute : ProxyAttribute { //覆寫CreateInstance函數,返回我們自建的代理 public override MarshalByRefObject CreateInstance(Type serverType) { MarshalByRefObject obj = base.CreateInstance(serverType); SqlVerifyProxy proxy = new SqlVerifyProxy(serverType, obj); return (MarshalByRefObject)proxy.GetTransparentProxy(); } } }
好啦,就這麼簡單,只要業務邏輯層的基類,集成ContextBoundObject添加我們的[SqlVerifyProxy]屬性就好啦,這個作為一個dll給其他項目引入,別人不用一個一個再寫一遍。不過這也是我們的權宜之計,相信我們以後的項目會有更好的架構設計,來防範諸如此類低級問題的發生。
RealProxy實現AOP業務層事務入侵
讓你的代碼不用每次都聲明事物,你也不必擔心你的事物是否提交,或者回滾啦嗎?我做了一個示例僅供參考。
/// <summary> /// 插入可以成功 /// </summary> public Int32 UpdateTrue(string appName) { try { using (var conn = GetInstance()) { string sql = "update aoptran set appName='"+appName+"' where id <10 "; var result = conn.ExecuteScalar(sql); return result != null ? Convert.ToInt32(result) : 0; } } catch (Exception ex) { return 0; } } /// <summary> /// 因為appName欄位多寫成了appName1所以修改不會成功 /// </summary> public Int32 UpdateFalse(string appName) { try { using (var conn = GetInstance()) { string sql = "update aoptran set appName1='" + appName + "' where id <10 "; var result = conn.ExecuteScalar(sql); return result != null ? Convert.ToInt32(result) : 0; } } catch (Exception ex) { return 0; } }
namespace TranAopRealProxy.Aop { /// <summary> /// RealProxy is a abstract class, which is a class in Framework to provide the function about base proxy. /// The Invoke method like the hook of MFC, it intercept the message, inject the custom logic and generate a new message /// for system to performance. /// </summary> class AOPRealProxy : RealProxy, IProxyDI { private MarshalByRefObject _target = null; private IInterception _interception = null; public AOPRealProxy(Type targetType, MarshalByRefObject target) : base(targetType) { _target = target; _interception = new NullInterception(); } /// <summary> /// Overridden the method "Invoke" of the base class, invokes the method that is specified // in the provided System.Runtime.Remoting.Messaging.IMessage on the remote // object that is represented by the current instance. /// </summary> /// <param name="msg">A System.Runtime.Remoting.Messaging.IMessage that contains a System.Collections.IDictionary // of information about the method call. // </param> /// <returns>The message returned by the invoked method, containing the return value and // any out or ref parameters. // </returns> public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg) { IMethodReturnMessage methodReturnMessage = null; IMethodCallMessage methodCallMessage = msg as IMethodCallMessage;//Check whether the message is method call message. if (methodCallMessage != null) { IConstructionCallMessage constructionCallMessage = methodCallMessage as IConstructionCallMessage; if (constructionCallMessage != null) { RealProxy defaultProxy = RemotingServices.GetRealProxy(_target); defaultProxy.InitializeServerObject(constructionCallMessage); methodReturnMessage = EnterpriseServicesHelper.CreateConstructionReturnMessage(constructionCallMessage, (MarshalByRefObject)GetTransparentProxy()); } else { _interception.PreInvoke(); try { methodReturnMessage = RemotingServices.ExecuteMessage(_target, methodCallMessage); } catch { } if (methodReturnMessage.Exception != null) { _interception.ExceptionHandle(); } else { _interception.PostInvoke(); } } } return methodReturnMessage; } #region IProxyDI Members /// <summary> /// Dependency injection the interception into proxy class. /// </summary> /// <param name="interception">The interception.</param> public void InterceptionDI(IInterception interception) { _interception = interception; } #endregion } }View Code
namespace TranAopRealProxy.Aop { /// <summary> /// Description of AOPProxyAttribute. /// </summary> [AttributeUsage(AttributeTargets.Class)] public class AOPProxyAttribute : ProxyAttribute { private IInterception _interception; public Type Interception { get { return _interception.GetType(); } set { IInterception interception = Activator.CreateInstance(value) as IInterception; _interception = interception; } } public AOPProxyAttribute() { _interception = new NullInterception(); } public override MarshalByRefObject CreateInstance(Type serverType) { MarshalByRefObject target = base.CreateInstance(serverType); AOPRealProxy aopRealProxy = new AOPRealProxy(serverType, target); aopRealProxy.InterceptionDI(_interception); return aopRealProxy.GetTransparentProxy() as MarshalByRefObject; } } }View Code
using System; namespace TranAopRealProxy.Aop { /// <summary> /// Description of IInterception. /// </summary> public interface IInterception { /// <summary> /// Pre the method invoke. /// </summary> void PreInvoke(); /// <summary> /// Post the method invoke. /// </summary> void PostInvoke(); /// <summary> /// Handling the exception which occurs when the method is invoked. /// </summary> void ExceptionHandle(); } }View Code
using System; using System.Collections.Generic; using System.Text; namespace TranAopRealProxy.Aop { interface IProxyDI { void InterceptionDI(IInterception interception); } }View Code
// // Authors: // Xiaoliang Pang (mailto:[email protected]) // // Copyright (c) 2010 Landpy Software // // http://www.cnblogs.com/pangxiaoliang // using System; using System.Collections.Generic; using System.Text; namespace TranAopRealProxy.Aop { /// <summary> /// Null Object pattern for interception. /// </summary> public class NullInterception : IInterception { #region IInterception Members /// <summary> /// Before invoke the real instance to do something. /// </summary> public virtual void PreInvoke() { // Do nothing. } /// <summary> /// End invoke the real instance to do something. /// </summary> public virtual void PostInvoke() { // Do nothing. } /// <summary> /// Handling the exception which occurs when the method is invoked. /// </summary> public void ExceptionHandle() { // Do nothing. } #endregion } }View Code
using System; using System.Collections.Generic; using System.Text; namespace TranAopRealProxy.Aop { public class ProxyFactory { public static T CreateProxyInstance<T>(IInterception interception) where T : new() { Type serverType = typeof(T); MarshalByRefObject target = Activator.CreateInstance(serverType) as MarshalByRefObject; AOPRealProxy aopRealProxy = new AOPRealProxy(serverType, target); aopRealProxy.InterceptionDI(interception); return (T)aopRealProxy.GetTransparentProxy(); } } }View Code
using System.Text; using System.Threading.Tasks; using TranAopRealProxy.Aop; using System.Transactions; namespace TranAopRealProxy.Aop { /// <summary> /// The interception of the AOP for trasaction. /// </summary> class Transaction : IInterception { #region IInterception Members TransactionScope tran = null; public void ExceptionHandle() { tran.Dispose(); } public void PostInvoke() { tran.Complete(); tran.Dispose(); } public void PreInvoke() { tran = new TransactionScope(); } #endregion } }
public class Logic : BaseTran { /// <summary> /// 回滾 /// </summary> public void ActionFalseRollBack() { DoDB db = new DoDB(); db.UpdateTrue("abc"); int isSuccess= db.UpdateFalse("defg"); if (isSuccess <=0) { throw new Exception("蒼天啊,大地啊,回滾吧。"); } } /// <summary> /// 提交 /// </summary> public void ActionFalseRollBack1() { DoDB db = new DoDB(); db.UpdateTrue("abc"); db.UpdateTrue("abc234"); } }
總結
AOP在.net中還有體現,比如mvc的過濾器。也有很多第三方的插件供我們使用,比如:postsharp,castle.net等,大家可以瞭解學習下。如果你想把這裡的透明代理與真實代理學透徹,也推薦閱讀.Net本質論中的高級方法。同時也歡迎大家加入左上方群,我們一起探討學習。