Protezione di un'API con Django Rest Framework

3

Sto cercando di implementare un'API pubblica (modifica: piuttosto, un'autentica API esposta a Internet) su Django e Django Rest Framework sembra essere una scelta popolare. Le API con le quali sono abituato a lavorare solitamente richiedono che le richieste contengano una chiave API, un nonce e quindi un HMAC.

Guardando la pagina di autenticazione, vedo che la cosa più vicina a una chiave API incorporata è autenticazione token web JSON .

Mi piacerebbe progettare un'API sicura contro gli attacchi di intercettazione e riproduzione. Se dovessi implementarlo a mano, il mio istinto sarebbe quello di generare API key & coppie segrete per gli utenti e farle firmare richieste come queste:

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)

Vorrei quindi verificare sul lato server che:

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

Mi piacerebbe sapere

  • Posso farlo tramite il Django Rest Framework e, se no, c'è un pacchetto per Django che lo fa? (Se questo non è possibile, c'è un motivo per cui non è implementato?)
  • Si tratta di uno schema di autenticazione sicuro in base ai miei obiettivi (solo la persona in possesso del token può generare richieste valide, senza replay, ecc.)?
  • C'è un approccio migliore, più idiomatico?
  • È anche necessario cercare una libreria di terze parti, o i casi limite sono abbastanza limitati che una soluzione prodotta in casa - accompagnata da test unitari - è sufficiente?
posta John Dorian 09.10.2017 - 15:43
fonte

1 risposta

2

Can I do this via the Django Rest Framework, and if not is there a package for Django that does this? (If this isn't possible, is there a reason it's not implemented?)

Questo non può essere fatto tramite il Django Rest Framework e il djangohmac pacchetto soffre della vulnerabilità di attacco temporale identificata da @Dogeatcatworld.

Is this a safe authentication scheme given my goals (only the person in possession of the token can generate valid requests, no replays, etc.)?

No, non completamente. Per essere sicuro, è necessario rimuovere la vulnerabilità dell'attacco di cronometraggio. Cioè piuttosto che controllare signature == expected_sig che terminerà in momenti diversi a causa del punto in cui nella stringa viene rilevata una discrepanza, è necessario utilizzare un algoritmo che controlli l'intera stringa.

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

Si noti che questo perde ancora la lunghezza della firma prevista, potenzialmente.

Is there a better, more idiomatic approach?

Che sembra.

Is it even necessary to seek a third-party library, or are the edge cases limited enough that a home brewed solution--accompanied by unit tests--is sufficient?

Usa le librerie python per tutta la crittografia e fai il controllo della lunghezza - va bene.

    
risposta data 09.11.2017 - 09:14
fonte

Leggi altre domande sui tag