继续对Fortify的漏洞进行总结,本篇主要针对 Dynamic Code Evaluation: Code Injection(动态脚本注入) 和 Password Management: Hardcoded Password(密码硬编码) 的漏洞进行总结,如下:
1.1、产生原因:
许多现代编程语言都允许动态解析源代码指令。这使得程序员可以执行基于用户输入的动态指令。当程序员错误地认为由用户直接提供的指令仅会执行一些无害的操作时(如对当前的用户对象进行简单的计算或修改用户的状态),就会出现 code injection 漏洞。然而,若不经过适当的验证,用户指定的操作可能并不是程序员最初所期望的。
示例:在这个经典的 code injection 实例中,应用程序可以实施一个基本的计算器,该计算器允许用户指定执行命令。
...
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine
scriptEngine = scriptEngineManager.getEngineByExtension("js");
userOps = request.getParameter("operation");
Object result = scriptEngine.eval(userOps);
...
如果 operation 参数的值为良性值,程序就可以正常运行。例如,当该值为 "8 + 7 * 2" 时,result 变量被赋予的值将为 22。然而,如果攻击者指定的语言操作既有可能是有效的,又有可能是恶意的,那么,只有在对主进程具有完全权限的情况下才能执行这些操作。如果底层语言提供了访问系统资源的途径或允许执行系统命令,这种攻击甚至会更加危险。例如,JavaScript 允许调用 Java 对象;如果攻击者计划将
"
java.lang.Runtime.getRuntime().exec("shutdown -h now")" 指定为 operation 的值,则主机系统就会执行关机命令。
1.2、修复方案:
在任何时候,都应尽可能地避免解析动态代码。如果程序必须动态地解释代码,您可以通过以下方式将这种攻击获得成功的可能性降到最低:尽可能限制程序中动态执行的代码数量,将代码限制到基本编程语言的程序特定的和上下文特定的子集。程序绝对不能直接解析和执行未经验证的用户输入。而应采用间接方法:创建一份合法操作和数据对象列表,用户可以指定其中的内容,并且只能从中进行选择。利用这种方法,就绝不会直接执行由用户提供的输入。
图1.2.1:对js引擎方法执行的源代码指令添加白名单过滤
2、Password
Management: Hardcoded Password(密码硬编码)
2.1、产生原因:
使用硬编码方式处理密码绝非好方法。这不仅是因为所有项目开发人员都可以使用通过硬编码方式处理的密码,而且还会使解决这一问题变得极其困难。一旦代码投入使用,除非对软件进行修补,否则您再也不能改变密码了。如果帐户中的密码保护减弱,系统所有者将*在安全性和可行性之间做出选择。
例 1:以下代码用 hardcoded password
来连接数据库:...
DriverManager.getConnection(url, "scott", "tiger");
...
该代码可以正常运行,但是任何有该代码权限的人都能得到这个密码。一旦程序发布,将无法更改数据库用户“scott”和密码“tiger”,除非是要修补该程序。雇员可以利用手中掌握的信息访问权限入侵系统。更糟的是,如果攻击者能够访问应用程序的字节代码,那么他们就可以利用 javap -c 命令访问已经过反汇编的代码,而在这些代码中恰恰包含着用户使用过的密码值。我们可以从以下看到上述例子的执行结果:
javap -c ConnMngr.class
22: ldc #36; //String jdbc:mysql://ixne.com/rxsql
24: ldc #38; //String scott
26: ldc #17; //String tiger
在移动世界中,由于设备丢失的几率较高,因此密码管理是一个非常棘手的问题。
图2.1.1:使用硬编码的方式设置密码
2.2、修复方案:
绝不能对密码进行硬编码。通常情况下,应对密码加以模糊化,并在外部资源文件中进行管理。在系统中采用明文的形式存储密码,会造成任何有充分权限的人读取和无意中误用密码。至少,密码要先经过 hash 处理再存储。
图2.2.1:获取配置文件配置好的密码
图2.2.2:配置文件配置好且加密的密码