10.10. Kontrol paneli için oturum açma

Web kontrol panelinin bir oturum açma formuna ihtiyacı vardır; LAN üzerindeki rastgele kişiler bahçeyi görmemelidir. İşte çerez tabanlı oturumların ve oturum açma dekoratörünün yaptığı şey budur.

Oturum (session), kameranın bir çereze yazdığı küçük bir sözlüktür. Çerez, bu bölümde daha önce yüklenen JWT imzalama gizli anahtarıyla imzalanır; böylece tarayıcı bunu taşıyabilir ancak imzayı geçersiz kılmadan içeriği değiştiremez.

10.10.1. Oturum ve oturum açma nesnelerini ayarlama

microdot.session.Session, oturum mekanizmasını app üzerine kurar. microdot.login.Login, login_required tarzı dekoratörü ve login_user / logout_user yardımcılarını ekler:

# 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, sayfadaki JavaScript’in çerezi okumasını engeller; bu, oturumu ele geçiren bir XSS saldırganına karşı katmanlı bir savunmadır. secure=False, HTTPS devreye girene kadar bir yer tutucudur; sunucu TLS üzerinden çalıştığında bunu True yapın ki çerez asla düz HTTP üzerinden gitmesin.

Not

Siteler arası betik çalıştırma (XSS), saldırganın güvenilir bir sayfanın kullanıcı görünümü içinde JavaScript çalıştırmayı başardığı saldırı sınıfıdır; tipik olarak kaçırılmamış (escape edilmemiş) bir form alanı, HTML olarak işlenmiş bir yorum veya savunmasız bir üçüncü taraf widget’ı üzerinden gerçekleşir. http_only çerez bayrağı XSS’i önlemez; yalnızca oturum çerezini enjekte edilen betiğin erişiminden uzak tutar, böylece başarılı bir XSS kolayca oturum ele geçirmeye dönüştürülemez.

user_loader, korunan her istekte çağrılır ve oturumda saklanan kimliği işleyicinin göreceği kullanıcı kaydına dönüştürür. Bunu hafif tutun; sıcak yolda (hot path) çalışır.

10.10.2. Oturum açma formu, oturum açma gönderimi, oturumu kapatma

Oturum açma formunun kendisi, tıpkı kontrol paneli gibi /sdcard/static/ dizininden sunulan statik bir sayfadır. Form, /login adresine POST eder:

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(), oturum çerezini yazar ve istemcinin başlangıçta ulaşmaya çalıştığı sayfaya (next= sorgu argümanı, yoksa /) 302 yönlendirmesi döndürür. remember=True ayrıca daha uzun ömürlü bir _remember çerezi yazar, böylece oturum bir tarayıcı yeniden başlatmasını atlatır.

microdot.login.Login.logout_user() her iki çerezi de temizler ve ardından gelen bir yönlendirme tarayıcıyı forma geri gönderir.

10.10.3. Kontrol panelini koruma

Kimliği doğrulanmamış bir isteğin içerik yerine /login adresine 302 alması için, kontrol paneline dönük her rotayı @login ile dekore edin:

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

Token tabanlı API uç noktaları (/api/login, /api/ack) token tabanlı kalır; bunlar tarayıcı için değil, telefon uygulaması içindir. Token kimlik doğrulaması ile oturum kimlik doğrulaması aynı app üzerinde sorunsuzca bir arada bulunur.

10.10.4. Taze oturumlar ile hatırlanan oturumlar

_remember çerezi, kullanıcının tarayıcı yeniden başlatmaları boyunca oturumda kalmasını sağlar, ancak bu daha zayıf bir kimlik doğrulama biçimidir; tarayıcı bir kafede bırakılmış olabilir. Parola değiştiren, API token’ları kaydeden veya yeniden kimlik doğrulamaya değecek başka bir şey yapan rotalar için, @login yerine @login.fresh ile dekore edin. Taze bir oturum açma, kullanıcının parolasını bu oturumda yazdığı oturumdur; hatırlanan bir oturum açma değildir. Kontrol panelinde bu eşiğe ulaşan bir şey yoktur, ancak ihtiyaç duyduğunuzda dekoratör oradadır.

Kontrol paneli artık rotalarından herhangi biri yanıt vermeden önce oturum açılmasını gerektirir.