← Назад к вопросам

Всегда ли нужно валидировать только POST запрос

2.0 Middle🔥 121 комментариев
#REST API и HTTP#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Валидация запросов: нужна ли она для всех HTTP методов?

Короткий ответ: ДА, валидация нужна для ВСЕХ запросов, не только для POST. Это распространённое заблуждение, которое приводит к уязвимостям.

1. Почему только POST — неправильно?

Проблема

Есть мнение что валидация нужна только для:

  • POST (создание данных)
  • PUT/PATCH (обновление данных)

А для GET/DELETE якобы не нужна.

Реальность

Все методы принимают пользовательский ввод, и всё это нужно валидировать!

2. GET запросы тоже нужно валидировать

Пример: Уязвимость в GET

from fastapi import FastAPI, Query

app = FastAPI()

# НЕПРАВИЛЬНО: Валидация отсутствует
@app.get("/users")
def get_users(user_id: int = Query(...)):  # Может быть любое число
    # user_id = -1
    # user_id = 999999
    # user_id = 0
    # user_id = None
    user = db.query(User).filter(User.id == user_id).first()
    return user

# Проблемы:
# - SQL injection (если неправильно сделан запрос)
# - Утечка информации (можно перебрать все ID)
# - DoS (очень большой ID может вызвать проблемы)

# ПРАВИЛЬНО: С валидацией
@app.get("/users")
def get_users(user_id: int = Query(..., gt=0, le=1000000)):
    # Гарантирует: 0 < user_id <= 1000000
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        return {"error": "Not found"}
    return user

Пример: Query параметры

from fastapi import FastAPI, Query
from pydantic import validator

app = FastAPI()

# НЕПРАВИЛЬНО: Нет валидации
@app.get("/search")
def search(query: str = Query(...)):
    # query может быть:
    # - пустой строкой
    # - очень длинной (DoS)
    # - содержать SQL injection
    results = db.raw_query(f"SELECT * FROM users WHERE name LIKE '%{query}%'")
    return results

# ПРАВИЛЬНО: С валидацией
@app.get("/search")
def search(query: str = Query(..., min_length=1, max_length=100)):
    # Гарантирует: 1 <= len(query) <= 100
    # Используем parameterized query!
    results = db.query(User).filter(
        User.name.contains(query)
    ).limit(50).all()
    return results

3. DELETE запросы тоже нужно валидировать

Пример: DELETE уязвимость

# НЕПРАВИЛЬНО: Нет валидации
@app.delete("/users/{user_id}")
def delete_user(user_id: int):
    # user_id может быть:
    # - -1 (ошибка в БД, удалит что-то неправильное)
    # - 0 (root admin?)
    # - очень большое число (ошибка)
    db.query(User).filter(User.id == user_id).delete()
    return {"status": "deleted"}

# Проблемы:
# - Можно удалить любого пользователя
# - Нет проверки авторизации
# - Нет проверки существования

# ПРАВИЛЬНО: С валидацией и авторизацией
@app.delete("/users/{user_id}")
def delete_user(user_id: int, current_user: User = Depends(get_current_user)):
    # Валидация: user_id валиден?
    if user_id <= 0:
        return {"error": "Invalid user_id"}
    
    # Авторизация: имеет ли право удалить?
    if user_id != current_user.id and not current_user.is_admin:
        return {"error": "Forbidden"}
    
    # Проверка существования
    user = db.query(User).get(user_id)
    if not user:
        return {"error": "Not found"}
    
    db.delete(user)
    db.commit()
    return {"status": "deleted"}

4. Что нужно валидировать

Для всех методов

from fastapi import FastAPI, Path, Query
from pydantic import BaseModel, validator

# 1. Path параметры
@app.get("/items/{item_id}")
def get_item(item_id: int = Path(..., gt=0)):  # item_id > 0
    pass

# 2. Query параметры
@app.get("/search")
def search(query: str = Query(..., min_length=1, max_length=100)):
    pass

# 3. Headers
@app.get("/data")
def get_data(user_agent: str = Header(...)):
    # Проверить формат user-agent
    if "bot" in user_agent.lower():
        return {"error": "Bots not allowed"}
    pass

# 4. Body для POST/PUT
class UserCreate(BaseModel):
    email: str
    password: str
    
    @validator("email")
    def validate_email(cls, v):
        if "@" not in v:
            raise ValueError("Invalid email")
        return v
    
    @validator("password")
    def validate_password(cls, v):
        if len(v) < 8:
            raise ValueError("Password too short")
        return v

@app.post("/users")
def create_user(user: UserCreate):
    # user уже валидирован благодаря Pydantic
    pass

5. Типы валидации

from pydantic import BaseModel, Field, validator, EmailStr
import re

class UserCreate(BaseModel):
    # 1. Type validation (встроено)
    age: int  # Должно быть число
    
    # 2. Range validation
    age: int = Field(..., ge=18, le=120)  # 18 <= age <= 120
    
    # 3. Length validation
    password: str = Field(..., min_length=8, max_length=100)
    
    # 4. Pattern validation (regex)
    phone: str = Field(..., regex=r"^\+?1?\d{9,15}$")
    
    # 5. Email validation
    email: EmailStr
    
    # 6. Custom validation
    @validator("password")
    def validate_password(cls, v):
        if not any(char.isupper() for char in v):
            raise ValueError("Password must contain uppercase")
        return v
    
    # 7. Cross-field validation
    @validator("confirm_password")
    def validate_confirm(cls, v, values):
        if "password" in values and v != values["password"]:
            raise ValueError("Passwords don't match")
        return v

6. На уровне БД (дополнительно)

# Всегда добавляй ограничения на уровне БД

from sqlalchemy import Column, Integer, String, CheckConstraint

class User(Base):
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True)
    age = Column(Integer, CheckConstraint("age >= 18 and age <= 120"))
    email = Column(String(255), unique=True, nullable=False)
    password = Column(String(255), nullable=False)
    
    # Проверка на уровне БД
    __table_args__ = (
        CheckConstraint("length(password) >= 8"),
    )

7. Чеклист валидации

  • Path параметры валидированы?
  • Query параметры валидированы?
  • Body параметры валидированы?
  • Headers проверены?
  • Типы данных проверены?
  • Диапазоны значений проверены?
  • Формат строк проверен (email, phone, UUID)?
  • Авторизация проверена?
  • SQL injection защита (parameterized queries)?
  • Rate limiting установлен?

Резюме

Валидация нужна для ВСЕХ HTTP методов:

  • GET — валидировать query параметры, path параметры
  • POST — валидировать body
  • PUT/PATCH — валидировать body и path параметры
  • DELETE — валидировать path параметры и авторизацию
  • HEAD/OPTIONS — менее критично но желательно

Правило: Никогда не доверяй пользовательскому вводу, независимо от HTTP метода.

Всегда ли нужно валидировать только POST запрос | PrepBro