温水煮青蛙

对青蛙而言,这是一个相当残忍的比喻,虽然大多数引用这个比喻的人并不清楚青蛙在温水中是否真正不会跳出来而一直被煮成青蛙汤。
很久以前我写过一篇blog,表达了我对目前国内技术现状的一种思考:希望有更多的人能够不仅仅跟随,更能够提出震撼性的技术框架/技术特点出来。然而,那个时候许多人包括干脆放弃了希望安心的做起了传教士,安慰着自己也麻痹着别人。那个时候是J2EE的鼎盛时期,而就在那个时候一个26岁的小伙子,一个J2EE的newbie, 写了今天无人不知的Rails,以及诞生了Web2.0代表性公司:37Signals.
接触到一些优秀的程序员,并非我们想象的那么悠闲,只是更倾向于寻求合作、贡献、参与而不是独来独往、寻找借口、不加思考的索取;更不是对思考者的漠然旁观。
现在,我们眼睁睁的看着这个软件时代逐渐分开和细化:面向企业应用的J2EE+.NET等, 面向消费应用的Python, Rails等。事实已经证明,消极的跟随只能使自己的水温越来越高,唯有贡献与参与才能永远保持警醒。努力成为使水温变高的人,而永远不要成为水盆中的青蛙!

关于buffalo的安全问题

感谢董董以及各位用户提出这个问题。这个问题在以前的时候虽然留意过,但没想到会给实际的运行用户带来这么大的影响;现在看来,很有必要为buffalo加上安全控制的部分了。
考虑到一些实际运营的系统已经上线,以及buffalo升级新版本的速度无法满足现在的需要,现在提出一些解决方案供参考。由于时间关系,这些方案没有经过实际的代码验证,但在方向以及结构上没有问题,供急切的同志使用。
1 问题提出
当一个应用部署到www.domain-a.com的时候,buffalo的特定servlet被暴露出来,这个servlet只接受buffalo请求(xml格式)。我们知道xmlhttp无法跨域访问,但在IE浏览器中,如果本地存在访问这个远程buffalo服务的网页并运行,你会发现没有任何警告就能访问远程。这是相当危险的操作,因为很多时候buffalo不仅完成读取数据,还完成修改数据的操作。恶意用户如果知道服务方法的命名,很容易模拟请求发送,造成损害。
这个场景同样存在于将页面置于http://localhost/
的情况。IE会认为这是本地操作,可以访问远程,只是会弹出一个警告是否允许与远程数据交互。但这种情况不会出现在www.domain-b.com上——也就是说,两个远程域之间的交互是不可以的。
然而,在Firefox上——根据我的测试,无论是本地,还是localhost, 都无法与domain-a.com交互。他的安全限制更严格。
2 如何解决问题?
现在我们知道这个问题归根结底是xmlhttp的问题。几乎所有的带有java->javascript序列化的框架都存在这个问题,dwr,
json, buffalo都有。http://getahead.ltd.uk/dwr/security
这里讨论了dwr的安全问题,文档中很巧妙地将这个问题回避过去,但是这里
http://iremia.univ-reunion.fr/intranet/wiki/Wiki.jsp?page=DWRandAcegi
又提供了对应的解决方案。这个解决方案针对buffalo也同样有用。
问题解决的基本原理是根据cookie来进行校验(或者session,都可以)。我们知道,从file://a.html发送到http://domain-a.com的请求,如果此时我们获取request.getCookie,是无法获取的,这可以作为判断的依据;session也一样;他们都只对特定域名以及当前应用下的请求保留session或者cookie状态,来自其他地方的请求不包含这些状态,因此可以进行区分。区分开之后,就可以拒绝这些请求,要么抛异常,要么转到其他页面。
实现方式上,根据buffalo service的部署方式的不同,可以分为两种:
i) 写在properties文件中。我估计没有多少系统这样做因为创建服务实例效率低下。但如果这么做了,可行的办法就是改写代码,在代码中加入访问来源地判断(request.getCookie).
这是简单的做法,如果代码不多可以这么完成。一些静态植入的AOP方案可以更优雅的解决这个问题,如AspectJ, AspectWerkz等
ii) 写在Spring配置文件中。这种方式下比较容易。步骤如下:
– 将原来的service移入到ProxyFactoryBean中
– 在ProxyFactoryBean中添加一个拦截器
– 拦截器完成了方法调用的拦截
– 其他代码不变
例如:原来有一个UserService:

现在改为

添加一个ProxyFactoryBean并设置拦截器:

myBuffaloSecurityIntereptor:
class MyBuffaloSecurityInterceptor implements MethodInterceptor
Object invoke(MethodInvocation i) throws Throwable {
HttpServletRequest request = context.getRequest();
[...]