Flash Remoting 教學 4 - 使用flash remoting Authenticate

In tutorials   August 1, 2005 - 4:29 pm

◆flash remoting的安全考量

Flash remoting是很快很方便的client/server連線方式,但由於flash swf本身很容易被破解,因此基本上任何人都可以知道server端的business delegate class名稱與method。

大家可以假想下列情況,如果在server上有一個MyClass 的component, 裏面有 deleteUserById($id)這支function,而現在hacker透過反組譯swf檔知道下列資訊:

Server ip: http://xxx.xxx.xxx.xxx
Component: /Test/MyClass.php
Function: deleteUserById()

他是否就可以開始跑迴圈將資料庫內的資料一筆筆刪除呢?

理論上這是可行的,即使你的程式設計中已經有login的手續,還是可以很輕易的繞過這個檢查而直攻server上的components.

以往在coldfusion/jrun使用flash remoting時,我們可以使用內建的role與authenticate功能,配合flash 端的setCredential來做每次method call的認証,但在amfphp v1.0之前這個功能一直是很穩定的維持爛掉的狀態,不論怎麼設定role或credential都不會有反應,好在這次patrick終於將整個功能都弄好了。

本文將介紹如何使用 flash remoting的 Authenticate class做安全認証。

◆Authentication 的方式

Amfphp進行authentication 的方式一樣是透過method table來設定,請看下列程式碼。

PHP:
  1. function AuthClass()
  2.     {
  3.         $this->methodTable = array(
  4.  
  5.             "getPerson" => array(
  6.                 "description" => "Describe here your method",
  7.                 "access" => "remote",
  8.                 "roles" => "admin"   // this method will be available only if authenticate method returns "admin"
  9.             ),
  10.  
  11.             "doLogin" => array(
  12.                 "description" => "Describe here your method",
  13.                 "access" => "remote",
  14.                 "roles" => "admin"   // this method will be available only if authenticate method returns "admin"
  15.             )
  16.         );
  17.     }

在這個method table中可看到 roles => “admin”這樣的參數,這就是在設定 doLogin()這支method只有身份為 role的人才能存取,當然這裏也可以放多個參數,例如roles => “admin”, “supervisor”, “dbadmin”這樣就有三種身份的人可以存取這個method。

而在flash端,則是透過一個可能大部份人都不曾知道的指令 setCredential()來搭配。它的語法如下。

Actionscript:
  1. setCredentials(inputName.text, inputPwd.text);

這是一個內建於 flash remoting NetConnection class的指令,一旦設定好日後每個method call之前都會在AMF封包頭加上這個資訊,因此php端一收到這個資訊就會自動進行認証。

聽起來很簡單對不對?amfphp可是搞了兩年才有完整的功能呀~

◆php程式

接下來我們開始寫server端的程式。

PHP:
  1. <?php
  2. /*
  3. @author: jeremy@richtechmedia.com
  4. @website: http://ria.richtechmedia.com
  5. released under CC 2.5.
  6. */
  7.  
  8. class AuthClass
  9. {
  10.     function AuthClass()
  11.     {
  12.         $this->methodTable = array(
  13.  
  14.             "getPerson" => array(
  15.                 "description" => "Describe here your method",
  16.                 "access" => "remote",
  17.                 "roles" => "admin"   // this method will be available only if authenticate method returns "admin"
  18.             ),
  19.  
  20.             "doLogin" => array(
  21.                 "description" => "Describe here your method",
  22.                 "access" => "remote",
  23.                 "roles" => "admin"   // this method will be available only if authenticate method returns "admin"
  24.             )
  25.         );
  26.     }
  27.  
  28.     function getPerson()
  29.     {
  30.         $person = array();
  31.         $person['first_name'] = "Alessandro";
  32.         $person['last_name']  = "Crugnola";
  33.         return $person;
  34.     }
  35.    
  36.     function doLogin()
  37.     {
  38.         return true;
  39.     }
  40.  
  41.     //
  42.     function _authenticate($name, $pwd){
  43.         //mysql settings
  44.         $host = "localhost";
  45.     $link = mysql_connect($host, "root", "");
  46.     mysql_select_db("test", $link);
  47.     //
  48.     $sql="select role from tbl_users where name='$name' && pwd='$pwd'
  49.     ";
  50.     $result = mysql_query($sql);
  51.     //
  52.     if(mysql_num_rows($result)>=1){
  53.         $rows = mysql_fetch_assoc($result);
  54.         $role= $rows["role"];
  55.         //now we know the role of this user, just return it
  56.         return $role;
  57.     }else {
  58.             return false;
  59.         }
  60.     }
  61. }
  62. ?>

這裏面需要注意的是最後一個_authenticate(),它是amfphp內定的名稱,任務就是在接收到AMF封包中的 credential資訊時進行認証。這個function的內容是可以由我們自由撰寫的,從範例中可以看到我們是跟資料庫取得name 與password做比較,這是很典型的認証手法。

如果帳號/密碼這關通過,我們就將前面順便query出的role傳回amfphp,從此這個user的身份就確認下來了。

附帶一提,這個role 的資訊是記錄在session中,每次flash 與 amfphp連線時,都會自動產生一個session key,因此不用擔心每次都會跟db要一次資料或是搞錯role的身份。

◆flash程式

接下來看一下flash的程式

Actionscript:
  1. /*
  2. @author: jeremy@richtechmedia.com
  3. @website: http://ria.richtechmedia.com
  4. released under CC 2.5.
  5. */
  6.  
  7. //remoting related classes
  8. import mx.remoting.Service;
  9. import mx.remoting.PendingCall;
  10. import mx.remoting.RecordSet;
  11. import mx.remoting.debug.NetDebug;
  12. import mx.rpc.RelayResponder;
  13. import mx.rpc.ResultEvent;
  14. import mx.rpc.FaultEvent;
  15.  
  16. //start debugging so we can see debug info in NCD.
  17. NetDebug.initialize();
  18.  
  19. //gateway settings
  20. var gateway:String = "http://localhost/auth/gateway.php";
  21. respGeneral = new RelayResponder(this, "resultReceived", "resultFailure");
  22. _service = new Service(gateway, null, "AuthClass", null, respGeneral);
  23.  
  24. //callback when result was returned from server
  25. function resultReceived(re:ResultEvent):Void{
  26.     //if login success, we can branch to other method calls
  27.     if(re.result){
  28.         trace("login result: " + re.result);
  29.         //other method calls here...
  30.     }
  31. };
  32.  
  33. //if authentication failed, FaultEvent will show the error message
  34. function resultFailure(fe:FaultEvent):Void{
  35.     trace("failed: " + fe.fault.description);
  36. }
  37.  
  38. //send message to server
  39. btnLogin.clickHandler = function(){
  40.     _service.connection.setCredentials(inputName.text, inputPwd.text);
  41.     var pc:PendingCall = _service.doLogin();
  42. }

這段程式碼其它部份都是很標準的remoting 程序,唯一要特別注意第40行這部份,當使用者在login form裏填好name, password按下 login button時,我們先用 setCredentials(inputName.text, inputPwd.text)將他/她輸入的帳號密碼設成credential,然後進行_service.doLogin(),這樣做的目地是因為setCredential()只會在下一次的remoting call才會發揮功能,因此不能太早設定。

另外,請特別注意最後這個 doLogin()它實際的功能並不是真的在負責處理login,我們只是用它去測試前面的 setCredential()是否正確,因為如果前面的credential設定是錯誤的,那接下來一執行 doLogin()時,就會產生類似下面的錯誤訊息:

This user is not does not have access to {doLogin}.

我們就可以依此做 if..else..判斷,看是要提醒user再試一次,或是進行下一步的動作。而如果不用doLogin當helper的話,我們就必需真的直接執行其它指令,這樣會增加method call曝光的機會,能免則免。

有了以上的認知,應該可以瞭解為何我們需要 resultReceived 與 resultFailure 兩個callback分別處理正確與錯誤的訊息,這部份要小心判斷免的讓駭客偷偷溜進去了。

◆ 測試

接者我們來測試看看。資料庫內已內建兩組帳號(在下載檔案裏有sql schema可直接套用),帳號|密碼|身份 分別是:
John, a, admin
Mary, a, client

我們先故意不輸入資料就按button,結果是下圖

圖中可以看到出現status訊息,說明User Error,自然就進不去囉!

然後我們輸入 john, a 試試

大家可以看到此時amfphp已知道john的身份是admin, 因此它有權限可以執行某些method了。

◆結論

Web app的安全性說實話再怎麼做都是不夠的,永遠有backdoor可能被駭客發現,但最低層次至少基本的防護是該先做好的,同樣的問題在寫flashcom的應用程式時其實更嚴重,而且要付出的成本也更大,畢竟flashcom是論人頭與頻寬來算license, 如果不好好保護被人隨便hijacking損失可就大了。

另一方面,除了上述的 role-based authentication外,也可考慮使用SSL做為連線方式,並搭配MD5將資料編碼,這樣可以連傳輸端這一塊也保護起來。

最後,關於flash安全性的問題要有一個基本的認知:只要swf發佈出去就是不安全的,就算你用三道編碼器反覆加密過,也總有被破解的一天,因此重要的business logic與data絕不可放在 client端。

據說在不久的將來當Actionscirpt 3出來後,flash會支援 binary-code downloads,屆時或許就有比較完善的保護方案,不過那又是後話等真的實現時再說吧。

檔案下載

by admin

5 Comments Add your own

  • 1. Yaya&hellip  |  August 9th, 2005 at 5:28 pm

    Hi Jeremy,

    i have theses errors when i publish the fla :
    **Erreur** C:\wamp\www\identifier_client_mot_de_pass\code.as: Ligne 21 : Impossible de charger la classe 'mx.rpc.RelayResponder'.
    respGeneral = new RelayResponder(this, "resultReceived", "resultFailure");

    **Erreur** C:\wamp\www\identifier_client_mot_de_pass\code.as: Ligne 22 : Impossible de charger la classe 'mx.remoting.Service'.
    _service = new Service(gateway, null, "AuthClass", null, respGeneral);

    **Erreur** C:\wamp\www\identifier_client_mot_de_pass\code.as: Ligne 25 : Impossible de charger la classe 'mx.rpc.ResultEvent'.
    function resultReceived(re:ResultEvent):Void{

    **Erreur** C:\wamp\www\identifier_client_mot_de_pass\code.as: Ligne 34 : Impossible de charger la classe 'mx.rpc.FaultEvent'.
    function resultFailure(fe:FaultEvent):Void{

    **Erreur** C:\wamp\www\identifier_client_mot_de_pass\code.as: Ligne 41 : Impossible de charger la classe 'mx.remoting.PendingCall'.
    var pc:PendingCall = _service.doLogin();

    What can i do ?
    Yaya

  • 2. jeremy&hellip  |  August 9th, 2005 at 11:29 pm

    hi yaya,

    you need to download this file from macromedia website:

    http://download.macromedia.com/pub/flashremoting/mx2004/components/actionscript_2.0/flashremoting_comp_sourcecode.zip

    and also read this page for usage guide:

    http://www.macromedia.com/support/documentation/en/flash_remoting/mx2004/readme_source.html

    basically those are flash remoting source files, which can be unzipped into flash class path, then the "import" command won't report error again.

    another way to get this file is simply dragging the flash remoting components on the stage, so compiler would find proper classes.

  • 3. jeff&hellip  |  April 27th, 2006 at 12:51 pm

    站长好样的。。。。多谢啦。。。偶会常常来得哦。。哈哈

  • 4. 真神&hellip  |  July 13th, 2006 at 6:04 pm

    這樣的話,是不是登錄成功後我就把name和pwd值存在一個地方,以後每做一次操作,都要運行一次

    setCredentials(strName, strPwd)

    好麻煩

  • 5. jeremy&hellip  |  July 13th, 2006 at 7:55 pm

    不用啊,setCredentials() 只要執行一次,接下來每次的 server call都會自動在封包前面加上 name, pass。

    你可以用抓封包的軟體分析看看就知道,這兩個值會被加在最前面。

留言回應

hidden

您的留言會先經過站長認証後才刊登在網站上。
your comments will be approved by Administrator before appearing on the page.

Trackback this post  |  Subscribe to the comments via RSS Feed

mobile phone