1. 程式人生 > >Python API簡單驗證

Python API簡單驗證

method timestamp pda 需求 -a none 數據 Coding params

前言

因為CMDB內部的需求,需要一個API進行數據傳輸,用來傳遞需要抓取的服務端信息信息給抓取的autoclient,autoclient抓取好之後再通過API傳輸到服務器,保存到數據庫。但是為了防止惡意的API訪問,需要做一個驗證。

設想一

可以在客戶端跟服務端都規定好一串隨機字符串做驗證,只有當帶著這串驗證的請求發送過來的時候,才讓其進行訪問。

技術分享圖片

如果學過了爬蟲,大家很容易就發現,這串隨機字符串在瀏覽器裏面是可以監聽的,多觀察幾次總是會發現的。而且無論通過如何的方式,只要暴露在外面,都是會被察覺的。此時,就需要對其進行加密。

設想二

既然隨機字符串在web傳輸中是明文狀態,那我們試著將其轉換成密文的,轉換下思路,如果每次的請求都是隨機字符串配合一串一直變動的值進行md5加密,此時,產生的驗證字符串也應該變成動態密文。用什麽做動態字符串來配合約定好的字符串進行md5加密呢?既然是動態的,時間戳,是最好的選擇。

技術分享圖片

此時一切看著都很完美,但是,忽略了一點,無論是不是加密的字符串,http請求的時候都是可以監聽到的,所以即使加密,即使不知道怎麽加密的,依舊可以直接拿著這串字符串直接進行驗證訪問。尷尬。。。

設想三

其實上面的設想二已經做到了動態,思路上只要改一點就立刻變成可行的了。在http請求的時候,即使是在相當糟糕的網絡環境裏,也不會需要發送非常長的時間,因此,卡住時間便可以實現。

在拿到client的時間戳是,服務器段只需要跟當前時間戳進行比對,如果時間間隔小於10秒就當作正常訪問。就可以了。

技術分享圖片上面的設計思路完美的解決了動態的問題,此時不免還有疑問,如果真的會在10s內盜取到字符串直接訪問呢?

完善思路

基於上面的問題,可以再多加異步驗證,寫一個列表,訪問過的字符串都放在列表裏,後面的訪問都跟此列表比對下,如果在此列表內,就拒絕訪問。

技術分享圖片

如上的設計思路就可以解決問題。

優化

最後的設計思路肯定能解決問題,但是也存在一個問題,就是,隨著時間進度的推移,訪問列表一定會越來越大,始終是不友好的一點。肯定需要給字符串設計一個超時時間。

visited_list = [28g12b12128912e2kj|127381237812391, 829312g12be120e102ej12je91|12312984123123,....]

如果以上述的方式取跟系統時間比較當然是一件很費事的工作,占用很多的IO,可以使用redis來輕松實現這個功能。

技術分享圖片

CBV通過此類裝飾方式實現驗證

驗證代碼

技術分享圖片
def api_auth_method(request):
    auth_key = request.META.get(HTTP_AUTH_KEY)
    if not auth_key:
        return False
    sp = auth_key.split(|)
    if len(sp) != 2:
        return False
    encrypt, timestamp = sp
    timestamp = float(timestamp)
    limit_timestamp = time.time() - ASSET_AUTH_TIME
    print(limit_timestamp, timestamp)
    if limit_timestamp > timestamp:
        return False
    ha = hashlib.md5(ASSET_AUTH_KEY.encode(utf-8))
    ha.update(bytes("%s|%f" % (ASSET_AUTH_KEY, timestamp), encoding=utf-8))
    result = ha.hexdigest()
    print(result, encrypt)
    if encrypt != result:
        return False

    exist = False
    del_keys = []
    for k, v in enumerate(ENCRYPT_LIST):
        print(k, v)
        m = v[time]
        n = v[encrypt]
        if m < limit_timestamp:
            del_keys.append(k)
            continue
        if n == encrypt:
            exist = True
    for k in del_keys:
        del ENCRYPT_LIST[k]

    if exist:
        return False
    ENCRYPT_LIST.append({encrypt: encrypt, time: timestamp})
    return True


def api_auth(func):
    def inner(request, *args, **kwargs):
        if not api_auth_method(request):
            return JsonResponse({code: 1001, message: API授權失敗}, json_dumps_params={ensure_ascii: False})
        return func(request, *args, **kwargs)

    return inner
auth

Python API簡單驗證