Asegurando una API con Django Rest Framework

3

Estoy buscando implementar una API pública (edición: más bien, una API autenticada expuesta a Internet) sobre Django, y Django Rest Framework parece ser una opción popular. Las API con las que estoy acostumbrado a trabajar generalmente requieren que las solicitudes contengan una clave de API, un nonce y luego un HMAC.

Al mirar la página de autenticación, veo que lo más parecido a una clave API que está integrada es autenticación JSON web token .

Me gustaría diseñar una API que sea segura contra ataques de espionaje y repetición. Si tuviera que implementar esto a mano, mi instinto sería generar la clave de API & Parejas secretas para usuarios y hacer que firmen solicitudes como esta:

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)

Luego verificaría en el servidor que:

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")

Me gustaría saber

  • ¿Puedo hacer esto a través de Django Rest Framework, y si no hay un paquete para Django que haga esto? (Si esto no es posible, ¿hay alguna razón para que no se implemente?)
  • ¿Es este un esquema de autenticación seguro dados mis objetivos (solo la persona que posee el token puede generar solicitudes válidas, sin repeticiones, etc.)?
  • ¿Existe un enfoque mejor, más idiomático?
  • ¿Es incluso necesario buscar una biblioteca de terceros, o los casos límite son lo suficientemente limitados como para que una solución elaborada en casa, acompañada de pruebas de unidad, sea suficiente?
pregunta John Dorian 09.10.2017 - 15:43
fuente

1 respuesta

2
  

¿Puedo hacer esto a través de Django Rest Framework, y si no hay un paquete para Django que haga esto? (Si esto no es posible, ¿hay alguna razón por la que no esté implementado?)

Esto no se puede hacer a través de Django Rest Framework, y el paquete djangohmac sufre la vulnerabilidad de ataque de tiempo identificada por @Dogeatcatworld.

  

¿Se trata de un esquema de autenticación seguro dados mis objetivos (solo la persona que posee el token puede generar solicitudes válidas, sin repeticiones, etc.)?

No, no completamente. Para estar seguro, la vulnerabilidad de ataque de tiempo necesita ser eliminada. Es decir. en lugar de verificar signature == expected_sig , que terminará en diferentes momentos debido a que en la cadena se encuentra una discrepancia, se debe usar un algoritmo que verifique toda la cadena, independientemente de cómo se use.

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

Tenga en cuenta que esto todavía pierde la longitud de firma esperada, potencialmente.

  

¿Hay un enfoque mejor, más idiomático?

Eso parece.

  

¿Es incluso necesario buscar una biblioteca de terceros, o son los casos límite lo suficientemente limitados como para que una solución elaborada en casa, acompañada de pruebas de unidad, sea suficiente?

Use las bibliotecas de Python para toda la criptografía y revise la longitud usted mismo: está bien.

    
respondido por el John Dorian 09.11.2017 - 09:14
fuente

Lea otras preguntas en las etiquetas