您的位置:首页 > 其它

构建更加安全的 Web 应用程序-一个新的保护框架可帮助您防止操作和数据篡改

2006-01-03 23:38 901 查看
Derek Fong , IT 架构师, IBM Canada

2005 年 12 月 29 日
Web 应用程序安全设计的目的是消除漏洞。仅仅基于用户凭证的已知安全设计元素(如验证和授权)可以很好地满足基本安全要求。不过,需要对收到的客户数据作进一步的审查,以便将安全边界从常用的设计元素扩展到应用程序代码。为了满足这一要求,我提供了一个新的安全设计框架,它保护了两类常见的漏洞:操作篡改和参数操纵(也称为 数据篡改)。
我将在表示层引入这个框架,负责对由浏览器发送的静态数据进行安全检查。这个框架还检测来自浏览器的操作事件,并为 Web 应用程序提供简单的浏览控制。
更多安全性的要求
考虑恶意用户用代理工具劫持由浏览器发送的数据这一情况。后果可能是严重的,因为服务器会处理规定之外的数据。下面的场景更详细地展示了 Web 应用程序的漏洞。
Web 页浏览
Web 页浏览控制属于应用程序的访问策略。访问策略必须指定特定用户角色可能的浏览路径,以防止恶意用户浏览他或她无权浏览的某一页(例如,通过书签)。
HTML 标记注入
与页浏览控制类似,当恶意用户试图在服务器将数据发回浏览器之前修改 HTML 内容时就会发生HTML 标记注入。HTML 标记篡改可能包含超链接、提交按钮或者其他表单标记。例如,如果一个 Web 页包含两个提交按钮,并根据特定条件只显示一个按钮,恶意用户可以使用代理工具在 Web 页显示在浏览器中之前在其中加上第二个按钮。这样用户就可以向他或她无权访问的内容提交操作。
参数操纵
参数操纵(即数据篡改)使恶意用户可以改变浏览器与 Web 应用服务器之间发送的数据。用户通常可以修改包含 URL 查询字符串和隐藏字段的参数。例如,如果 Web 页包含一个表示客户 ID 的隐藏字段,那么恶意用户就可以在浏览器将数据发送给服务器之前改变隐藏字段的值。
我在这里介绍的解决方案会处理 Web 应用程序漏洞,将对应用程序代码的影响降至最低,与应用程序代码松散耦合,而且可以根据将来的需求而扩展。

使用这个框架
可以在以下情况下使用这个保护框架:
Web 应用程序/Struts 框架要求简单的浏览控制。
Web 应用程序需要确认用户操作事件(如返回和刷新按钮)。
Web 应用程序要求保护页面静态数据中的漏洞,如操作、链接、按钮和隐藏字段。

模型结构
为了防止恶意用户劫持 Web 页中的 HTML 静态内容,这个框架使用了服务器端验证技术。这个框架的程序模型如 图 1 所示,它描述了技术设计。

图 1. 保护框架的类图



Processor
Processor
定义了保护建模为
ActionParams
的用户操作的操作。它也是处理客户请求的单元素对象。
Processor
ParamsRepository
注册用户操作,并生成一个引用代码。它通过引用代码验证通过 http 请求发送的用户操作(Post、Get 和 Put)。
ParamsRepository
ParamsRepository
维护对所有
ActionParams
对象的引用。它提供了对在 http 会话中获取和存储
ActionParams
的基本访问操作,并提供了一个生成引用代码的机制。
ActionParams
ActionParams
定义了对所有所支持的查询操作和静态数据通用的接口。它实现了一个
validate
操作以验证查询操作和静态数据。可以扩展
ActionParams
以支持应用程序特有的需求。
FormActionParams
FormActionParams
扩展了
ActionParams
以支持 HTML 表单操作和静态(隐藏)字段。它还覆盖了
validate
方法以支持表单操作验证。

框架对象的合作
本节描述客户机与框架交互以注册和验证用户操作的过程。
客户机注册要受到数据保护的静态内容
首先,客户机创建并向
Processor
传递
ActionParams
对象的一个实例。然后
Processor
ParamRepository
交互以存储
ActionParams
实例,并向客户机返回一个引用代码,如 图 2 所示。

图 2. 客户机注册要保护的数据



服务器验证来自客户机的数据
然后
Processor
将特定
ActionParams
的引用代码从客户机转发给
ParamRepository
Processor
ActionParams
实例交互以验证用户请求(请参阅 图 3)。

图 3. 服务器验证用户操作



实现思路
现在,我将讨论在实现这个框架时需要知道的设计细节。
代码惟一性
为每个
ActionParams
生成的引用代码必须在 Web 页中是惟一的,并且应当可以在不同的页面间重复。不过,如果要扩展这个框架以支持双击检测(请参阅 其他扩展),那么引用代码必须在应用程序中是惟一的。
代码生命周期
图 4 描绘了引用代码的生命周期。

图 4. 引用代码生命周期



来自浏览器的每一个 JSP 请求都会生成一组新的引用代码并将它们存储到 http 会话中。当用户向服务器提交请求时,一个引用代码会映射到会话中的
ActionParams
。当
Processor
完成了验证后,它会删除 http 会话中的所有代码。
异常处理
这个框架不会向调用者抛出任何异常。可以根据应用程序的需要处理返回的错误代码。一般来说,应用程序应当在发现任何攻击时,中止用户会话并返回到欢迎/登录页。
加密方式
ParamsRepository
用 http 会话存储受保护的信息。还可以加密受保护的数据并将它直接到存储到 Web 页中。向服务器提交数据时,也发送加密的数据。
Processor
将解密数据并验证它是否是真正的参数。这种方式会减小会话的大小,不过,它使性能降低很多。

场景分析
下面,我将描述需要保护 JSP 中静态内容的不同场景。然后展示这个保护框架如何在运行时防止漏洞。这些场景可以使您更好地理解这个框架。
场景 1:JavaServer Page scriptlet
为了定义所有超链接和表单隐藏字段的 JSP scriptlet,必须:
定义每一个
Processor
JSP scriptlet 的操作和参数为
ActionParams

为每一个超链接定义一个以
ActionParams
为参数的
Processor
JSP scriptlet,如 图 5 中的代码所示。
图 5. 保护超链接的 scriptlet



为隐藏字段定义一个以
ActionParams
为参数的
Processor
JSP scriptlet,如 图 6 中的代码所示。
图 6. 保护隐藏字段的 scriptlet



服务器装载 JSP 时,它执行这些 scriptlet 并调用
Processor
Processor
将受保护的数据存储到 http 会话中的
ParamsRepository
中。然后
Processor
向 Web 页返回引用代码并显示为超链接参数或者隐藏参数。引用代码见 图 7 (红色部分)。

图 7. 得到的 HTML



场景 2:在正常情况下验证用户操作
在这个场景中,用户单击 Web 页面中的 Submit,然后:
服务器上的处理程序(如 Struts 中的
RequestProcessor
或者
ActionServlet
)调用
Processor

然后
Processor
从 http 请求中提取引用代码并发送给
ParamsRepository
。由于用户单击了 Submit 按钮,所以浏览器向服务器发送引用代码
x2w3e
(请参阅 图 7)。
ParamsRepository
查找匹配的
ActionParam
实例并将它返回给
Processor
,如 图 8 所示。
图 8. 存储在 ParamsRepository 中的 ActionParam



然后
Processor
用收到的
ActionParams
验证用户提交的操作。这个场景的
ActionParams
实例是一个
FormActionParams
,并调用了
FormActionParams
validate
方法。由于发送给服务器的数据没有改变,因此
Processor
将返回成功的代码,如 图 9 所示。
图 9. 用 ParamsRepository 中的 ActionParams 验证 FormActionParams



场景 3:验证用户操作是否篡改数据
在这里,当用户单击 Web 页中的 Submit 时:
用户在浏览器将请求发送给服务器之前,将隐藏值从
12345
改为
XYZ

重复 场景 2 中的第 2 步到第 4 步。
Processor
查明用户请求中的参数与
ParamsRepository
FormActionParams
实例中的不一样。
Processor
返回错误代码。
图 10 所示,
ActionParams
包含一个无效的参数 ID
XYZ
Processor
将其与
ParamsRespository
中的有效参数 ID
12345
进行比较。

图 10. Processor 验证收到的 ActionParams



这种验证防止用操作/参数操纵和 HTML 标记注入进行篡改,因为对操作/参数的任何修改都不会通过验证。
场景 4:验证用户操作是否修改引用代码
在这种场景中,当用户单击 Web 页上的 Submit 时:
用户在浏览器将请求发送给服务器之前,将隐藏字段标记中的引用代码值从
x2w3e
修改为
hackValue

服务器端的处理程序(例如 Struts 中的
RequestProcessor
或者
ActionServlet
)会调用
Processor

Processor
从 http 请求中提取引用代码并发送给
ParamsRepository
。由于用户修改了值,所以浏览器向服务器发送的引用代码是
hackValue

ParamsRepository
无法找到匹配的
ActionParams
实例。
图 11 所示,
Processor
无法在
ParamsRepository
中找到收到的代码。
图 11. Processor 无法找到收到的引用代码



Processor
返回一个错误代码。
由于
ParamsRepository
规定了特定 Web 页上可以进行的操作,所以这个保护框架会拒绝发送给它的所有不能识别的操作。这防止了 Web 浏览劫持(如书签)。
场景 5:验证用户操作是否没有代码
最后一种情况,当用户单击 Web 页中的 Submit 时:
用户在浏览器向服务器发送请求之前,删除了引用代码的隐藏字段。
服务器端的处理程序(例如 Struts 中的
RequestProcessor
或者
ActionServlet
)调用
Processor

Processor
确定从请求中不能提取代码并返回一个错误代码。

示例代码
下面的代码展示了如何在 Java 中实现这个框架。
ParamsRepository
storeParams
方法将
ActionParams
存储到 http 会话里的一个映射中。代码参数标识特定的
ActionParams
实例。因为实现是与调用者隔离的,所以可以用其他类型的实现存储
ActionParams
,如 图 12 所示。

图 12. storeParams 方法实现



retrieveParams
方法根据引用代码返回一个
ActionParams
实例,如 图 13 所示。

图 13. retrieveParams 方法实现



ActionParams
validate
方法可以让
ActionParams
用另一个实例验证自身。它将操作和参数验证委派给不同的方法,如 图 14 所示。

图 14. validate 方法实现



FormActionParams
FormActionParams
覆盖了
doValidateParams
方法以验证表单中的静态隐藏字段(请参阅 图 15)。

图 15. doValidateParams 方法



Processor
Processor
调用
verifyAction
方法以验证用户操作,如果验证失败,它就返回错误代码。表 1 描述了会让
Processor
发出错误代码的可能场景。

表 1. Processor 对于以下场景生成错误代码
场景错误代码原因
检查 http 请求中是否缺少引用代码。CODEMISSING_ERR用户向服务器发送未知请求。
verifyWithRepository
方法检查
ParamsRepository
中是否有代码。
INVALIDCODE_ERR用户向服务器发送以前做过书签的请求。
检查用户是否篡改过 http 请求中的静态数据。DATATAMPERING_ERR用户修改了发送给服务器的数据。
verifyAction
方法可以用存储在信息库中的
ActionParams
验证用户操作(请参阅 图 16)。

图 16. 验证操作



Struts
可以容易地用 Struts 标记库集成这个保护框架,使这个框架的实现变为透明的。要在 Struts 中集成这个框架,必须继承以下 Struts 标记库。
表单和隐藏标记库
为了保护表单和隐藏字段,要修改 Struts 的
FormTag
HiddenTag
以调用
Processor
。创建一个子类,它:
扩展 Struts
HiddenTag
类,收集所有隐藏字段参数并将它们存储到
pageContext
属性中。
扩展 Struts
FormTag
类,覆盖
doAfterBody
方法以向
Processor
注册
pageContext
属性和表单操作。返回的引用代码输出作为隐藏字段(请参阅 图 17)。

图 17. FormTag 的 doAfterBody 方法



链接标记库
为了保护链接,可以修改 Struts
LinkTag
以调用
Processor
。 创建一个子类,它:
扩展 Struts
LinkTag
类,覆盖
calculateURL
方法以填充
ActionParams
对象并向
Processor
注册。返回的引用代码附加到 URL 的最后,如 图 18 所示。

图 18. calculateURL 方法



将保护框架集成到 Struts 后,只要在 JSP 中使用 Struts 标记库,就可以自动应用这个框架。

其他扩展
只要扩展
ActionParams
,这个框架就可以支持其他类型的保护。类似于
FormActionParams
,可以对
ActionParams
扩展下拉框保护,用一组有效的下拉值验证所选的值。通过维护以前处理的代码值,这个框架可以用同步的用户请求探测双击事件。如果收到具有相同代码值的请求,那么可以将以前的响应指定给这个请求。

结束语
本文描述了常见的 Web 应用程序攻击类型,并提供了一个解决这些问题的框架。这个框架覆盖了 Web 应用程序的逻辑安全方面,并保护了 Web 页中的静态数据。虽然这个框架可以减少受攻击的危险,但是设计者应当反复分析应用程序体系结构并找出所有可能的漏洞。这可让您增强或者修改这个框架以防止新的攻击形式。

参考资料
学习

您可以参阅本文在 developerWorks 全球站点上的 英文原文

Open Web Application Security Project (OWASP) :了解常见的 Web 应用程序漏洞。

WebGoat :分析这个为讲解 Web 应用程序安全性而设计的 J2EE 应用程序。

Michael Howard 和 David C. LeBlanc 所写的 Writing Secure Code, Second Edition :探讨安全对于互连的计算机(服务器、桌面个人计算机、手机、掌上设备等)的重要性。

Web seminar on Application and Data Security :了解当今商业面临的安全问题和挑战,包括计算机窃贼们希望您永远也不知道的解决方案。

Domino servers :避免数据库崩溃和安全问题。不要创建到文件服务器的映射目录链接或者共享的网络附属存储(Network Attached Storage,NAS)。

Linux security solutions (developerWorks,2002 年 1 月):仔细阅读大量关于 Linux 系统的安全性问题的文档。

developerWorks Java 技术专区 :寻找文章、教程和其他关于各种基于 Web 的解决方案的内容。

获得产品和技术

Struts @ ApacheCon :介绍 Struts 框架。

讨论

Cert.org :跟踪关于安全漏洞和策略的最新消息。

BugTraq :在这个公共邮件列表中讨论漏洞和安全隐患。

developerWorks blogs :参与 developerWorks 社区。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐