令牌信息验证Flash提交信息合法性

今天GRE学好回来的比较早,那就说一下昨天提到的问题: 首先我把事情交待清楚,也许你正需要解决这个的方法^_^ 搞过动态网页脚本比如ASP的朋友都知道,有一个HTTP_REFER的系统变量可以获得本次请求是又那个页面提交的。

换句话说,可以用这个东西来知道用户是通过那个页面或其他网站上页面的连入本网站的。

用这个可以进行防盗链检测。

比如www.websiteA.com上有一个文档:aaa.asp。websiteA不希望其他网站盗链到这个页面。于是他就在aaa.asp做了些检测,比如:

  if Instr(1,LCase(request.ServerVariables("HTTP_REFER")),"http://www.websitea.com") <>1 Then
     response.write "对不起,您是通过其他网页的盗链进入该网页的"
     response.End
  End IF

上面的意思就是说,首先获取用户是通过哪个页面进入该文档的,比如用户在www.websiteB.com/bbb.asp上的一个超链接连入了aaa.asp。那么request.ServerVariables("HTTP_REFER")就返回值www.websiteB.com/bbb.asp

于是知道,在"www.websiteB.com/bbb.asp"中是找不到子串"http://www.websitea.com"的,于是就可以判定该页面被盗链了,于是拒绝显示信息。

 当然HTTP_REFER还能用来做页面访问统计,还有在高手挑战2中就是用它来做关卡跟踪的,但这和本主题无关,所以想知道详情就自己找吧。 接下来就进入正题了。

那Flash和HTTP_REFER有什么关系呢?

这里就不是单纯的盗链问题。我认为一般下面几种情况是需要判断HTTP_REFER的。 比如我在网站上写了一个Flash游戏,游戏结束后允许玩家上传自己的姓名和得分。

这就会出现安全问题:做过Flash交互的人都知道,Flash和后台网页脚本的交互手段目前一般是用MovieClip.LoadVarible();和XML.sendAndLoad()方法。这没什么问题,但服务器是不会判断提交数据的真实性的,只要我协议和语法格式一致,任何人在任何地方都能提交一个伪造的“得分”到服务器。而如果那个游戏时对高分进行奖励的,那损失可不小。

那么是不是也能用HTTP_REFER来解决呢?理论上似乎可以,但试过人会发现,那该死的FlashPlayer是不会发送Refer数据的(可能为了安全性)。这样HTTP_REFER就完全失效了。 也有人想出来用密码首先加密数据,这个的确是办法,但就要看怎么实现了。如果这个游戏本来就是需要采用帐号登陆后进行的,那么密码是由玩家提供,问题也能解决。但如果没有登陆这一关,似乎密码就只能保存在swf文件中了。 但现在Flash完全是透明的东西。外面各种反编译软件可以很轻松的把swf高保真的还原到fla格式。这个密码也自然形同虚设……而且上面的登陆办法也有问题:如果玩家本人提交伪造数据呢? 最近设计ReformPost时候我也考虑到这个问题。

其实解决办法也不是什么新鲜的,就是采用目前比较常见的防治伪造数据包的办法,在提交表中附上令牌(Token) 说白了,令牌就是一个密钥。他首先由服务器给出,今后在客户端和服务器间的通讯都是需要密钥验证,如果发来的信息的密钥不存在或与当前的不符,则认为是伪造数据。 对于上面的大家都懂。那么怎么用Token来保护Flash通讯?关键就是在服务端何时在何地,用何种方式“告诉”Flash了我采用的办法是:首先在flash所在的页面中给出Token(需要动态脚本页)。比如服务端自动产生如下JavaScript插入在Flash所在的页面里

function GetToken()
{
   swfOcx.OnGetToken(strToken);
}

其中strToken是服务器每次产生的不重复序列(可以用当前时间),swfOcx是插入Flash的元素对象。 呵呵,想起来了吗?Flash8开始有了个新特性,可以利用ExternalInterface类随意访问外部JavaScript函数,也可以从外部JavaScript调用Flash中的用户函数。 下面就给出Flash中主动获得服务端给出的Token的代码:

import flash.external.*;
var strTokenID:String;
function OnGetToken(strToken:String) {
  strTokenID = strToken;
  play();
}
ExternalInterface.addCallback("OnGetToken"null, OnGetToken); 
ExternalInterface.call("GetToken");

首先调用外面的GetToken函数,然后GetToken调用了flash中注册的OnGetToken,并把Token作为参数传递回来。 p.s.其实应该可以直接用ExternalInterface.call一次性调用,利用函数返回值取得Token,但我试了几次都失败了,所以就采用这个折中的办法。 至于以后的事情我就不多说了。这样我们的目的也就达到了。呵呵,其实这都怪Adobe不好……(以前要怪macromedia) 恩,然后我再写篇别的...