Является ли это Rails JSON authentication API (с помощью Devise) безопасным?



моих Rails приложение использует разработать для проверки подлинности. Он имеет сестру iOS приложение, и пользователи могут войти в приложение, используя те же учетные данные для веб-приложения. Поэтому мне нужен какой-то API для аутентификации.



много подобных вопросов здесь пункт в этом уроке, но он, кажется, устарел, так как token_authenticatable модуль с тех пор был удален из Devise и некоторые из строк бросают ошибки. (Я использую Devise 3.2.2.) Я попытался свернуть мой собственный на основе этого учебника (и этот), но я не уверен в этом на 100% - я чувствую, что может быть что-то, что я неправильно понял или пропустил.



во-первых, следуя советам в этом суть, я добавил мой текст users таблица, и следующее к user.rb:



before_save :ensure_authentication_token

def ensure_authentication_token
if authentication_token.blank?
self.authentication_token = generate_authentication_token
end
end

private

def generate_authentication_token
loop do
token = Devise.friendly_token
break token unless User.find_by(authentication_token: token)
end
end


тогда у меня есть следующие контроллеры:



api_controller.РБ



class ApiController < ApplicationController
respond_to :json
skip_before_filter :authenticate_user!

protected

def user_params
params[:user].permit(:email, :password, :password_confirmation)
end
end


(обратите внимание, что мой application_controller и в строке before_filter :authenticate_user!.)



API-интерфейс/sessions_controller.РБ



class Api::SessionsController < Devise::RegistrationsController
prepend_before_filter :require_no_authentication, :only => [:create ]

before_filter :ensure_params_exist

respond_to :json

skip_before_filter :verify_authenticity_token

def create
build_resource
resource = User.find_for_database_authentication(
email: params[:user][:email]
)
return invalid_login_attempt unless resource

if resource.valid_password?(params[:user][:password])
sign_in("user", resource)
render json: {
success: true,
auth_token: resource.authentication_token,
email: resource.email
}
return
end
invalid_login_attempt
end

def destroy
sign_out(resource_name)
end

protected

def ensure_params_exist
return unless params[:user].blank?
render json: {
success: false,
message: "missing user parameter"
}, status: 422
end

def invalid_login_attempt
warden.custom_failure!
render json: {
success: false,
message: "Error with your login or password"
}, status: 401
end
end


API-интерфейс/registrations_controller.РБ



class Api::RegistrationsController < ApiController
skip_before_filter :verify_authenticity_token

def create
user = User.new(user_params)
if user.save
render(
json: Jbuilder.encode do |j|
j.success true
j.email user.email
j.auth_token user.authentication_token
end,
status: 201
)
return
else
warden.custom_failure!
render json: user.errors, status: 422
end
end
end


и в config/маршруты.РБ:



  namespace :api, defaults: { format: "json" } do
devise_for :users
end


я немного не в своей тарелке, и я уверен, что здесь есть что-то, что мое будущее Я будет оглядываться назад и съеживаться (обычно есть). Некоторые сомнительные части:



во-первых, вы заметите, что Api::SessionsController наследует от Devise::RegistrationsController, тогда как Api::RegistrationsController наследует от ApiController (у меня также есть некоторые другие контроллеры, такие как Api::EventsController < ApiController которые имеют дело с более стандартными вещами отдыха для моих других моделей и не имеют большого контакта с Devise.) Это довольно некрасиво, но я не мог найти другой способ получить доступ к методам в Api::RegistrationsController. Учебник, на который я ссылался выше, имеет строку include Devise::Controllers::InternalHelpers, но этот модуль, похоже, был удален в более поздних версиях Разрабатывать.



во-вторых, я отключил защиту CSRF с помощью линии skip_before_filter :verify_authentication_token. У меня есть сомнения в том, что это хорошая идея - я вижу много противоречивых или трудных для понимания советов о том, уязвимы ли API JSON для атак CSRF, но добавление этой строки было единственным способом заставить проклятую вещь работать.



в-третьих, я хочу убедиться, что понимаю, как работает аутентификация после входа пользователя. Скажем, у меня есть API звоните GET /api/friends который возвращает список друзей текущего пользователя. Как я понимаю, приложение iOS должно было бы получить пользователя authentication_token из базы данных (которая является фиксированной величиной для каждого пользователя, который никогда не меняется??), затем представить его в качестве param вместе с каждым запросом, например GET /api/friends?authentication_token=abcdefgh1234, тогда мой Api::FriendsController можно сделать что-то вроде User.find_by(authentication_token: params[:authentication_token]) чтобы получить current_user. Это действительно так просто, или я что-то упускаю?



так что для тех, кто успел прочитать весь путь до конца этот гигантский вопрос, Спасибо за ваше время! Подводя итог:





  1. эта система входа в систему безопасна? или есть что-то, что я упустил или неправильно понял, например, когда дело доходит до атак CSRF?


  2. я понимаю, как аутентифицировать запросы после того, как пользователи вошли в систему правильно? (см. "В-третьих..." выше.)


  3. есть ли способ этот код можно очистить или сделать лучше? особенно некрасиво дизайн наличия одного контроллера наследуется от Devise::RegistrationsController и других ApiController.


спасибо!

682   3  

3 ответов:

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

  • на стороне сервера добавьте after_filter к контроллеру сеансов:

    after_filter :set_csrf_header, only: [:new, :create]
    
    protected
    
    def set_csrf_header
       response.headers['X-CSRF-Token'] = form_authenticity_token
    end
    

    это создаст токен, поместите его в свой сеанс и скопируйте его в заголовок ответа для выбранных действий.

  • на стороне клиента (iOS) вы должны убедиться, что две вещи на месте.

    • ваш клиент должен проверить все ответы сервера для этого заголовка и сохранить его, когда он передается.

      ... get ahold of response object
      // response may be a NSURLResponse object, so convert:
      NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
      // grab token if present, make sure you have a config object to store it in
      NSString *token = [[httpResponse allHeaderFields] objectForKey:@"X-CSRF-Token"];
      if (token)
         [yourConfig setCsrfToken:token];
      
    • наконец, ваш клиент должен добавить этот маркер ко всем запросам "non GET", которые он отправляет:

      ... get ahold of your request object
      if (yourConfig.csrfToken && ![request.httpMethod isEqualToString:@"GET"])
        [request setValue:yourConfig.csrfToken forHTTPHeaderField:@"X-CSRF-Token"];
      

заключительная часть головоломки состоит в том, чтобы понять, что при входе в систему для разработки два последующих сеанса / CSRF-токены быть используемым. Поток входа будет выглядеть так:

GET /users/sign_in ->
  // new action is called, initial token is set
  // now send login form on callback:
  POST /users/sign_in <username, password> ->
    // create action called, token is reset
    // when login is successful, session and token are replaced 
    // and you can send authenticated requests

ваш пример, похоже, имитирует код из блога Devise -https://gist.github.com/josevalim/fb706b1e933ef01e4fb6

Как уже упоминалось в этом посте, вы делаете это похоже на Вариант 1, как они говорят, это небезопасный вариант. Я думаю, что ключ заключается в том, что вы не хотите просто сбрасывать маркер аутентификации каждый раз, когда пользователь сохраняется. Я думаю, что токен должен быть создан явно (каким-то TokenController в API) и должен истекать периодически.

вы заметите, что я говорю "я думаю", так как (насколько я могу судить) никто не имеет больше информации об этом.

топ-10 наиболее распространенных уязвимостей в веб-приложениях описаны в OWASP Top 10. В этом вопросе упоминалось, что защита от подделки межсайтовых запросов(CSRF) была отключена, и CSRF находится в ТОП-10 OWASDP. Короче говоря, CSRF используется злоумышленниками для выполнения действий в качестве аутентифицированного пользователя. Отключение защиты CSRF приведет к высокому риску уязвимости в приложении и подрывает цель наличия безопасной системы аутентификации. Свой вероятно, что защита CSRF не удалась, потому что клиент не может передать маркер синхронизации CSRF.

читать весь OWASP top 10, не в состоянии сделать это чрезвычайно опасные. Обратите пристальное внимание на сломанная аутентификация и управление сеансами, также проверьте Шпаргалка Управления Сеансом.

Comments

    Ничего не найдено.