构建更加安全的 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
ParamsRepository
ActionParams
FormActionParams
框架对象的合作
本节描述客户机与框架交互以注册和验证用户操作的过程。
客户机注册要受到数据保护的静态内容
首先,客户机创建并向
图 2. 客户机注册要保护的数据
服务器验证来自客户机的数据
然后
图 3. 服务器验证用户操作
实现思路
现在,我将讨论在实现这个框架时需要知道的设计细节。
代码惟一性
为每个
代码生命周期
图 4 描绘了引用代码的生命周期。
图 4. 引用代码生命周期
来自浏览器的每一个 JSP 请求都会生成一组新的引用代码并将它们存储到 http 会话中。当用户向服务器提交请求时,一个引用代码会映射到会话中的
异常处理
这个框架不会向调用者抛出任何异常。可以根据应用程序的需要处理返回的错误代码。一般来说,应用程序应当在发现任何攻击时,中止用户会话并返回到欢迎/登录页。
加密方式
场景分析
下面,我将描述需要保护 JSP 中静态内容的不同场景。然后展示这个保护框架如何在运行时防止漏洞。这些场景可以使您更好地理解这个框架。
场景 1:JavaServer Page scriptlet
为了定义所有超链接和表单隐藏字段的 JSP scriptlet,必须:
定义每一个
为每一个超链接定义一个以
图 5. 保护超链接的 scriptlet
为隐藏字段定义一个以
图 6. 保护隐藏字段的 scriptlet
服务器装载 JSP 时,它执行这些 scriptlet 并调用
图 7. 得到的 HTML
场景 2:在正常情况下验证用户操作
在这个场景中,用户单击 Web 页面中的 Submit,然后:
服务器上的处理程序(如 Struts 中的
然后
图 8. 存储在 ParamsRepository 中的 ActionParam
然后
图 9. 用 ParamsRepository 中的 ActionParams 验证 FormActionParams
场景 3:验证用户操作是否篡改数据
在这里,当用户单击 Web 页中的 Submit 时:
用户在浏览器将请求发送给服务器之前,将隐藏值从
重复 场景 2 中的第 2 步到第 4 步。
如 图 10 所示,
图 10. Processor 验证收到的 ActionParams
这种验证防止用操作/参数操纵和 HTML 标记注入进行篡改,因为对操作/参数的任何修改都不会通过验证。
场景 4:验证用户操作是否修改引用代码
在这种场景中,当用户单击 Web 页上的 Submit 时:
用户在浏览器将请求发送给服务器之前,将隐藏字段标记中的引用代码值从
服务器端的处理程序(例如 Struts 中的
如 图 11 所示,
图 11. Processor 无法找到收到的引用代码
由于
场景 5:验证用户操作是否没有代码
最后一种情况,当用户单击 Web 页中的 Submit 时:
用户在浏览器向服务器发送请求之前,删除了引用代码的隐藏字段。
服务器端的处理程序(例如 Struts 中的
示例代码
下面的代码展示了如何在 Java 中实现这个框架。
ParamsRepository
图 12. storeParams 方法实现
图 13. retrieveParams 方法实现
ActionParams
图 14. validate 方法实现
FormActionParams
图 15. doValidateParams 方法
Processor
表 1. Processor 对于以下场景生成错误代码
图 16. 验证操作
Struts
可以容易地用 Struts 标记库集成这个保护框架,使这个框架的实现变为透明的。要在 Struts 中集成这个框架,必须继承以下 Struts 标记库。
表单和隐藏标记库
为了保护表单和隐藏字段,要修改 Struts 的
扩展 Struts
扩展 Struts
图 17. FormTag 的 doAfterBody 方法
链接标记库
为了保护链接,可以修改 Struts
扩展 Struts
图 18. calculateURL 方法
将保护框架集成到 Struts 后,只要在 JSP 中使用 Struts 标记库,就可以自动应用这个框架。
其他扩展
只要扩展
结束语
本文描述了常见的 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 社区。
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,必须:
定义每一个
ProcessorJSP scriptlet 的操作和参数为
ActionParams。
为每一个超链接定义一个以
ActionParams为参数的
ProcessorJSP scriptlet,如 图 5 中的代码所示。
图 5. 保护超链接的 scriptlet
为隐藏字段定义一个以
ActionParams为参数的
ProcessorJSP 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 社区。
相关文章推荐
- iframe标签引用显示为空,ie下 【此内容不能显示在一个框架中, 为了帮助保护在此网站中输入的信息的安全……】
- Java Web 中防止同时操作一个数据引起错误
- 构建更加安全的 Web 应用程序
- Java实现URI参数签名算法,确保应用与REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为
- Silverlight + DomainService 简易框架之三完成一个数据(非集合)查询操作
- Python web框架Django学习(1)——在win7 64bit下配置开发环境Django:一个可以使Web开发工作愉快并且高效的Web开发框架。 使用Django,使你能够以最小的代价构建和
- Java实现URI参数签名算法,确保应用与REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为
- 如何使用Microsoft .NET保护应用程序和数据的安全
- 基于MVC4+EasyUI的Web开发框架经验总结(3)- 使用Json实体类构建菜单数据
- 编写一个应用程序,除了主线程外,还有两个子线程。两个子线程对同一个数据操作,其中一个线程负责对该数据做递增操作,一个线程负责对该线程做递减操作。当这个数据小于0的话,递减操作等待,当这个数据大于100
- UIkit – 轻量级前端框架,帮助你快速构建 Web 界面
- 使用 Bluemix 和 AppScan Mobile Analyzer 服务构建一个 Android 应用程序安全测试工具
- 基于MVC+EasyUI的Web开发框架经验总结(3)- 使用Json实体类构建菜单数据
- 使用 ASP.NET MVC 和 Ext JS 构建以数据为中心的 Web 应用程序
- (转)基于MVC4+EasyUI的Web开发框架经验总结(3)- 使用Json实体类构建菜单数据
- CodeIgniter框架——创建一个简单的Web站点(include MySQL基本操作)
- Cocoa框架是iOS应用程序的基础,了解Cocoa框架,对开发iOS应用有很大的帮助。 1、Cocoa是什么? Cocoa是OS X和 iOS操作系统的程序的运行环境。 是什么因素使一个程序成为Co
- (总结)web安全 防止数据采集的几种方式
- web开发——4.一个完整的web项目和子应用程序的创建以及数据模型操作的两种方法
- 理解Web框架,和如何构建一个CSS框架