不妨设想一个恶意黑客能够访问贵公司所有客户的账户细节,或者使用别人的信用卡在线购物,而这一切只需改变URL中的几个数字。这听起来似乎不太可能,但是如果你的Web应用程序容易遭受不安全的直接对象引用的危害,恶意黑客要达到这个目的简直易如反掌。
不安全的直接对象引用举例
这里的“对象”是指文件、目录、数据库记录等内部实施的对象,在应用程序将URL(或表单参数)中的一个引用暴露给这些对象之一时,就会发生安全问题。这是因为黑客可以修改这些直接对象引用,例如,黑客可以在一个URL被提交之前进行参数修改,企图访问一个不同的、未获得授权的文件、目录,或数据库中的条目。如果不加强其它的授权检查,这种企图就会成功。
假设有一个Web应用程序最终会生成下面这个URL:
http://www.yourinsecurewebapp.com/yourgetfile.cfm?filename=yoursometextfile.txt
这里有一个非常明显的对yoursometextfile.txt文件的直接对象引用。它对黑客的诱惑在于,看到如果将这个文件名换成另外一个文件名(如“yourpasswords.txt”或“youraccounts.txt”)会发生什么。
要取得这种成功,黑客必须正确地猜测出系统上另外一个文件名,但一个更合理的方法,是寻找系统上其它位置的特定内容,其使用的方法就是目录遍历攻击(目录遍历是Http的一个安全漏洞,它使得攻击者能够访问受限制的目录,并能够在Web服务器的根目录以外执行命令。)。从本质上讲,这意味着访问一个完全不同的目录,或者存在漏洞的应用程序的开发者所构建的任何方面。为访问Apache Tomcat文件名和口令,黑客可能将URL的最后一部分改成:
- ?filename=../../tomcat/conf/tomcat-users.xml
并非所有的直接对象引用都提供对文件的访问。还有另外一种可能激发黑客兴趣的URL,其结尾格式如下:
- ...account.cfm?customerid=4567
这会使黑客进一步问,“如果我将客户ID(customerid)换成4568会发生什么?”
与此类似,如果一个Web应用程序允许一个用户根据数据库的关键字引用从存储在数据库中的一个或多个信用卡中的一个,那么黑客修改此数据库的关键字时,会发生什么呢?
- <select name="choosecreditcard">
- <option value="56">
- XXXXXXXXXXXX6902
- </option>
- <option value="88">
- XXXXXXXXXXXX5586
- </option>
- </select>
在这里,用户可以从两个分别以6902和5586为结尾的卡中选择一个,该卡号由数据库的关键字引用,而应用程序可以访问此数据库文件。因此,黑客可以将56或88改为另一个数字,如78,用来引用属于另外一个用户的卡号。如果没有其它的认证检查来防止这种引用,攻击将获得成功。
避免不安全的直接对象引用
避免不安全的直接对象引用(DOR)漏洞的最佳方法是,完全不要暴露私密的对象引用,但如果非用不可,非常重要的一点是确保在向任何用户提供访问之前对其进行认证和审查。全球顶级的Web应用安全机构OWASP建议企业建立一种引用应用程序对象的标准方法,现简述如下:
1、尽可能避免将私密的对象引用暴露给用户,如重要的关键字或文件名。
2、运用一种“可接受的良好方法”,详细地验证任何私密的对象引用。决定准许用户访问哪些文件,并仅授与这些用户访问这些文件的权力。
3、对所有引用的对象都要进行验证。
OWASP还提供了第三个要点的一个例子。在此,黑客可以将电子商务网站的购物车ID参数改为任何值:
- int cartID = Integer.parseInt( request.getParameter( "cartID" ) );
- String query = "SELECT * FROM table WHERE cartID=" + cartID;
要想防止受到这种攻击,就只能允许获得授权的记录可以显示:
- int cartID = Integer.parseInt( request.getParameter( "cartID" ) );
- User user = (User)request.getSession().getAttribute( "user" );
- String query = "SELECT * FROM table WHERE
- cartID=" + cartID + "AND userID=" + user.getID();
直接对象引用的另外一种选择是每个用户或会话都使用非直接的对象引用。
在前面那个关于信用卡的例子中,用户需要从两个卡中选择一个信用卡,这会暴露对信用卡数据库的直接引用。一个更好的方法是将这两个信用卡的记录存储到一个针对此用户的特定阵列中。关于信用卡的选择,其代码类似于下面的内容:
- <select name=" choosecreditcard">
- <option value="1">
- XXXXXXXXXXXX6902
- </option>
- <option value="2">
- XXXXXXXXXXXX5586
- </option>
- </select>
在这种方法中,仅有对此用户阵列的一个直接引用,它仅包含此用户的数据。将选项的值改为大于2的任何值不会导致其他用户的信用卡细节被利用。然后,应用程序将把用户的特定的非直接对象引用(选项值为1或2)映射回底层的数据库关键字(前面例子中的56和88)
对不安全的直接对象引用的测试
不幸的是,漏洞扫描器在发现不安全的直接对象引用漏洞方面并不是很高效,所以最佳的选择是:
1、仔细检查代码,确认是否有重要的参数易于遭到利用和操纵。
2、经常实施专业的渗透测试。