Ho cercato di risolvere un problema simile.
I miei utenti devono essere autenticati per ogni richiesta che fanno.
Mi sono concentrato sull'ottenere l'autenticazione degli utenti almeno una volta dall'app backend (convalida del token JWT), ma dopo ho deciso che non avrei più bisogno del back-end.
Ho scelto di evitare di richiedere qualsiasi plug-in Nginx che non sia incluso di default. Altrimenti puoi controllare nginx-jwt o Lua scripting e queste sarebbero probabilmente ottime soluzioni.
Indirizzamento dell'autenticazione
Finora ho fatto quanto segue:
-
Delegata l'autenticazione a Nginx usando auth_request
. Ciò chiama un'ubicazione internal
che passa la richiesta al mio endpoint di convalida del token di back-end. Questo da solo non risolve il problema della gestione di un numero elevato di convalide.
-
Il risultato della convalida del token viene memorizzato nella cache utilizzando una direttiva proxy_cache_key "$cookie_token";
. In seguito alla convalida del token, il backend aggiunge una direttiva Cache-Control
che indica a Nginx di memorizzare solo il token nella cache fino alla data di scadenza. A questo punto, qualsiasi token di autenticazione convalidato una volta è nella cache, le richieste successive dallo stesso utente non toccano più il back-end di autenticazione!
-
Per proteggere la mia app di back-end da potenziali inondazioni da token non validi, memorizzo anche le convalide rifiutate, quando il mio endpoint backend restituisce 401. Questi sono solo memorizzati nella cache per una breve durata per evitare potenzialmente di riempire la cache di Nginx con tali richieste
Ho aggiunto un paio di ulteriori miglioramenti come un endpoint di logout che invalida un token restituendo 401 (che è anche memorizzato nella cache da Nginx) in modo che se l'utente fa clic su Esci, il token non può più essere utilizzato anche se non lo è scaduto.
Inoltre, la mia cache Nginx contiene per ogni token, l'utente associato come un oggetto JSON, che mi salva dal recuperarlo dal DB se ho bisogno di queste informazioni; e inoltre mi salva dalla decrittografia del token.
Con questa configurazione, potrei disabilitare l'autenticazione nel mio back-end, poiché tutte le richieste in arrivo raggiungono la direttiva auth_request
Nginx prima di toccarla.
Questo non risolve completamente il problema se è necessario eseguire qualsiasi tipo di autorizzazione per risorsa, ma almeno hai salvato la parte di autorizzazione di base. Inoltre, puoi evitare di decodificare il token o effettuare una ricerca DB per accedere ai dati dei token poiché la risposta dell'autenticazione memorizzata nella cache di Nginx può contenere dati e passarli indietro al back-end.
Ora, la mia più grande preoccupazione è che potrei rompere qualcosa di ovvio relativo alla sicurezza senza rendermene conto.
Detto questo, ogni token ricevuto viene comunque convalidato almeno una volta prima di essere memorizzato nella cache da Nginx. Qualsiasi token temperato sarebbe diverso, quindi non colpirebbe la cache dato che anche la chiave della cache sarebbe diversa.
Inoltre, forse vale la pena ricordare che un'autenticazione del mondo reale avrebbe combattuto contro il furto di token generando (e verificando) un Nonce o qualcosa di simile.
Ecco un estratto semplificato della mia configurazione di Nginx per la mia app:
# Cache for internal auth checks
proxy_cache_path /usr/local/var/nginx/cache/auth levels=1:2 keys_zone=auth_cache:10m max_size=128m inactive=30m use_temp_path=off;
# Cache for content
proxy_cache_path /usr/local/var/nginx/cache/resx levels=1:2 keys_zone=content_cache:16m max_size=128m inactive=5m use_temp_path=off;
server {
listen 443 ssl http2;
server_name ........;
include /usr/local/etc/nginx/include-auth-internal.conf;
location /api/v1 {
# Auth magic happens here
auth_request /auth;
auth_request_set $user $upstream_http_X_User_Id;
auth_request_set $customer $upstream_http_X_Customer_Id;
auth_request_set $permissions $upstream_http_X_Permissions;
# The backend app, once Nginx has performed internal auth.
proxy_pass http://127.0.0.1:5000;
proxy_set_header X-User-Id $user;
proxy_set_header X-Customer-Id $customer;
proxy_set_header X-Permissions $permissions;
# Cache content
proxy_cache content_cache;
proxy_cache_key "$request_method-$request_uri";
}
location /api/v1/Logout {
auth_request /auth/logout;
}
}
Ora, ecco l'estratto di configurazione per l'endpoint auth interno, incluso sopra come /usr/local/etc/nginx/include-auth-internal.conf
:
# Called before every request to backend
location = /auth {
internal;
proxy_cache auth_cache;
proxy_cache_methods GET HEAD POST;
proxy_cache_key "$cookie_token";
# Valid tokens cache duration is set by backend returning a properly set Cache-Control header
# Invalid tokens are shortly cached to protect backend but not flood Nginx cache
proxy_cache_valid 401 30s;
proxy_pass http://127.0.0.1:1234/auth/_Internal;
proxy_set_header Host ........;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Accept application/json;
}
# To invalidate a not expired token, use a specific backend endpoint
# Then we cache the 401 response for the token for longer than
location = /auth/logout {
internal;
proxy_cache auth_cache;
proxy_cache_key "$cookie_token";
# Proper caching duration (> token expire date) set by backend, which will override below default duration
proxy_cache_valid 401 30m;
# A Logout requests forces a cache refresh in order to store a 401 where there was previously a valid authorization
proxy_cache_bypass 1;
# This backend endpoint always returns 401, with a cache header set to the expire date of the token
proxy_pass http://127.0.0.1:1234/auth/_Internal/Logout;
proxy_set_header Host ........;
proxy_pass_request_body off;
}
.
Indirizzamento del contenuto che serve
Ora l'autenticazione è separata dai dati. Poiché hai detto che era identico per ogni utente, il contenuto stesso può anche essere memorizzato nella cache da Nginx (nel mio esempio, nella zona content_cache
).
Qualsiasi commento sarebbe molto apprezzato e apprezzato!