Обновить

Комментарии 5

Но тем самым вы жёстко привязали свою версию JSON-RPC к HTTP, в то время, как

It is transport agnostic in that the concepts can be used within the same process, over sockets, over http, or in many various message passing environments.

Если уж вы хотите полностью соблюдать стандарт, то любые бинарные данные должны передаваться через преобразование в строково-безопасный вид, например в base64.

Замечание про дух спецификации справедливое, но фича устроена ровно наоборот: ядро протокола не тронуто. В multipart-запросе полный JSON-RPC request object едет нетронутой JSON-строкой в поле jsonrpc - method, params, id и ответ ровно те же, что и в обычном запросе. Это не «версия JSON-RPC», а опциональный транспортный биндинг, выключенный по умолчанию: метод, который его явно не объявил, отвечает -32600, а application/json работает как работал.

Привязка к HTTP - свойство самого бандла, а не этой фичи: это Symfony-бандл, который отдаёт JSON-RPC поверх HTTP. Сокеты и очереди сообщений вне его рамок независимо от multipart.

Насчёт base64 - спецификация 2.0 про бинарные данные не говорит вообще ничего, так что "должны" тут уже интерпретация. Base64 в строковом параметре работал и работает без какой-либо поддержки со стороны бандла, и в документации он прямо назван правильным ответом для мелких файлов - иконка, подпись, миниатюра. Multipart добавлен для случая, где base64 объективно плох: плюс треть к размеру на проводе и весь файл в памяти на обеих сторонах - при кодировании, при json_decode, при валидации.

как раз этот CSRF-вектор и закрывал

А вот кстати, с 2019 года и 70 версии Firefox это уже не проблема.

Любой POST запрос в браузере, включая не-корсовые и отправку формы, посылает заголовок Origin

При помощи no-referrer значения самого поля можно скрыть (заменить на null), но заставить браузер вообще не посылать Origin или как-то его подменить нельзя. Чтобы защититься от CSRF, на серверной стороне уже достаточно проверять его значение

Спасибо, по фактам всё так: с Firefox 70 (2019) Origin действительно приходит на любом браузерном POST, включая отправку формы, и страницей его не подменить - заголовок forbidden. Проверка Origin на сервере - рабочая защита, она честно указана в OWASP среди анти-CSRF-мер. Добавлю, почему в доках всё равно нет рецепта "просто проверяйте Origin": 1. JSON-RPC API редко обслуживает только браузеры. curl, мобильное приложение, серверная интеграция не шлют Origin вовсе. Политика "нет Origin - отклонить" ломает легитимных клиентов, а "нет Origin - пропустить" делает защиту работающей только для браузерного трафика. Для CSRF этого формально достаточно, но универсальным рецепт перестаёт быть.
2. Вы сами отметили деградацию до null через no-referrer: атакующий может форсировать Origin: null, а серверу он неотличим от легитимного privacy-клиента. null приходится считать недоверенным - и снова см. пункт про "просто". Плюс старая оговорка: промежуточные прокси могут вырезать заголовок целиком.
3. С 2020 года основная мера и вовсе другая - SameSite=Lax по умолчанию: cookie без явного SameSite=None на cross-site POST просто не уезжает. Поэтому статья не про то, что защиты не существует, а про зону ответственности: бандл ограничивает радиус двухуровневым opt-in и предупреждает, а связку Origin/SameSite/токен выбирает приложение - исходя из того, кто его клиенты.

Я не против samesite=lax, это просто комментарий по поводу content-type: application/json. Этот механизм защиты стал излишним, теперь проверка CORS и защита от CSRF считай совмещены. Убрали его из стандарта и хорошо. От curl он в любом случае не защищал никак. А в браузерах теперь нужен на один cors preflight запрос меньше (если запрос к апишке кроссориджин).

По поводу origin: null на всяких Brave Browser, ну честно говоря, это не проблема шерифа имхо. Если идёшь на willful violation, то и сам себе злой Буратино. Юзеры Брейва и подобных браузеров и расширений сами как правило знают, что это они ломают сайт, и переключают на средний уровень паранойи.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации