Как вы защищаете API RESTful, потребляемый браузером от атак CSRF?

Я разрабатываю API для группы сайтов. Сайты очень похожи (вроде StackOverflow, SuperUser и ServerFault), и для них имеет смысл иметь общий бэкэнд. Следовательно, мы решили попробовать и иметь хороший REST API в качестве бэкэнд, а также кучу очень-подобных, но разных интерфейсов, потребляющих указанный API. Передняя панель должна быть предпочтительно статичной, но это не является жестким требованием, если она окажется невозможной.

Я сейчас занимаюсь разработкой этого API, и меня беспокоят последствия для безопасности, особенно CSRF. Из моего основного понимания атак CSRF они состоят из двух важных компонентов:

  • Возможность указать ресурс и тело запроса.

  • Обход пользователя/браузера с использованием ambient auth (например, сеансов) для запроса на этот ресурс, который выглядит аутентифицированным.

Многие классические подходы к исправлению атак CSRF основаны на сеансе. Поскольку мой REST API на самом деле не делает сеансы, это предотвращает много векторов, а также почти все способы их исправления. Например, двойное подчинение не имеет смысла, потому что нечего делать двойным.

Мой первоначальный подход включал атаку на часть 2 атаки CSRF. Если я аутентифицирую все запросы (скажем, используя HTTP Basic Auth), и браузер не сохранит эти учетные данные (например, некоторые JS сделали запрос), только JS, у которого есть учетные данные, может сделать запрос, и мы закончили, Очевидным недостатком является то, что приложение должно знать учетные данные пользователя. Другой немного менее очевидный недостаток заключается в том, что если я хочу надежно хранить учетные данные на конце API, то проверка пароля должна занять фиксированное, нетривиальное количество времени. Если проверка пароля безопасно занимает 100 мс, тогда каждый другой запрос будет занимать не менее 100 мс + eps, и он собирается взять некоторые черты умных клиентов, чтобы сделать это не слишком медленным. Я мог бы кэшировать это (так как учетные данные всегда будут одинаковыми), и если я буду очень осторожен, мне это удастся сделать без введения временной уязвимости, но это звучит как гнездо шершни.

OAuth 2.0 кажется немного выше, но я думаю, что это может быть лучшим решением в конце концов, чтобы я в конечном итоге не реализовал его. Я полагаю, что сейчас могу сделать HTTP Basic Auth, и перейдем к OAuth, когда у нас есть сторонние разработчики приложений.

Там немного несоответствие импеданса с OAuth. OAuth действительно хочет помочь приложениям получать доступ к другим приложениям в основном. Я хочу, чтобы пользователи регистрировались на одном из интерфейсов, прежде чем такая учетная запись даже существует.

Я также рассмотрел атакующий пункт 1, сделав рандомизированные URL-адреса, то есть добавляя токены в строку запроса. Это, безусловно, будет работать, и это очень близко к тому, как работает традиционный рандомизированный токен в форме, и, учитывая HATEOAS, он должен быть даже довольно RESTful, хотя это вызывает два вопроса: 1) с чего вы начинаете? Есть ли обязательная начальная точка API, в которой вы входите в систему, используя HTTP Basic Auth? 2) Насколько он заставит разработчиков приложений довольствоваться тем, что они не могут предсказать URL-адрес впереди, HATEOAS быть проклятым?

Я видел Как предотвратить CSRF в приложении RESTful?, но я не согласен с предпосылкой, что рандомизированные URI обязательно являются unRESTful. Кроме того, этот вопрос действительно не имеет удовлетворительных ответов и не упоминает OAuth. Кроме того, решение о двойном представлении сеанса недействительно, как я уже упоминал выше (другой домен для статического интерфейса, чем конечная точка API).

Я понимаю, что то, что я в основном пытаюсь сделать здесь, пытается разрешить межсайтовые запросы из одного домена и запрещать их другим, и это не так просто. Разумеется, должно быть какое-то разумное решение?

Ответ 1

Ток CSRF по определению "на пользовательское состояние" и, следовательно, не RESTful. Большинство API нарушают свои требования "для каждого пользователя" в целях безопасности и требуют токена CSRF, переданного в качестве параметра заголовка HTTP.

Существуют другие способы предотвращения CSRF. Проверка рефератора не такая сильная, как токен CSRF, но он RESTful и ОЧЕНЬ вряд ли будет подорван. Имейте в виду, что отсутствие реферирования следует считать неудавшимся запросом.

XSS может использоваться для обхода предотвращения CSRF на основе токенов и предотвращения CSRF на основе рефералов.

Ответ 2

Ответ зависит от того, что вам нужно поддерживать. Если мы предположим, что вы хотите поддерживать веб-приложение, которое использует службу REST, и использовать ту же службу REST для API, которая отличается от веб-приложения, которое является RESTFUL (вы можете решить, что "сеансы" предназначены для вы!).

На данный момент (/me довольно устал) Я думаю, что способ, которым вы предложили использовать javascript с HTTP Basic Auth, - хорошее начало.

Ответ 3

У меня есть другое потенциальное решение, поэтому я перечисляю его как ответ. Пожалуйста, не стесняйтесь разобрать:)

auth.platform.com принимает аутентификацию и устанавливает файлы cookie. Если auth.site.com является CNAME для auth.platform.com, будет ли запрос auth.site.com (заканчивается на auth.platform.com после разрешения) установить cookie для site.com? Таким образом, я мог бы дважды отправить сеансовые файлы cookie.

Конечно, auth.platform.com будет устанавливать только файлы cookie для нескольких белых списков.

EDIT: за исключением того, что это вообще не сработает, потому что вам нужно будет использовать HTTPS для надежной проверки подлинности, и HTTPS увидит вашу хитрость.

Ответ 4

Разработка вашего API как истинного RESTful, предотвращает наиболее распространенные векторы CSRF:

  • избежание куки файлов предотвращает их "кражу",
  • Использование GET для "безопасных" операций предотвращает img и iframes для запуска небезопасных действий без взаимодействия с пользователем.

Затем вы должны реализовать CORS, чтобы позволить браузерам пользователей блокировать запросы из источников, которым вы не доверяете.