Уявімо ваш API як двері в ваш дім. Якщо двері завжди відчинені і будь-хто може зайти, то це доволі ризикована ситуація. У контексті API це загрожує тим, що зловмисники зможуть отримати доступ до даних вашого застосунку або спричинити небажані зміни.
Токени безпеки — це ключі, які надають користувачам (або системам) для того, щоб надати доступ до даних і сервісів API. Захищати API з використанням токенів — означає зачинити двері і видавати ключі лише тим, хто справді має право зайти.
Завдання токенів безпеки:
- Переконатися, що запит походить від авторизованого користувача.
- Визначити, які дії користувач може виконувати.
- Захистити ваші ендпоінти від несанкціонованого доступу.
Вступ до токенів безпеки: JWT
Найпопулярнішим варіантом для роботи з токенами в сучасних API є JSON Web Token (JWT). Це стандарт RFC 7519, який дозволяє безпечно передавати інформацію між сторонами у вигляді JSON-об'єкта.
JWT — це не просто рядок, а структурований об'єкт, який складається з трьох частин:
- Header (заголовок) — містить інформацію про тип токена і алгоритм шифрування.
- Payload (корисне навантаження) — містить дані, які ми хочемо передати (наприклад, ідентифікатор користувача).
- Signature (підпис) — використовується для перевірки того, що токен не було підроблено.
Коли ці три частини об'єднані, вони формують рядок, який виглядає приблизно так:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE2ODg4OTk5OTl9.KyJw2aKXgWU1Lp5Jd0xWJoN3sUYOZ0qGqH8GDF8JMoQ
Встановлення необхідних бібліотек
Для роботи з токенами безпеки у FastAPI потрібна бібліотека python-jose для генерації і перевірки JWT. Також знадобиться python-multipart для читання даних форми (використовується при аутентифікації).
Встановимо їх командою:
pip install python-jose python-multipart
Налаштування безпеки у FastAPI
- Створення моделі користувача
Почнемо з моделі даних для користувача. Цю модель будемо використовувати для збереження інформації про користувачів, які пройшли аутентифікацію.
from pydantic import BaseModel class User(BaseModel): username: str full_name: str | None = None email: str | None = None disabled: bool | None = NoneТепер створимо ще одну модель для передачі даних при вході користувача:
class UserInDB(User): hashed_password: strЗверніть увагу на поле
hashed_password. Ми не зберігаємо паролі у відкритому вигляді. Зберігання "хешованих" паролів — це безпечна практика. - Налаштування бази даних користувачів
Для спрощення тимчасово створимо базу користувачів у пам'яті:
db_users = { "jsmith": { "username": "jsmith", "full_name": "John Smith", "email": "jsmith@example.com", "hashed_password": "fakehashedpassword", "disabled": False, } } def get_user(db, username: str): user = db.get(username) if user: return UserInDB(**user) - Створення функцій для роботи з токенами
Для створення JWT використаємо бібліотеку
jose.from jose import JWTError, jwt from datetime import datetime, timedelta SECRET_KEY = "secret" # Використовуйте більш надійний ключ у реальних проєктах ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwtДля перевірки токенів декодуємо JWT і витягуємо дані.
def verify_token(token: str): try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise ValueError("Invalid token") return username except JWTError: raise ValueError("Invalid token") - Реалізація аутентифікації
Тепер створимо ендпоінт, на якому користувач зможе аутентифікуватися й отримати токен.
from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm app = FastAPI() oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") def authenticate_user(username: str, password: str): user = get_user(db_users, username) if not user or user.hashed_password != "fakehashedpassword": # Використовуйте bcrypt у реальних проєктах return False return user @app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): user = authenticate_user(form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) access_token = create_access_token(data={"sub": user.username}) return {"access_token": access_token, "token_type": "bearer"} - Захист ендпоінтів з використанням токенів
Тепер можемо додати захист на ендпоінт, щоб вимагати токен для доступу.
from fastapi import Depends async def get_current_user(token: str = Depends(oauth2_scheme)): try: username = verify_token(token) user = get_user(db_users, username) if user is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) return user except ValueError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token", headers={"WWW-Authenticate": "Bearer"}, ) @app.get("/users/me") async def read_users_me(current_user: User = Depends(get_current_user)): return current_user
Корисні поради та практичне застосування
Токени безпеки використовуються повсюдно. Наприклад:
- У e-commerce платформах для захисту особистого кабінету.
- У корпоративних застосунках для розмежування прав доступу.
- У мікросервісній архітектурі для авторизації між сервісами.
Зверніть увагу, що:
- Ніколи не використовуйте простий текстовий пароль. Використовуйте бібліотеки на кшталт
bcryptабоargon2для хешування. - Токени мають строк дії. Оновлюйте їх за допомогою refresh-токенів.
- Не передавайте токени через незахищені з’єднання (використовуйте HTTPS).
Тепер у нас є базова налаштування безпеки FastAPI з токенами. Ми зробили перший крок до того, щоб не тільки створювати, а й захищати наші API.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ