在现代 Web 开发中,使用 MVC 架构是一种流行的做法。MVC 是 Model-View-Controller 的缩写,它将 Web 应用分为三层,View 层负责用户视图、页面展示等工作;Controller 负责应 用的逻辑实现,接收 View 层传入的用户请求,并转发给对应的 Model 做处理;Model 层则负 责实现模型,完成数据的处理。
从数据的流入来看,用户提交的数据先后流经了 View 层、Controller、Model 层,数据的 流出则反过来。在设计安全方案时,要牢牢把握住数据这个关键因素。在 MVC 框架中,通过 切片、过滤器等方式,往往能对数据进行全局处理,这为设计安全方案提供了极大的便利。
比如在 Spring Security 中,通过 URL pattern 实现的访问控制,需要由框架来处理所有用户 请求,在 Spring Security 获取了 URL handler 基础上,才有可能将后续的安全检查落实。在 Spring Security 的配置中,第一步就是在 web.xml 文件中增加一个 filter,接管用户数据。
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
然而数据的处理是复杂的,数据经过不同的应用逻辑处理后,其内容可能会发生改变。比 如数据经过 toLowercase,会把大写变成小写;而一些编码解码,则可能会把 GBK 变成 Unicode 码。这些处理都会改变数据的内容,因此在设计安全方案时,要考虑到数据可能的变化,认真 斟酌安全检查插入的时机。
举例来说,在“注入攻击”一章中,我们并没有使用 PHP 的 magic_quotes_gpc 作为一项 对抗 SQL 注入的防御方案,这是因为 magic_quotes_gpc 是有缺陷的,它并没有在正确的地方 解决问题。magic_quotes_gpc 实际上是调用了一次 addslashes(),将一些特殊符号(比如单引号) 进行转义,变成了 \’ 。 对应到 MVC 架构里,它是在 View 层做这件事情的,而 SQL 注入是 Model 层需要解决的 问题,结果如何呢?黑客们找到了多种绕过 magic_quotes_gpc 的办法,比如使用 GBK 编码、 使用无单引号的注入等。 PHP官方在若干年后终于开始正视这个问题,于是在官方文档1 的描述中不再推荐大家使用 它:
所以 Model 层的事情搞到 View 层去解决,效果只会适得其反。
一般来说,我们需要先想清楚要解决什么问题,深入理解这些问题后,再在“正确”的地 方对数据进行安全检查。一些主要的 Web 安全威胁,如 XSS、CSRF、SQL 注入、访问控制、 认证、URL 跳转等不涉及业务逻辑的安全问题,都可以集中放在 MVC 框架中解决。
在框架中实施安全方案,比由程序员在业务中修复一个个具体的 bug,有着更多的优势。
首先,有些安全问题可以在框架中统一解决,能够大大节省程序员的工作量,节约人力成 本。当代码的规模大到一定程度时,在业务的压力下,专门花时间去一个个修补漏洞几乎成为 不可能完成的任务。
其次,对于一些常见的漏洞来说,由程序员一个个修补可能会出现遗漏,而在框架中统一 解决,有可能解决“遗漏”的问题。这需要制定相关的代码规范和工具配合。 最后,在每个业务里修补安全漏洞,补丁的标准难以统一,而在框架中集中实施的安全方 案,可以使所有基于框架开发的业务都能受益,从安全方案的有效性来说,更容易把握