Parser differential in web and API authorization
Авторская схема Virusologia: parse input, verify node, consume claim и regression corpus.
Коротко: Если security validation и business logic парсят вход по-разному, у приложения две реальности. Безопасная реальность должна быть единственной, которую код может consume.

Проблема

Современный web-stack прогоняет данные через proxies, frameworks, SSO libraries, JSON/XML parsers, API gateways и business services. Каждый слой может по-своему нормализовать paths, headers, encodings или assertions.

Так появляется хрупкая authorization boundary: один компонент проверяет безопасный объект, а другой позже потребляет уже другое представление. Симптом может выглядеть как SAML bypass, cache confusion, role bypass, route shadowing или неожиданный API inventory drift.

Решение

Используйте same-node contract. Parse once, reject ambiguity, validate parsed structure и передавайте verified object by reference в business logic. Не парсите те же недоверенные bytes повторно ниже по стеку.

Второй контроль - regression corpus. Каждое обновление parser/library/proxy должно прогонять безопасные malformed test cases, доказывающие, что приложение всё ещё отбрасывает ambiguity.

Что проверить руками

  • Где authentication data парсится, где verify, и где claims consume.
  • Согласованы ли proxies и application frameworks по host, path, method и encoded characters.
  • Отдает ли SSO library verified node, а не заставляет application code заново искать его в документе.
  • Есть ли для каждого authentication parser upgrade безопасный malformed-input regression set.

Безопасный пример

Небольшой contract test: business logic должна consume verified object, а не raw user input.

class VerifiedClaims(dict):
    pass

def verify_claims(raw_assertion: str) -> VerifiedClaims:
    parsed = parse_once_and_reject_ambiguity(raw_assertion)
    if not signature_is_valid(parsed):
        raise ValueError("invalid assertion")
    return VerifiedClaims(user_id=parsed["user_id"], role=parsed["role"])

def authorize_account_view(claims: VerifiedClaims, account_owner_id: str) -> bool:
    if not isinstance(claims, VerifiedClaims):
        raise TypeError("business logic requires verified claims")
    return claims["user_id"] == account_owner_id or claims["role"] == "admin"

Критерий ретеста

  • Malformed SSO и routing fixtures отклоняются до попадания claims в business logic.
  • Приложение логирует parser decision, verified object ID и rejection reason без сохранения secrets.
  • Library или proxy upgrade не merge, пока regression corpus не проходит.
Первичные материалы

Материал написан Virusologia с нуля как авторская инженерная интерпретация. Факты сверены по первичным публикациям; формулировки и практические выводы не копируют источники.

Этическая рамка

Материал предназначен для defensive architecture reviews, owned labs и разрешенных аудитов. Он не содержит operational exploit payloads.