2012-01-19 40 views
1

我一直在做幾個月的web開發,並一直存在這個嘮叨問題。通常頁面通過查詢字符串來請求內容,該查詢字符串通常包含有意義的數據,例如數據庫中的ID。一個例子將是一個鏈接,如: Django:防止外部查詢字符串請求

http://www.example.com/posts?id=5

我一直在想一個好的策略,以防止用戶手動輸入的id值沒有從鏈接訪問它 - 我只是希望確認通過我網站上顯示的鏈接提出的請求。此外,該網站可能沒有認證系統,並允許匿名瀏覽;儘管如此,信息並不是特別敏感,但我仍然不喜歡不能控制訪問某些信息的想法。我想,一種選擇是使用這些頁面的HTTP POST請求 - 我不相信用戶可以模擬發佈請求,但我可能是錯的。

此外,用戶可以爲該id放置任意數量並最終請求數據庫中不存在的記錄。當然,我可以驗證所請求的身份證,但是我會浪費資源來適應此檢查。

有什麼想法?我正在與Django合作,但任何編程語言的一般策略都會很好。謝謝。

+5

用戶可以模擬瀏覽器可能提出的任何請求。 –

+0

爲了防止用戶使用不需要的方法提供數據,有一個CSRF,它可以在POST請求中使用,所以它也應該在GET請求中工作(我希望)。但要正確 - 依靠URL中請求的參數並不是一件好事。檢查數據庫項目是否存在不是浪費時間和資源。這可能會在未來某個時候導致嚴重的問題。 – koressak

+0

「......我不喜歡不能控制訪問某些信息的想法」。你怎麼知道哪些信息是「確定的」,哪些不是?正如我看到的最好的方法是添加'私人'布爾字段到您的模型。 – DrTyrsa

回答

4

首先,在GET和POST之間進行選擇:用戶可以模擬任何類型的請求,因此POST不會幫助您。在兩者之間進行選擇時,最好根據用戶採取的行動決定或他們如何與您的內容進行交互。他們是否得到一個頁面或發送數據(一種形式就是一個明顯的例子)?對於您檢索某種文章的情況,GET是適當的。

另外值得注意的是,如果內容適用於書籤,GET是正確的選擇。正如你所說的,僅僅根據引薦來源提供一個URL,「阻止用戶在沒有從鏈接訪問它的情況下手動輸入id的值」 - 這是一個糟糕的主意。這會給你帶來無數的麻煩,這對用戶來說可能不是一個好的體驗。

作爲一般原理,避免依賴數據庫記錄的主鍵。該鍵(在您的情況下,id = 5)應該純粹作爲自動增量字段來處理,以防止記錄衝突,即保證您始終擁有表中所有記錄的唯一字段。該ID字段是後端實用程序。不要將其暴露給用戶,也不要自己依靠它。

如果你不能使用ID,你使用什麼?一個常見的習慣用法是使用記錄的日期slu 012或兩者。如果您正在處理帖子,請使用發佈/創建的日期。然後添加一個文本字段,將保存URL友好和描述性的話。稱它爲slu and並閱讀Django的models.SlugField以獲取更多信息。另外,請查看基本上任何新聞網站上的文章的網址。您的最終網址將如下所示:http://www.example.com/posts/2012/01/19/this-is-cool/

現在您的網址對您的網頁很友善,Google-fu搜索引擎優化功能,可以書籤並且不可猜測。因爲您不依賴後端數據庫修復任意ID,所以您可以自由地......恢復備份數據庫轉儲,移動數據庫,將自動增量數ID更改爲UUID哈希,無論如何。只有你的數據庫纔會關心,而不是你作爲程序員而不是你的用戶。

哦,不要擔心用戶「請求不存在的記錄」或「驗證所請求的ID」......無論如何您必須這樣做。它不消耗不必要的資源。數據庫支持的網站是如何工作的。您必須將請求連接到數據。如果請求不正確,則爲404。您的網絡服務器爲不存在的網址執行此操作,您需要爲不存在的數據執行此操作。結賬Django的想法/實施get_object_or_404()

+0

+1不在URL中使用PK。 –

+0

謝謝,這給了我一些方向。 –

+0

當使用slu how時,你如何保證不會有任何碰撞(即對於不同的ID有兩個相同的slu))?我想時間戳中更精細的粒度會做到這一點。 –

0

我不知道這是否是正確的方法,但也許你可以保存他將在GET請求後重定向的url到會話中,然後編寫decorator,以便如果會話有url將他帶到那個頁面。否則給404錯誤或什麼。

0

我知道有兩種方法可以有效地做到這一點,因爲基本上沒有辦法阻止某人僞造任何請求。

第一個是不在查詢參數中使用裸ID。相反,生成一個大的隨機數,並將其鏈接出來。您必須在數據庫中保留一張表格,將您的隨機數字映射到它們所代表的實際ID,並且您最終必須清理表格。這很容易實現,但需要一些存儲空間,並且偶爾會對存儲的數據進行一些管理。

第二種方法是在您建立鏈接時簽署數據。通過向數據附加密碼簽名,並在請求發出時驗證簽名,確保只有您的Web服務可能創建了鏈接。即使請求本身是僞造的 - 也許是書籤,寫下來,複製並粘貼到另一個瀏覽器 - 你知道你的網站已經授權該URL。爲了做到這一點,你需要創建一個消息認證碼(MAC)和你正在簽名的數據(比如說,'id'值,或者可能是你簽名數據的id和時間)和與一個祕密密鑰,你只保留在你的服務器上。

然後,在您的視圖中,您將採用id值(或id和timestamp,如果這就是您正在使用的),然後再次構建MAC並查看它們是否匹配。如果有任何區別,您拒絕該請求被篡改。

查看所有細節的hmac module以及hashlib module的python文檔。

您可以像這樣在python中生成鏈接:

設置。潘岳:

hmac_secret_key = '12345' 

views.py:

import time, hmac, hashlib 
from django.conf import settings 

def some_view(request): 
    ... 
    id = 5 
    time = int(time.time()) 
    mac = hmac.new(
     settings.hmac_secret_key, 
     '%d/%d' % (id, time), 
     hashlib.sha1) 
    url = 'http://www.example.com/posts/id=%d&ts=%d&mac=%s' % (
     id, time, mac.hexdigest()) 
    # Now return a template with that url in it somewhere 

進行驗證,另一種觀點認爲,你可以使用這樣的代碼:(警告,警告,不健壯,很多錯誤檢查還是要做)

def posts_view(request): 
    id = int(request.GET['id']) 
    ts = int(request.GET['ts']) 
    mac_from_url = request.GET['mac'] 

    computed_mac = hmac.new(
     settings.hmac_secret_key, 
     '%d/%d' % (id, time), 
     hashlib.sha1) 

    if mac_from_url <> computed_mac: 
     raise SomeSecurityException() 

    # Now you know that the request is legit. 
    # You can check the timestamp here, too, if you like. 
+0

這很聰明。謝謝。 –