Tvorba internetových aplikací

Jakub Klinkovský

:: České vysoké učení technické v Praze
:: Fakulta jaderná a fyzikálně inženýrská
:: Katedra softwarového inženýrství

Akademický rok 2023-2024

Authentication and authorization

  1. Authentication (ověření, autentizace) – proces ověření pravosti dané věci
    (v kontextu softwarových architektur typicky uživatele)

    Provádí se na základě jednoho nebo kombinace různých ověřovacích faktorů:

    • znalostní: např. heslo, PIN
    • vlastnické: např. bezpečnostní token, čipová karta, mobilní telefon
    • biometrické: např. otisk prstu, snímek oční duhovky, DNA, Face ID
  2. Authorization (autorizace, povolení) – proces získání souhlasu s provedením nějaké operace nebo povolení přístupu někam

    • autentizace se často provádí před autorizací, ale není to pravidlo
      (např. pro některé operace hraje roli čas, ale ne identita uživatele)

HTTP authentication

HTTP authentication je nejstarší metoda pro ověřování přístupu na webu.

  1. Po prvním, neověřeném požadavku server odešle odpověď se stavovým kódem 401 (Unauthorized) a hlavičkou WWW-Authenticate: <type> <params..>
  2. Prohlížeč požádá uživatele o zadání uživatelského jména a hesla
  3. Prohlížeč odešle požadavek s hlavičkou Authorization: <type> <credentials>

Poznámka: ve specifikaci hlaviček se míchají pojmy authentication a authorization 🙃

Vlastnosti:

  • jednoduchá metoda – nevyžaduje cookies, session ID ani stránky s přihlašovacím formulářem
  • není flexibilní (např. nelze modifikovat UI, není možnost odhlášení)
  • nemusí zajistit důvěrnost informací (závisí na typu autentizace)
  • vyžaduje podporu prohlížeče (pokročilé typy autentizace nemusí být podporované)
  • na straně serveru se o autentizaci stará web server (ne webová aplikace!)

Typy pro HTTP authentication

1. Basic access authentication

Prohlížeč odešle požadavek s hlavičkou Authorization: Basic <credentials>, kde <credentials> vznikne spojením <username>:<password> a zakódováním do base64.

Vlastnosti:

  • <username> nesmí obsahovat :
  • nezajišťuje důvěrnost (potřeba HTTPS)

2. Digest access authentication

Prohlížeč odešle požadavek s hlavičkou Authorization: Digest <response>, kde <response> vznikne hashováním stringu obsahujícího username, password, unikátní hodnotu specifikovanou serverem (nonce), metodu HTTP a URL stránky.

Podporované hashovací algoritmy: MD5 a od roku 2015 také SHA-256, SHA-256-sess, SHA-512-256 a SHA-512-256-sess.

Nevýhody:

  • nenabízí silné ověření (snadné útoky typu man in the middle mohou přimět klienta k odeslání jména a hesla pomocí basic metody)
  • neumožňuje serveru ukládat hesla pomocí silných hashových funkcí

3. Další typy HTTP authentication

  • Bearer (tokeny pro OAuth ověřování)
  • VAPID (pro protokol Web Push)
  • AWS4-HMAC-SHA256 (pro Amazon Web Services)

Experimentální:

  • HOBA (založen na digitálních podpisech, použitelný s JavaScriptem)
  • Mutual (zajišťuje vzájemné ověření klienta i serveru)
  • SCRAM (má zajistit větší robustnost oproti basic + HTTPS)

Autentizace implementovaná v aplikaci

V dnešní době jsou nejběžnější typy autentizace, které jsou implementovány přímo na straně webové aplikace.

Komponenty: HTML formulář, zabezpečený přenos, zpracování na straně aplikace, session cookie

Výhody:

  • větší flexibilita (možnost odhlášení, změna vzhledu UI, možnost společné identity pro několik stránek – single sign-on (SSO), např. OpenID, SAML, Shibboleth)
  • větší bezpečnost (možnost použití kryptograficky silnějších metod pro uchování a výměnu informací)

Protokoly pro autentizaci v aplikaci

Existuje spousta protokolů pro ověření uživatele:

  1. Single-factor protokoly: vyžadují pouze jednu hodnotu (např. heslo nebo PIN)
  2. Multi-factor protokoly: vyžadují několik nezávislých hodnot (např. heslo + časově omezený kód doručený přes SMS nebo vygenerovaný klientem pomocí kryptografického klíče)

Pro přenos údajů se nejčastěji používá princip PAP (password authentication protocol), kde klient odesílá přímo požadované hodnoty. To je bezpečné jen při použití protokolu HTTPS! Pokročilejší autentizační protokoly využívají kryptografické metody pro výměnu klíčů (např. password-authenticated key agreement nebo HMAC).

Authorization

Proces získání souhlasu s provedením nějaké operace nebo povolení přístupu někam.

Existuje několik strategií pro udělování přístupu, např.:

Pro zvýšení bezpečnosti je vhodné dodržovat princip nejnižších privilegií.

Ve větších aplikacích se často používají tzv. přístupové tokeny, které umožňují uživateli nebo externí aplikaci přístup s omezenou podmnožinou přiřazených rolí.
Např. standard OAuth umožňuje vzdálené ověřování přístupu (např. Google, GitHub).

Implementace ve frameworku django

Pro připomenutí (z přednášky 5): diagram zpracování požadavku a vytvoření odpovědi

center

Přehled middleware

V Django projektech jsme dosud nechali nakonfigurované defaultní middleware:

Seznam dostupného middleware najdete v dokumentaci. Externí balíčky (rozšíření) také mohou poskytovat svůj middleware.

Django session middleware

Django session middleware poskytuje funkčnost pro anonymní session. Systém se stará o zajištění:

  • identifikace návštěvníka pomocí session ID (hodnota uložená na straně klienta pomocí cookie) – abstrakce pro nastavení a kontrolu včetně expirace
  • uložení libovolných dat, kde klíč je daná session ID

Pro uložení asociovaných dat je k dispozici několik backendů:

  • databáze (defaultní) – model django.contrib.sessions.models.Session
  • cache – dočasné úložiště na straně serveru (rychlejší než databáze)
  • soubor – dočasné úložiště na serveru (typicky /tmp)
  • cookie – úložiště na straně klienta, využívá se kryptografických podpisů (ale ne šifrování)

Příklad: počítání návštěv pomocí session middleware

Viz testovací aplikace na GitLabu. Jiný příklad: MDN Web Docs

Poznámky k použití:

  • atribut request.session – chová se jako slovník s dodatečnými metodami
  • automatické uložení dat po modifikaci hodnot ve slovníku
  • atribut request.session.session_key představuje aktuální session ID (ale může se měnit z důvodu bezpečnosti)
  • model django.contrib.sessions.models.Session lze použít jako foreign key ve vlastních modelech, ale instanci tohoto modelu je potřeba získat ručně pomocí session_key

Django authentication middleware

Django authentication middleware poskytuje autentizaci uživatelů:

  • middleware přidává atribut request.user do každého požadavku – je to buď objekt AnonymousUser nebo User (oba v modulu django.contrib.auth.models)

    Primární atributy: username, password, email, first_name, last_name, is_staff (může se přihlásit do admin UI), is_superuser (má všechna práva)

  • využívá session middleware – sdílí stejné nastavení pro expiraci apod.

  • existuje několik backendů pro samotnou autentizaci (defaultní ModelBackend ukládá data přímo v databázi, RemoteUserBackend využívá externí systém)

  • obsahuje také prostředky pro autorizaci – modely Permission a Group (ale Permission lze přiřadit i přímo objektu User)

Správa uživatelských účtů

Uživatelské účty lze modifikovat pomocí admin rozhraní přímo ve webové aplikaci.
K tomu je potřeba účet s atributem superuser, který lze vytvořit v terminálu příkazem python manage.py createsuperuser.

Akce lze provést i přímo v programu. Např. vytvoření nového uživatele:

from django.contrib.auth.models import User
user = User.objects.create_user("john", password="johnpassword")
user.last_name = "Lennon"
user.save()

Nebo změna hesla:

u = User.objects.get(username="john")
u.set_password("new password")
u.save()

Uložení hesla v databázi

Atribut password uložený v databázi neobsahuje samotné heslo, ale string získaný pomocí kryptografické hashovací funkce ve tvaru

<algorithm>$<iterations>$<salt>$<hash>

Defaultní algoritmus je PBKDF2, ale Django podporuje i jiné algoritmy (Argon2, bcrypt, scrypt). Typický problém při správě hesel je ale migrace na novější algoritmus, která vyžaduje akci každého uživatele (probíhá při přihlášení). Viz dokumentace.

Validace kvality hesla

Django poskytuje několik validátorů kvality hesel, které lze nastavit pomocí proměnné AUTH_PASSWORD_VALIDATORS v souboru settings.py:

  • UserAttributeSimilarityValidator kontroluje podobnost mezi heslem a
    atributy uživatele
  • MinimumLengthValidator kontroluje minimální délku hesla (lze nastavit)
  • CommonPasswordValidator porovnává se seznamem 20000 častých hesel
  • NumericPasswordValidator kontroluje, jestli heslo neobsahuje samé číslice

Kromě toho lze definovat vlastní validátory.

Přihlášení uživatele

Pomocí funkcí login a authenticate:

from django.contrib.auth import authenticate, login

def my_view(request):
    username = request.POST["username"]
    password = request.POST["password"]
    user = authenticate(request, username=username, password=password)
    if user is not None:
        login(request, user)
        # Redirect to a success page.
        ...
    else:
        # Return an 'invalid login' error message.
        ...
  • authenticate ověří údaje a vrátí objekt User
  • login přiřadí uživatele pro session daného požadavku (nastaví cookies apod.)

Úrovně využití authentication middleware

Funkce a objekty z authentication middleware lze využít na několika úrovních:

1. Low-level funkce

Např. zmíněné funkce authenticate a login.

  • low-level přístup umožňuje libovolným způsobem modifikovat vzhled a chování aplikace
  • např. přihlašovací stránku včetně formuláře je nutné definovat ručně
  • náchylný na duplikaci kódu a bezpečnostní chyby

Další pomocná funkce: redirect_to_login – pro přesměrování anonymního uživatele na přihlašovací stránku, po úspěšném přihlášení další přesměrování na původní stránku (přihlašovací formulář to musí podporovat).

2. Mid-level: využití připravených formulářů

Django poskytuje spoustu built-in formulářů pro autentizaci (používají se v admin UI):

  • AuthenticationForm – přihlašovací formulář (zadání jména a hesla)
  • PasswordChangeForm – změna hesla (vyžaduje zadat aktuální heslo)
  • SetPasswordForm – změna hesla (nevyžaduje zadat aktuální heslo)
  • UserCreationForm – pro vytvoření nového uživatele
  • a další...

Tyto formuláře je možné využít ve vlastních view funkcích a šablonách.

3. High-level: využití kompletních view

Pro nejběžnější použití authentication middleware poskytuje několik view pro
zpracování přihlášení, odhlášení a nastavení hesla.

Základní použití: includování všech view s defaultním mapováním na URL v urls.py:

urlpatterns = [
    path("accounts/", include("django.contrib.auth.urls")),
]

Zde jsme použili globální cestu accounts/. Tím vznikne mapování těchto URL vzorů:

accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']

3. High-level: selektivní použití authentication view

If you want more control over your URLs, you can reference a specific view in your URLconf:

from django.contrib.auth import views as auth_views

urlpatterns = [
    path("change-password/", auth_views.PasswordChangeView.as_view()),
]

Pomocí klíčového parametru template_name lze změnit šablonu pro dané view:

urlpatterns = [
    path(
        "change-password/",
        auth_views.PasswordChangeView.as_view(template_name="change-password.html"),
    ),
]

3. High-level: modifikace authentication view

Všechny authentication view jsou třídy, které je možné zdědit a modifikovat nebo rozšířit jejich funkčnost:

  • LoginView
  • LogoutView
  • PasswordChangeView
  • PasswordChangeDoneView
  • PasswordResetView
  • PasswordResetDoneView
  • PasswordResetConfirmView
  • PasswordResetCompleteView

Podrobnosti najdete v dokumentaci.

Použití autentizace v aplikaci: omezení přístupu k view

Pro omezení přístupu k view lze použít 2 přístupy (viz dokumentace). V obou je potřeba nastavit LOGIN_URL v souboru settings.py.

  1. Ověření atributu is_authenticated v kódu funkce, např.:
    from django.conf import settings
    from django.shortcuts import redirect
    
    def my_view(request):
        if not request.user.is_authenticated:
            return redirect(f"{settings.LOGIN_URL}?next={request.path}")
        ...
    
  2. Použití dekorátoru login_required (má také parametry, např. login_url):
    from django.contrib.auth.decorators import login_required
    
    @login_required
    def my_view(request):
        ...
    

Obecný dekorátor pro ověření uživatele

Obecně lze pro uživatele kontrolovat libovolnou podmínku, např.:

from django.conf import settings
from django.shortcuts import redirect

def my_view(request):
    if not request.user.email.endswith("@example.com"):
        return redirect(f"{settings.LOGIN_URL}?next={request.path}")
    ...

Totéž lze docílit pomocí dekorátoru user_passes_test (má i další keyword parametry):

from django.contrib.auth.decorators import user_passes_test

def email_check(user):
    return user.email.endswith("@example.com")

@user_passes_test(email_check)
def my_view(request):
    ...

Autorizace pomocí modelu Permission

Dekorátor permission_required kontroluje oprávnění pro daného uživatele pomocí metody User.has_perm() – propojení modelů User, Group a Permission:

from django.contrib.auth.decorators import permission_required

@permission_required("app_name.permission_name")
def my_view(request):
    ...

Další parametry dekorátoru jsou login_url a raise_exception (defaultně False). Vyvolání výjimky způsobí chybu HTTP 403 (Forbidden) místo přesměrování.
Dekorátory lze řetězit, např.:

from django.contrib.auth.decorators import login_required, permission_required

@login_required
@permission_required("app_name.permission_name", raise_exception=True)
def my_view(request):
    ...

Autentizace a autorizace v šablonách

S defaultním nastavením šablonového systému jsou v contextu při renderování automaticky k dispozici proměnné user a perms týkající se autentizace a autorizace.

Příklad autentizace:

{% if user.is_authenticated %}
    <p>Welcome, {{ user.username }}. Thanks for logging in.</p>
{% else %}
    <p>Welcome, new user. Please log in.</p>
{% endif %}

Příklad autorizace pomocí proměnné perms najdete v dokumentaci.

Shrnutí

Authentication = ověření uživatele, authorization = ověření přístupu

Django authentication middleware poskytuje funkce a objekty pro autentizaci
(i autorizaci):

  • modely User, Group, Permission
  • low-level funkce authenticate a login
  • předpřipravené formuláře (AuthenticationForm a další)
  • modifikovatelné view (LoginView a další)
  • dekorátory pro vlastní view funkce
  • proměnné user a perms v šablonách

Nástroje pro správu uživatelů a oprávnění

Pro správu uživatelů a oprávnění jsou možné tři přístupy nebo jejich kombinace:

  1. definice vlastních funkcí/view
  2. interakce s admin UI
  3. použití externích balíčků

Externí balíčky (viz djangopackages.org):