序言
这个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 { IDictionarydic = new Dictionary (); 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业务层事务入侵
让你的代码不用每次都声明事物,你也不必担心你的事物是否提交,或者回滚啦吗?我做了一个示例仅供参考。
////// 插入可以成功 /// 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; } } ////// 因为appName字段多写成了appName1所以修改不会成功 /// 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{ ////// 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. /// 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(); } ////// 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. /// /// A System.Runtime.Remoting.Messaging.IMessage that contains a System.Collections.IDictionary // of information about the method call. // ///The message returned by the invoked method, containing the return value and // any out or ref parameters. // 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 ////// Dependency injection the interception into proxy class. /// /// The interception. public void InterceptionDI(IInterception interception) { _interception = interception; } #endregion }}
namespace TranAopRealProxy.Aop{ ////// Description of AOPProxyAttribute. /// [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; } }}
using System;namespace TranAopRealProxy.Aop{ ////// Description of IInterception. /// public interface IInterception { ////// Pre the method invoke. /// void PreInvoke(); ////// Post the method invoke. /// void PostInvoke(); ////// Handling the exception which occurs when the method is invoked. /// void ExceptionHandle(); }}
using System;using System.Collections.Generic;using System.Text;namespace TranAopRealProxy.Aop{ interface IProxyDI { void InterceptionDI(IInterception interception); }}
//// Authors:// Xiaoliang Pang (mailto:mv@live.cn)//// Copyright (c) 2010 Landpy Software//// http://www.cnblogs.com/pangxiaoliang//using System;using System.Collections.Generic;using System.Text;namespace TranAopRealProxy.Aop{ ////// Null Object pattern for interception. /// public class NullInterception : IInterception { #region IInterception Members ////// Before invoke the real instance to do something. /// public virtual void PreInvoke() { // Do nothing. } ////// End invoke the real instance to do something. /// public virtual void PostInvoke() { // Do nothing. } ////// Handling the exception which occurs when the method is invoked. /// public void ExceptionHandle() { // Do nothing. } #endregion }}
using System;using System.Collections.Generic;using System.Text;namespace TranAopRealProxy.Aop{ public class ProxyFactory { public static T CreateProxyInstance(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(); } }}
using System.Text;using System.Threading.Tasks;using TranAopRealProxy.Aop;using System.Transactions;namespace TranAopRealProxy.Aop{ ////// The interception of the AOP for trasaction. /// 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 { ////// 回滚 /// public void ActionFalseRollBack() { DoDB db = new DoDB(); db.UpdateTrue("abc"); int isSuccess= db.UpdateFalse("defg"); if (isSuccess <=0) { throw new Exception("苍天啊,大地啊,回滚吧。"); } } ////// 提交 /// public void ActionFalseRollBack1() { DoDB db = new DoDB(); db.UpdateTrue("abc"); db.UpdateTrue("abc234"); } }
总结
AOP在.net中还有体现,比如mvc的过滤器。也有很多第三方的插件供我们使用,比如:postsharp,castle.net等,大家可以了解学习下。如果你想把这里的透明代理与真实代理学透彻,也推荐阅读.Net本质论中的高级方法。同时也欢迎大家加入左上方群,我们一起探讨学习。