Python API簡單驗證
前言
因為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 innerauth
Python API簡單驗證