使用 Django Rest 框架保护 API

信息安全 hmac Python api
2021-09-01 01:47:19

我希望在 Django 之上实现一个公共(编辑:更确切地说,一个公开的经过身份验证的 api)API,而Django Rest Framework似乎是一个流行的选择。我习惯使用的 API 通常要求请求包含 API 密钥、随机数和 HMAC。

查看身份验证页面,我发现最接近内置 API 密钥的是JSON web token authentication

我想设计一个可以防止窃听和重放攻击的 API。如果我要手动实现这一点,我的直觉是为用户生成 API 密钥和秘密对,并让他们签署这样的请求:

import hmac, hashlib, time

API_KEY, API_SECRET = # loaded from environment
BASE_URL = "https://myservice/api/v1/"
def signed_request(method, endpoint, body=None):
    nonce = str(int(round(time.time() * 1000)))
    to_sign = nonce + method + endpoint + (body or '')
    sig = hmac.new(API_SECRET, to_sign, hashlib.sha256).hexdigest()

    headers = {
        'key' : API_KEY,
        'signature' : sig,
        'nonce' : nonce
    }

    # Actually make the request
    do_the_request(method, BASE_URL + endpoint, body, headers)

然后我会在服务器端验证:

def verify_request(method, endpoint, body, headers):
    secret = get_secret_from_database(api_key=headers['key'])
    verify = headers['nonce'] + method + endpoint + body

    valid_sig = hmac.new(secret, verify, hashlib.sha256).hexdigest() == headers['sig']
    valid_nonce = int(headers['nonce']) > get_last_nonce_from_database(api_key=headers['key'])

    if valid_sig and valid_nonce:
        # Valid message
    else:
        raise Exception("Bad signature")

我想知道

  • 我可以通过 Django Rest Framework 执行此操作吗?如果没有,是否有 Django 包可以执行此操作?(如果这不可能,是否有没有实施的原因?)
  • 考虑到我的目标,这是一个安全的身份验证方案吗(只有拥有令牌的人才能生成有效请求,没有重播等)?
  • 有没有更好、更惯用的方法?
  • 是否甚至有必要寻求第三方库,或者边缘案例是否足够有限,以至于家庭酿造的解决方案——伴随着单元测试——就足够了?
1个回答

我可以通过 Django Rest Framework 执行此操作吗?如果没有,是否有 Django 包可以执行此操作?(如果这不可能,是否有没有实施的原因?)

这不能通过 Django Rest 框架来完成,并且djangohmac受到@Dogeatcatworld 识别的定时攻击漏洞的影响。

考虑到我的目标,这是一个安全的身份验证方案吗(只有拥有令牌的人才能生成有效请求,没有重播等)?

不,不完全。为了安全起见,需要去除定时攻击漏洞。即,不是检查signature == expected_sig由于在字符串中的何处发现差异而在不同时间终止的,而是需要使用检查整个字符串的算法。

def is_equal(str_a, str_b):
    equal = True
    for i in range(min(len(str_a), len(str_b))):
        if str_a[i] != str_b[i]:
            equal = False
    return len(str_a) == len(str_b) and equal

请注意,这仍然可能会泄漏预期的签名长度。

有没有更好、更惯用的方法?

看起来是这样的。

是否甚至有必要寻求第三方库,或者边缘案例是否足够有限,以至于家庭酿造的解决方案——伴随着单元测试——就足够了?

使用 python 库进行所有加密并自己进行长度检查——这很好。