计算机应用研究
APPLICATION RESEARCH OF COMPUTERS
2000 Vol.17 No.3 P.33-35




网上点击付费业务中的安全问题和解决方案
洪帆　杨洋
摘　要：介绍了一种新型的网上点击付费业务并分析了其中的安全问题，主要针对于如何监控业务提供方的行为提出了两种简单实用的解决方案，最后利用数字签名和哈希链技术对其进行了改进。
关键词：点击付费 超链接 JavaScript 数字签名 哈希链
1　简介
　　在Internet互联网日益普及的今天，越来越多的公司利用WWW主页在网上对自己的产品及服务进行宣传。为了增加公司主页的访问人数，许多公司以付费或交换等方式将自己的主页广告及超链接显示在其它站点的主页上。如今，网上又出现了一种新型的主页广告方式-点击付费业务(Click-Through Payment)。简单地说，Web站点A的管理员可以到提供点击付费业务的公司去申请一个帐号，然后他将由该公司所提供的主页广告和超链接放到他自己的主页当中。以后，只要有人通过站点A主页上的超链接跳转到该公司的主页上时，公司就付给站点A一定的报酬(一般是几分钱)。通过这种方式，公司就可以达到吸引其它Web站点为其主页进行宣传的目的。
　　这种网上的点击付费业务的内部运行机制非常简单，完全基于HTTP协议。见图1。

　　某用户U正在访问站点A(业务申请方)的主页(http://siteA/pageA.html)。当他点击该主页上站点B(业务提供方)的超链接(http://siteB/pageB.html)时，用户的浏览器会向站点B的Web服务器发出一个HTTP客户端请求，请求传输pageB.html页。服务器响应客户端的请求把pageB.html页发往用户U的浏览器。这与用户点击其它的Web超链接没有什么两样。而这一业务的关键之处在于，在客户端请求报文中有一个Referrer域，该域将被浏览器自动置为用户当前网页的URL地址(http://siteA/pageA.html)。站点B的Web服务器正好利用了这个Referrer域来进行判断，如果它指向某一业务申请方的主页，则给该业务申请方的帐号中增加一次计数(计数与报酬成正比)。
2　安全问题
　　众所周知，在Internet互联网上凡涉及到经济利益关系的业务，其安全性(广义的)是不容忽视的。然而从点击付费业务这种基于HTTP协议的机制来看，它并不十分可靠。除了网络本身不可靠的因素以外，它还有很明显的安全漏洞。首先容易想到的一个问题是：业务申请方可以重复地点击业务提供方的超链接以获得更多的计数。业务提供方如果不能防止这种情况的发生，那么这项业务在网上是无法生存下去的。另外的一个问题是：业务申请方无法去监控业务提供方的行为。因为业务申请方并不知道实际上有多少用户是通过其主页上的超链接而到达业务提供方主页的，所以后者完全可以在背地里少进行一些计数或者篡改前者的计数，就算被前者察觉出来也没有充分的证据可说明。
　　对于第一个问题，点击付费业务的提供者一般采用一种简单而有效的解决办法--判断客户端的IP地址。由于在一般情况下，不同客户端的IP地址是不同的，所以如果服务器发现在一段时间里，由同一IP地址发出的且Referrer域指向某业务申请方的HTTP客户端请求报文不止一份，则说明可能有人在重复地点击超链接，对于这种情况服务器就不进行计数。这样一来，业务申请方就很难再去“骗取”过多的计数，即使有办法(如更换客户机或伪造IP地址)也往往是得不偿失。
　　对于后一个问题，目前申请点击付费业务的许多Web站点还没有重视起来，而对业务提供方抱以相信的态度。针对这种现况，我们将在下面讨论作为业务申请方如何去监控业务提供方的行为。
3　基本方案
　　对于业务申请方来说，他首先必须要能够发现业务提供方的欺骗行为。其实，要做到这一点并不太难。业务申请方可以在他自己的站点上也进行用户点击计数，然后经常性地与业务提供方所作的计数进行比较。如果双方统计出的计数相差太大，业务申请方就有理由怀疑业务提供方有欺骗行为。下面提供了两种不同计数方案。
　　．方案一
　　业务申请方可以在他自己站点A的主页pageA.html上嵌入一段脚本语言，使得每当访问该页的用户点击业务提供方站点B的超链接时，用户的浏览器都会先向站点A发出一个通知，然后才向站点B的Web服务器提出HTTP请求。如图2。

　　这样站点A就可以确切地知道站点B的超链接被用户点击了多少次。下面就是这种方案的JavaScript脚本语言实现。我们在pageA.html页中采用帧结构把这一页分为两帧，一帧为pageAcontents.html，显示站点A的实际内容，而另一帧则对用户不可见，用于CGI程序的执行。pageAcontents.html页中定义的onClick事件使得用户在点击站点B的超链接时会先调用过程notify( )通知站点A中用于计数的CGI程序record.cgi，然后再去访问站点B的主页。
<html>
<!-- File: pageAcontents.html -->
...
<a href="http://siteB/pageB.html"
onClick="parent.notify('http://siteB/pageB.html')">
Click here for site B.</a>
...
</html>
*****************************************
<html>
<!-- File: pageA.html -->
<head><script language="JavaScript">
function notify(url) {
invisible.location="http://siteA/cgi-bin/record.cgi?refer=" + url；
</script></head>
<frameset rows="100%,*">
<frame src="pageAcontents.html" name="visible">
<frame src="about:blank" name="invisible">
</frameset>
</html>
　　让我们来分析一下站点A用方案一来统计计数的准确性。在通常的情况下，站点A和站点B会同时进行计数，但是如果通往站点B的网络出现拥塞或者站点B本身出现了问题，用户浏览器在通知站点A后并没有访问到pageB.html页，那么站点A就会比站点B多进行一次计数。所以站点A以这种方式统计的用户点击次数要比站点B的略高些。我们在这里并没有考虑用户浏览器在通知站点A的时候会出现问题。这主要是因为用户刚成功地访问过站点A的主页，在这时候出现问题的机会很小。
　　．方案二
　　方案二与方案一的机制稍有不同，如图3。

　　当用户点击了pageA.html页上站点B的超链接时，用户浏览器先向站点B发出HTTP客户端请求，待pageB.html页传回后，然后才向站点A发出一个通知报文。由于HTTP协议的状态无关性，所以在实现这一方案时利用了浏览器的安全特性。
<html>
<!--File:pageAcontents.html-->
...
<a href="http://siteB/pageB.html"
onClick="parent.setup('http://siteB/pageB.html')">
Click here for siteB.</a>
...
</html>
******************************************
<html>
<!--File:pageA.html-->
<head>
<script language="JavaScript">
var retrieved=null；
function setup(url) {
retrieved=url；
probe( ); }
function probe ( ) {var temp=visible.location.href；
setTimeout("probe ( )", 100); }
function notify ( ) {
invisible.location="http://siteA/cgi-bin/record.cgi>refer=" +retrieved; }
window.onerror=function( ){setTimeout("notify( )",1000); return true; }
</script></head>
<frameset rows="100%, *">
<frame src="3.htm" name="visible">
<frame src="about:blank" name="invisible">
</frameset>
</html>
　　同方案一类似，pageA.html页也是分为可见和不可见的两帧。当用户点击了站点B的超链接时，触发onClick事件去调用setup( )过程记录下一些必要的信息。然后用户浏览器去访问站点B的pageB.html页，并将其显示在可见帧(visible)之中。而在下载pageB.html页的过程中，顶级窗口定期地调用probe( )过程读取可见帧窗口中Location对象的href属性。根据浏览器中的安全模型，当新网页下载完成时，浏览器会重新定义新网页所在窗口中的对象，并不再允许其它窗口去访问这些对象了。所以在下载pageB.html页的过程中，浏览器允许顶级窗口中的脚本语言去访问可见帧中的对象；而一旦pageB.html页下载完毕，这种操作起一就会引个安全错误，我们正好可以利用这个安全错误触发onError事件去通知站点A的计数CGI程序。
　　在这种方式下，因为在站点A上进行的计数总是在站点B之后进行，所以站点A所统计出来的用户点击次数要比站点B统计出来的稍低些。
　　以上的两种方案的优点是：(1)除了特殊情况以外，计数比较准确；(2)它们对于业务申请方来说都很容易实现，并且不需要业务提供方的参与；(3)基本上对用户透明。完全可以作为发现业务提供方欺骗行为的一种方法。
　　但是这两种方案也有它们一定的局限性和缺陷。比如说在方案的实现中，用户的浏览器必须要能够支持帧结构；还必须允许脚本语言程序在网页中运行。而最重要的一点是，业务申请方用这两种方案所统计出的计数只能作为一种参考，而对业务提供方不具有任何约束力。业务提供方完全可以在出现问题时进行抵赖，而业务申请方也拿不出任何可以令人信服的证据。为了解决这个问题，我们提出了一种对业务提供方有一定约束力的改进方案。
4　改进方案一
　　要防止业务提供方的抵赖行为，我们可以采用数字签名技术。办法是，当业务提供方的站点B收到由业务申请方站点A转来的有效的请求时，一方面在站点A的帐号里增加计数，另一方面还向站点A发出一个确认消息，表示承认站点A推荐了一名用户到站点B。在这个确认消息中采用数字签名技术，使得站点B在事后不能否认它曾经发送过此消息。如果站点B不向站点A发确认消息，站点A可以通过前面的两种基本方案发现站点B的欺骗行为，从而可以提前采取必要的行动(如终止与站点B的点击付费业务)来避免更大的损失(至少站点B不可否认它以前发过的确认消息)。对于站点B而言，为了吸引更多站点为其作主页广告，它会遵守双方的协议向站点A发出签了名的确认消息。一旦站点A收到了站点B的确认消息以后，站点B就无法对此进行抵赖了。
　　但是，这个改进方案也存在着一个缺陷，那就是每次发送确认消息时站点B都需要进行数字签名。就目前的情况来看，进行数字签名的时间开销较大，这在站点B业务忙的时候不太适用，因此我们采用哈希链(Hash Chain)技术对这个方案作进一步的改进。
5　改进方案二
　　站点B首先产生一个大随机数s，再用一个单向哈希函数f( )对s作用k次产生p=fk(s)，然后站点B将元组<k，p>用数字签名后发给站点A。以后站点B的确认消息只需要包含元组<i, p'>即可，其中i表示第i次确认(1≤i≤k)，p'=fk-i(s)，这样站点B就可以避免每次确认都进行计算量较大的数字签名了。站点A每次收到B的确认后可以通过计算p=fi(p')来检查其正确性。如果双方发生争议，A只需要出示B签名过的元组<k, p>和<i, p'>，仲裁者通过判断p=fi(p')就可以知道B是否发过i次确认。这种哈希链的方法可以解决数字签名的时间开销问题，但它也有它的不利之处：因为k值不能太大(否则fk-i(s)的计算量也很大)，所以站点B必须经常更新k值。更新方法是站点B再传给站点A一个新的签名过的元组<k', fk'(s)>。
6　结束语
　　本文介绍了一种新型的网上点击付费业务并分析了它的安全问题，主要针对如何监控业务提供方的行为提出了几种实用的解决方案。这些方案或多或少还存在着一些缺陷和不足之处，还有待于作进一步的研究。
洪帆(华中理工大学计算机系 武汉 430074)
杨洋(华中理工大学计算机系 武汉 430074)
参考文献
1 M. K. Reiter, Detecting Hit Shaving in Click-Through Payment Schemes, in Proceedings of the 7th USENIX Workshop on Electronic Commerce (1998)
2 V.Anupam, Security of Web Browser Scripting Languages: Vulnerabilities, Attacks and Remedies, In Proceedings of the 7th USENIX Workshop on Security Symposium (1998)
3 FIPS180-1, Secure Hash Standard, Federal Information Processing Standards Publication 180-1
4 Tim Ritchey. Javascript for Netscape编程指南. 北京：清华大学出版社
收稿日期：1999年10月1日
