Работа с Django, VUE, CORS и CSRF на примере реального мира - PullRequest
0 голосов
/ 23 февраля 2019

Я действительно застрял.Вот что я пытаюсь сделать.

  1. СОХРАНИТЬ CSRF Вкл.- пожалуйста, не говорите мне, чтобы отключить его.
  2. У меня есть приложение API, запущенное Django и Django Rest Framework
  3. У меня есть приложение внешнего интерфейса, запущенное Vue
  4. Я установил django-cors-headers для управления CORS

Все отлично работает локально.Как только я перехожу в производство, я получаю ошибки CSRF.Вот как все работает.

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

Итак, вот что у меня есть.

Установлено: django-cors-headersdjango-rest-framework drf-nested-маршрутизаторы ... и другие

У меня есть API, работающий на api.websitename.com, а приложение Vue.js работает на websitename.com.

GET запросы работают отлично.Запросы OPTION, похоже, работают.

Любой рискованный запрос не работает.

Для моего CORS у меня установлено 'corsheaders.middleware.CorsMiddleware', перед моим другим MIDDLEWARE.

Затем мой CORSнастройки:

CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
    '*.websitename.com',
)

И мои настройки CSRF:

CSRF_TRUSTED_ORIGINS = [
    "api.websitename.com",
]

Независимо от того, как я с ними играю, я получаю ошибку токена CSRF.

Я попытался сделать что-то подобное в моем файле Vue App.vue:

mounted () {
  this.getCSRFToken()
},
methods: {
  getCSRFToken () {
    return axios.get('token/').then(response => {
      axios.defaults.headers.common['x-csrftoken'] = Cookies.get('csrftoken')
    }).catch(error => {
      return Promise.reject(error.response.data)
    })
  }
}

Идея в том, что я получаю токен CSRF, как только приложение загружается в браузер.Но даже при этом я получаю ошибочные ошибки токена CSRF, когда приложение пытается сделать что-либо, кроме GET или OPTION.

Вот представление, которое возвращает токен, если вас интересует:

class CSRFTokenView(APIView):
    permission_classes = (permissions.AllowAny,)

    @method_decorator(ensure_csrf_cookie)
    def get(self, request):
        return HttpResponse()

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

Ответы [ 2 ]

0 голосов
/ 02 марта 2019

Прежде всего вы хотите использовать SessionAuthentication :

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
    )
}

Это будет принудительно использовать CSRF, за исключением анонимных пользователей (подробнее об этом чуть ниже).Для браузерного веб-интерфейса самое простое решение состоит в том, чтобы веб-интерфейс и браузер (браузер) находились в одном домене - это позволяет избежать CORS - как предлагается в комментариях выше.Если у вас есть другие клиенты, просто используйте токены (токены DRF или JWT), но они небезопасны для использования браузером из-за опасности атак XSS (хранение токенов в localStorage по своей сути небезопасно).

Как выиспользуют axios, настройка CSRF очень проста:

import axios from 'axios'

axios.defaults.xsrfHeaderName = 'X-CSRFToken'
axios.defaults.xsrfCookieName = 'csrftoken'

Таким образом, вы должны иметь безопасные сеансы с применением CSRF.Почти.Чтобы процитировать связанную страницу выше:

Предупреждение: всегда используйте стандартный вид входа Django при создании страниц входа.Это обеспечит надлежащую защиту ваших представлений для входа в систему.

Проверка CSRF в среде REST работает немного иначе, чем в стандартном Django, из-за необходимости поддерживать сеансовую и несессионную аутентификацию для одних и тех же представлений.Это означает, что только аутентифицированные запросы требуют токенов CSRF, и анонимные запросы могут отправляться без токенов CSRF.Это поведение не подходит для представлений входа в систему, к которым всегда должна применяться валидация CSRF.

Это странно - вам нужно либо просто использовать представления на стороне сервера Django, что делает ваш дизайн SPA несколько более сложным, либовоссоздайте вход в систему и другие виды авторизации в DRF с оговоркой об использовании декоратора метода @ csrf_protect для принудительного применения CSRF для этих «анонимных» представлений.Очевидно, что такие представления будут нарушены для клиентов, использующих токены, поэтому вы, вероятно, захотите использовать для них разные конечные точки (возможно, повторно использовать одни и те же базовые классы).Таким образом, ваш логин в браузере использует / auth / browser / login / и ваш логин для мобильного телефона / auth / mobile / login / , первый из которых заключен в @ csrf_protect .

Воссоздание логина и других представлений авторизации с нуля должно быть сделано тщательно после изучения исходного кода contrib auth;для ванильных требований я бы порекомендовал уже существующие решения, такие как django-rest-auth и django-all-auth .Пакет django-rest-auth, однако, не очень хорошо разработан для браузеров и требует использования генерации токенов, плюс вам нужно будет обернуть представления, как описано выше.С другой стороны, all-auth предоставляет ответы AJAX для клиентов JS и может быть лучше.

0 голосов
/ 23 февраля 2019

Самый простой способ решить эту проблему - обслуживать все из одного домена.Вы можете иметь свои CDN или прокси для прямых /api вызовов на один сервер, а остальные - на внешний сервер.Таким образом, вам не нужно беспокоиться о CORS.

Чтобы все заработало, я думаю, вам просто не хватает withCredentials = true в конфигурации AXIOS.Django требует, чтобы файл cookie CSRF отправлялся, а файлы cookie не отправлялись по запросам перекрестного происхождения, если withCredentials не задано.

axios.interceptors.request.use(function (config) {
  config.withCredentials = true
  return config
})

Другой параметр, который может отсутствовать, - это SESSION_COOKIE_DOMAIN в Djano.Вы должны установить его следующим образом:

SESSION_COOKIE_DOMAIN=".mywebsite.com"

Эта первая точка важна, потому что она сообщает Django, а затем веб-браузеру, чтобы использовать cookie для *.mywebsite.com, включая api.mywebsite.com.

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

...