10.10. Вхід для панелі керування¶
Веб-панелі керування потрібна форма входу – сторонні користувачі в локальній мережі не повинні бачити двір. Саме для цього призначені сесії на основі cookie та декоратор входу.
Сесія – це невеликий словник, який камера записує в cookie. Cookie підписується секретом JWT, завантаженим раніше в цьому розділі, тому браузер може зберігати його, але не може змінити вміст, не порушивши підпис.
10.10.1. Налаштування об’єктів сесії та входу¶
microdot.session.Session встановлює механізм сесій для app. microdot.login.Login додає декоратор у стилі login_required та допоміжні функції 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 не дозволяє JavaScript на сторінці читати cookie – це багаторівневий захист проти викрадення сесії XSS-зловмисником. secure=False є заповнювачем до впровадження HTTPS; змініть на True, коли сервер працюватиме через TLS, щоб cookie ніколи не передавалося через незахищений HTTP.
Примітка
Міжсайтовий скриптинг (XSS) – це клас атак, при яких зловмисник виконує JavaScript у браузері користувача на довіреній сторінці – зазвичай через неекранізоване поле форми, HTML-коментар або вразливий сторонній віджет. Прапорець cookie http_only не запобігає XSS; він просто приховує сесійний cookie від впровадженого скрипту, щоб успішний XSS не міг легко перетворитися на викрадення сесії.
user_loader викликається під час кожного захищеного запиту, щоб перетворити ідентифікатор, збережений у сесії, на запис користувача, який побачить обробник. Тримайте його легким – він виконується на гарячому шляху.
10.10.2. Форма входу, POST входу, вихід¶
Сама форма входу – це статична сторінка, що обслуговується з /sdcard/static/, як і панель керування. Форма надсилає POSTна /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() записує сесійний cookie і повертає перенаправлення 302 на сторінку, яку клієнт спочатку намагався відкрити (аргумент запиту next=, або / за замовчуванням). remember=True також записує довготривалий cookie _remember, щоб сесія зберігалася після перезапуску браузера.
microdot.login.Login.logout_user() очищає обидва cookies, а подальше перенаправлення повертає браузер до форми.
10.10.3. Захист панелі керування¶
Декорувати кожен маршрут панелі керування за допомогою @login, щоб неавтентифікований запит отримував перенаправлення 302 на /login замість вмісту:
@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):
...
Кінцеві точки API на основі токенів (/api/login, /api/ack) залишаються токен-орієнтованими – вони призначені для мобільного додатку, а не для браузера. Аутентифікація за токеном і аутентифікація за сесією чудово співіснують на одному app.
10.10.4. Свіжі та запам’ятовані сесії¶
Cookie _remember зберігає вхід користувача після перезапуску браузера, але це слабша форма аутентифікації – браузер міг бути залишений у кав’ярні. Для маршрутів, що змінюють паролі, реєструють токени API або виконують будь-що інше, що вимагає повторної аутентифікації, декоруйте за допомогою @login.fresh замість @login. Свіжий вхід – це той, де користувач ввів свій пароль у цій сесії; запам’ятований вхід – ні. На панелі керування немає нічого, що відповідало б цьому критерію, але декоратор є, коли він потрібний.
Тепер панель керування вимагає входу перед тим, як будь-який маршрут відповідатиме.