10.10. Přihlášení k dashboardu¶
Webový dashboard potřebuje přihlašovací formulář – náhodní lidé v LAN by neměli vidět dvorek. Přesně to zajišťují relace založené na cookies a přihlašovací dekorátor.
Relace (session) je malý slovník, který kamera zapíše do cookie. Cookie je podepsaná tajným klíčem pro podepisování JWT načteným dříve v této kapitole, takže ji prohlížeč může nosit s sebou, ale nemůže měnit její obsah, aniž by zneplatnil podpis.
10.10.1. Nastavení objektů session a login¶
microdot.session.Session nainstaluje mechanismus relací do app. microdot.login.Login přidává dekorátor ve stylu login_required a pomocné funkce login_user / logout_user:
# auth/login.py
from microdot.session import Session
from microdot.login import Login
Session(app, secret_key=SECRET,
cookie_options={'http_only': True, 'secure': False})
login = Login()
USERS = {
'owner': {'id': 'owner', 'password_hash': load_password_hash()},
}
@login.user_loader
async def load_user(user_id):
return USERS.get(user_id)
http_only=True zabrání JavaScriptu na stránce ve čtení cookie – vrstvená obrana proti útočníkovi využívajícímu XSS k únosu relace. secure=False je zástupný symbol, dokud není zavedeno HTTPS; jakmile server poběží přes TLS, přepněte jej na True, aby cookie nikdy neputovala přes nešifrované HTTP.
Poznámka
Cross-site scripting (XSS) je třída útoků, při nichž útočník dosáhne spuštění JavaScriptu uvnitř uživatelova zobrazení důvěryhodné stránky – typicky prostřednictvím neošetřeného pole formuláře, komentáře vykresleného jako HTML nebo zranitelného widgetu třetí strany. Příznak cookie http_only nezabraňuje XSS; pouze drží cookie relace mimo dosah vloženého skriptu, takže úspěšné XSS nelze triviálně přeměnit na únos relace.
user_loader se volá při každém chráněném požadavku, aby z ID uloženého v relaci vytvořil záznam uživatele, který handler uvidí. Udržujte jej levný – běží na horké cestě.
10.10.2. Přihlašovací formulář, odeslání přihlášení, odhlášení¶
Samotný přihlašovací formulář je statická stránka servírovaná z /sdcard/static/ stejně jako dashboard. Formulář provádí POST na /login:
from microdot import redirect
@app.get('/login')
async def login_form(request):
return Response.send_file('/sdcard/static/login.html')
@app.post('/login')
async def do_login(request):
form = request.form
user = USERS.get(form.get('user'))
if not user or not check_password(user, form.get('pass')):
return redirect('/login?error=1')
return await login.login_user(request, user, remember=True)
@app.post('/logout')
async def logout(request):
await login.logout_user(request)
return redirect('/login')
microdot.login.Login.login_user() zapíše cookie relace a vrátí přesměrování 302 na stránku, kterou se klient původně pokusil navštívit (argument dotazu next=, s návratem na /). remember=True navíc zapíše déle žijící cookie _remember, takže relace přežije restart prohlížeče.
microdot.login.Login.logout_user() vymaže obě cookies a následné přesměrování pošle prohlížeč zpět na formulář.
10.10.3. Ochrana dashboardu¶
Dekorujte každou cestu obsluhující dashboard pomocí @login, aby neautentizovaný požadavek dostal 302 na /login namísto obsahu:
@app.get('/<path:filename>')
@login
async def static(request, filename):
...
@app.get('/config')
@login
async def get_config(request):
...
@app.post('/config')
@login
async def set_config(request):
...
@app.get('/events')
@login
@with_sse
async def events(request, sse):
...
@app.get('/control')
@login
@with_websocket
async def control(request, ws):
...
Koncové body API založené na tokenech (/api/login, /api/ack) zůstávají založené na tokenech – jsou určeny pro mobilní aplikaci, ne pro prohlížeč. Autentizace tokenem a autentizace relací spolu vesele koexistují na téže app.
10.10.4. Čerstvé vs. zapamatované relace¶
Cookie _remember udržuje uživatele přihlášeného i po restartu prohlížeče, ale je to slabší forma autentizace – prohlížeč mohl zůstat zapnutý v kavárně. Pro cesty, které mění hesla, registrují API tokeny nebo dělají cokoli jiného, co stojí za opětovnou autentizaci, použijte místo @login dekorátor @login.fresh. Čerstvé přihlášení je takové, při kterém uživatel zadal své heslo v této relaci; zapamatované přihlášení nikoli. Dashboard nemá nic, co by dosahovalo této úrovně, ale dekorátor je k dispozici, když jej budete potřebovat.
Dashboard nyní před odpovědí kterékoli ze svých cest vyžaduje přihlášení.