Un possibile metodo di limitazione delle sessioni a una singola scheda comporta la creazione di un token casuale al caricamento della pagina e l'incorporamento di questo token nella pagina. Questo token generato più di recente viene memorizzato anche nella sessione dell'utente. Questo sarà simile al modo in cui i vari framework aggiungono i token di convalida per prevenire gli attacchi CSFR.
Breve esempio: pagina carichi
- utente nella scheda 1 in Firefox.
Token1
viene generato, incorporato e memorizzato nella sessione
- L'utente carica la pagina nella scheda 2 in Firefox.
Token2
viene generato, incorporato e archiviato in sessione. Questo sovrascrive il valore precedente.
- L'utente carica la pagina nella scheda 1 in Chrome.
Token3
viene generato, incorporato e archiviato in sessione. questo sovrascrive il valore precedente.
A questo punto, l'utente ha la pagina aperta in 3 schede. La sessione dell'utente, tuttavia, ha solo Token3
memorizzati. Questo metodo impedisce all'utente di essere bloccato (diversi indirizzi IP, diverse stringhe di user agent, modalità di navigazione in incogneto, ecc.) Perché ogni nuova sessione genera semplicemente un nuovo token. Il carico più recente diventa la finestra attiva, invalidando immediatamente tutte le sessioni precedenti.
Successivamente, ogni volta che la pagina interagisce con il server (fa clic su un collegamento, invia dati, ecc.), Viene inviato anche il token incorporato nella pagina. Il server convalida che il token passato corrisponde al token nella sessione. Se corrispondono, l'azione ha successo. Se non corrispondono, il server restituisce un messaggio di errore.
È possibile generare numeri casuali in più modi, ma probabilmente si desidera qualcosa di sicuro.Useremo il example da un'altra domanda:
import string
import random
...
N = 20 # Length of the string we want, adjust as appropriate
''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))
Questo utilizza random.SystemRandom
, che è più sicuro di semplicemente utilizzando random.choice
al caricamento della pagina, è necessario controllare se il token esistente è valido , genera il token casuale e lo memorizza nella sessione dell'utente. Dal momento che lo vogliamo ovunque, facciamo prima un decoratore, per ridurre il codice duplicato in un secondo momento. Il decoratore controlla se la sessione è valida e se non è possibile selezionare cosa fare (inserire la propria logica). Imposta anche un token di sessione. Questo è necessario (o hai bisogno di logica per escludere la tua pagina principale) altrimenti colpirai un ciclo infinito in cui l'utente tenta di caricare la pagina principale, non ha un token, fallisce e il processo si ripete. Ho il token che si rigenera su ogni caricamento della pagina tramite la clausola else
. Se non si implementa la sezione if
, questo decoratore è inutile in quanto entrambi i percorsi eseguono la stessa azione e semplicemente ripristinano il token al caricamento della pagina. La logica nel if
è ciò che impedirà all'utente di avere più sessioni.
from flask import session
from functools import wraps
def random_string(length):
return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(length))
def validate_token(f):
@wraps(f)
def wrapper(existing_token, *args, **kwargs):
if session['token'] != existing_token:
# Logic on failure. Do you present a 404, do you bounce them back to your main page, do you do something else?
# It is IMPORTANT that you determine and implement this logic
# otherwise the decorator simply changes the token (and behaves the same way as the else block).
session['token'] = random_string(20)
else:
session['token'] = random_string(20)
return f(*args, **kwargs)
return wrapper
Now nei nostri percorsi, si può applicare questa decoratore a ciascuno, in modo che la sessione utente viene aggiornato ad ogni caricamento della pagina:
from flask import render_template
@app.route('/path')
@validate_token
def path(token=None):
return render_template('path.html', token=session['token'])
Nel modello, si desidera utilizzare questo valore token
ovunque sia necessario per impedire che la sessione continui. Ad esempio, mettilo su collegamenti, in moduli (anche se Flask has a method of CSRF protection già), ecc. Il server stesso può controllare se il token passato è valido. Il modello potrebbe essere così semplice:
<a href="{{ url_for('path', token=token) }}">Click here to continue</a>
Sei a conoscenza di un modo per farlo usando la cache? – user2601010
No ... Non ho mai nemmeno usato Flask in un vero progetto, sono più un utente di Django. Detto questo, non vedo davvero perché il caching sarebbe un problema, – BriceP