10.10. Bejelentkezés az irányítópultra

A webes irányítópultnak szüksége van egy bejelentkezési űrlapra – a LAN-on lévő idegeneknek nem szabad látniuk az udvart. Pontosan ezt teszik a cookie-alapú munkamenetek és a bejelentkezési dekorátor.

A munkamenet egy kis dict, amelyet a kamera egy cookie-ba ír. A cookie-t a fejezet korábbi részében betöltött JWT-aláíró titkos kulccsal írja alá, így a böngésző magával hordozhatja, de a tartalmat nem módosíthatja anélkül, hogy az aláírás érvénytelenné ne válna.

10.10.1. A munkamenet- és bejelentkezési objektumok beállítása

microdot.session.Session telepíti a munkamenet-kezelő mechanizmust az app objektumra. microdot.login.Login hozzáadja a login_required stílusú dekorátort, valamint a login_user / logout_user segédfüggvényeket:

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

A http_only=True megakadályozza, hogy az oldalon futó JavaScript olvassa a cookie-t – ez egy rétegzett védelem az XSS támadók ellen, akik eltéríthetnék a munkamenetet. A secure=False csak átmeneti helyőrző, amíg a HTTPS nincs a helyén; állítsd True értékre, amint a szerver TLS felett fut, hogy a cookie soha ne utazzon sima HTTP-n keresztül.

Megjegyzés

A cross-site scripting (XSS) támadások azon osztálya, ahol a támadó JavaScript-et futtat egy megbízható oldal felhasználói nézetén belül – jellemzően egy nem megfelelően kezelt űrlapmező, egy HTML-ként megjelenített hozzászólás vagy egy sebezhető harmadik féltől származó widget révén. A http_only cookie-jelző nem akadályozza meg az XSS-t; pusztán távol tartja a munkamenet-cookie-t a beinjektált szkripttől, így egy sikeres XSS nem alakítható át triviálisan munkamenet-eltérítéssé.

A user_loader minden védett kérésnél meghívásra kerül, hogy a munkamenetben tárolt azonosítót átalakítsa a kezelő által látott felhasználói rekordra. Tartsd olcsón – a forró útvonalon fut.

10.10.2. Bejelentkezési űrlap, bejelentkezési post, kijelentkezés

Maga a bejelentkezési űrlap egy statikus oldal, amelyet a /sdcard/static/ könyvtárból szolgálunk ki, ugyanúgy, mint az irányítópultot. Az űrlap a /login címre POSTol:

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() kiírja a munkamenet-cookie-t, és egy 302-es átirányítást ad vissza arra az oldalra, amelyet a kliens eredetileg el akart érni (next= lekérdezési argumentum, /-re visszaesve). A remember=True egy hosszabb élettartamú _remember cookie-t is kiír, így a munkamenet túléli a böngésző újraindítását.

microdot.login.Login.logout_user() törli mindkét cookie-t, és egy ezt követő átirányítás visszaküldi a böngészőt az űrlapra.

10.10.3. Az irányítópult védelme

Dekoráld minden irányítópult felé néző útvonalat a @login dekorátorral, hogy egy hitelesítetlen kérés a tartalom helyett egy 302-es átirányítást kapjon a /login címre:

@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):
    ...

A token-alapú API-végpontok (/api/login, /api/ack) token-alapúak maradnak – ezek a telefonos alkalmazáshoz valók, nem a böngészőhöz. A token-hitelesítés és a munkamenet-hitelesítés békésen megfér egymás mellett ugyanazon az app objektumon.

10.10.4. Friss vs. emlékezett munkamenetek

Az _remember cookie a böngésző újraindításain át bejelentkezve tartja a felhasználót, de ez a hitelesítés gyengébb formája – a böngészőt egy kávézóban is ott hagyhatták. Azon útvonalaknál, amelyek jelszót változtatnak, API-tokeneket regisztrálnak, vagy bármi mást tesznek, amiért érdemes újrahitelesíteni, a @login helyett a @login.fresh dekorátort használd. A friss bejelentkezés az, ahol a felhasználó ebben a munkamenetben gépelte be a jelszavát; egy emlékezett bejelentkezés nem az. Az irányítópulton nincs semmi, ami elérné ezt a szintet, de a dekorátor ott van, amikor szükséged van rá.

Az irányítópult most már bejelentkezést igényel, mielőtt bármelyik útvonala válaszolna.