2011-10-03 430 views
24

我們正在爲Android構建一個需要訪問Web服務的遊戲 - 因此我們在PHP中編寫了一個RESTful API,該API運行在我們自己的服務器上。什麼API提供的是:創建用戶,登錄,下載遊戲,檢索遊戲列表,提交得分...等現在我想,如果一些有經驗的用戶獲取URL格式的API - s /他就能垃圾的系統在許多方面:保護可從Android訪問的REST API

  • 創建一個腳本&運行它來自動創建用戶 - 我想我可以阻止它通過CAPTCHA或成才這樣。但是,驗證碼會再次令遊戲玩家煩惱。
  • 惡意用戶使用他的瀏覽器登錄,下載遊戲&然後按照他的意願提交分數 - 所有這些都是通過在瀏覽器中輸入API來調用API。我假設惡意用戶以某種方式知道API URL調用 - 通過在應用程序發出HTTP請求時嗅探。
  • 我需要確保請求只能從安裝遊戲的Android設備進行。 (遊戲將免費)

現在我該如何防止這種濫用?

回答

27

我認爲你將永遠無法隱藏的URL由應用程序 叫(我是否正在運行一個根版Android手機,我應該能夠窺探所有網絡通信)

但你真正的問題是你需要以某種方式驗證你的api。

一種方法是實現OAUTH,但也許這是矯枉過正。

如果你想要一個簡單的機制,那麼這個怎麼樣;

  1. 創建祕密密鑰
  2. 構建的API請求(如:https://my.example.com/users/23?fields=name,email
  3. 哈希這個請求路徑+加你的密鑰(例如MD5(網址+ SECRET_KEY)== 「a3c2fe167」)
  4. 該散列添加到您的要求(現在是https://.....?fields=name,email&hash=a3c2fe167
  5. 的API結束,做相同的轉換(刪除散列PARAM)
  6. 檢查URL的MD5和祕密密鑰

只要祕密保守祕密,沒有人可以僞造你的請求。

例(僞代碼):

Android的一面:

SECRET_KEY = "abc123" 

def call_api_with_secret(url, params) 
    # create the hash to sign the request 
    hash = MD5.hash(SECRET_KEY, url, params) 

    # call the api with the added hash 
    call_api(url+"&hash=#{hash}", params) 
end 

服務器端:

SECRET_KEY = "abc123" 

def receive_from_api(url, params) 
    # retrieve the hash 
    url_without_hash, received_hash = retrieve_and_remove_hash(url) 

    # check the hash 
    expected_hash = MD5.hash(SECRET_KEY, url_without_hash, params) 

    if (expected_hash != received_hash) 
    raise our exception! 
    end 

    # now do the usual stuff 
end 
+1

非常感謝,@ matthew-rudy :)我是否爲每個用戶創建了「密鑰」?如果那樣,我該如何分配它?如果我在註冊後將密鑰發送給應用程序作爲響應,則可以簡單地從他的桌面瀏覽器進行API調用,並將密鑰作爲響應。 – giga

+6

在馬修的方法中,祕密密鑰絕不會通過電線發送。這是一個「鹽」,附加到URL來創建一個哈希。由於哈希是單向函數,因此用戶無法從中恢復密鑰。它需要用戶反編譯APK並查看源代碼以找到密鑰。您可以使用代碼混淆技術來使這變得更加困難,但只要有足夠的時間和動力,人們總能找到鑰匙。 –

+3

確實@JasonLeBrun,我添加了一個僞代碼示例。但是,千兆,我建議你在PHP中搜索現有的框架,而不是創建自己的框架。我確定必須有許多現有的解決方案。 –

1

如果你真的要保護的連接,那麼你就必須使用公鑰密碼學,例如RSA。設備將使用公鑰對登錄信息進行加密,並且在服務器端您必須使用私鑰解密。登錄後,服務器將發送令牌/加密密鑰(響應將是加密的JSON或其他內容),設備將存儲該令牌。從那時起,只要會話未過期,設備就會發送使用該令牌加密的所有信息。對於這個請求,你不應該使用需要更多時間的RSA原因。您可以使用AES256(這是一種流行的私鑰加密)和從服務器收到的加密密鑰來加密您的請求。

爲了簡單起見,您可以完全放棄RSA(如果您未發送付款信息),並使用帶有私鑰的AES256完成所有操作。步驟應該是 -

  • 使用私鑰加密每個傳出的請求。
  • 將加密的字符串轉換爲基本64字符串。
  • 對基本64編碼字符串進行URL編碼。
  • 發送。

在服務器端

  • DO基站64解碼
  • 解密使用私鑰。

您的請求應該帶有一個簽名(例如,加密密鑰附加爲salt),以便在解密後可以識別它。如果簽名不存在,只需放棄請求。

對於發送回覆也是這樣。

Android SDK應該具有使用AES256和Base 64編碼進行加密的方法。

+0

那麼HTTPS會更好嗎? –

+0

我從來沒有告訴過它。如果您沒有HTTPS認證,這是一種解決方法。 – rushafi

+0

如果您不願意從CA購買證書幾美元,您可以使用HTTPS自簽名證書。你也提出了一個私鑰解決方案,所以這在技術上是相同的,只是更難實施。 –

3

您提到用戶僞造高分。如果您的用戶通過身份驗證,這仍然會發生。當遊戲上傳高分時,您可能需要上傳分數證明。例如,來自103個臭蟲的得分20100,飛行1200英里,達到3級,吃了2顆櫻桃。這絕不是完美的,但會覆蓋低垂的果實。

您應該做的第一件事就是通過身份驗證的用戶。 Userid /密碼/會話令牌等,看看你是否可以找到一些已經存在的框架。一旦你有用戶身份驗證,確保你可以用TLS或類似的方法安全地做到這一點。

據我所知,你的服務器無法確定請求是否來自你的應用程序(這只是數據包中的一小部分),但你至少可以讓別人難以惡意。

+0

感謝您的評論。不是上傳總分,而是將其分成幾個部分,聽起來不那麼有害。 – giga

+0

@giga這不僅僅是分手。您希望他們提交分數,並證明提交的分數是由知道分數如何工作(您的應用程序會知道這一點)生成的。如果你只是分割它,那麼惡意用戶仍然可以提交任意值。你想迫使他們不得不對你的應用程序進行反向工程,而不是隻是窺探通信。 – goldsz

7

,其他人已經在這裏提出的解決方案是討論的一些想法被發現稱爲security through obscurity。基本上他們試圖掩蓋協議並隱藏實現。這可能會工作,直到有人能夠足夠的反彙編應用程序和反向工程協議。黑客非常有能力。

問題是如果你的應用程序是值得開裂的?像iTunes,DVD或索尼PS3網絡等計劃顯然是值得的。如果沒有人能夠解決問題,那麼默默無聞的方法可能會奏效。只是不要欺騙自己,這是不可行的。

由於您不能相信設備或您的應用,您必須信任該用戶。爲了信任用戶,您需要用戶識別和授權系統。基本上是登錄到您的應用程序。而是使用第三方系統:OpenID(谷歌帳戶)或OAuth(臉譜,微博)來滾動您自己的識別系統(使用確認電子郵件等登錄)。在facebook的情況下使用服務器端認證方案。

我會做什麼:

  1. 允許,直到他們想「拯救」的服務器,結果用戶自由地玩遊戲。
  2. 保存結果前,請通過上述方法登錄。
  3. 使用HTTPS將數據發送到您的服務器。從受信任的CA購買SSL證書,因此您不必處理自簽名證書。
1

按照these guidelines從Android團隊,以確保你的後端,通過使用通過谷歌的API提供OAuth憑證。