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