¿Es segura esta API de autenticación Rails JSON (usando Devise)?

La aplicación My Rails utiliza Devise para la autenticación. Tiene una aplicación hermana de iOS y los usuarios pueden iniciar sesión en la aplicación de iOS con las mismas credenciales que utilizan para la aplicación web. Así que necesito algún tipo de API para la autenticación.

Muchas preguntas similares aquí apuntan aeste tutorial, pero parece estar desactualizado, ya que eltoken_authenticatable desde entonces, el módulo se ha eliminado de Devise y algunas de las líneas arrojan errores. (Estoy usando Devise 3.2.2.) Intenté rodar mi propio basado en ese tutorial (yéste), pero no estoy 100% seguro de ello. Siento que puede haber algo que no he entendido o que no he entendido.

En primer lugar, siguiendo el consejo deesta esencia, Agregué unauthentication_token atributo de texto a miusers mesa, y lo siguiente auser.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

Luego tengo los siguientes controladores:

api_controller.rb

class ApiController < ApplicationController
  respond_to :json
  skip_before_filter :authenticate_user!

  protected

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

(Tenga en cuenta que miapplication_controller tiene la líneabefore_filter :authenticate_user!.)

api / session_controller.rb

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.rb

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

Y enconfig / route.rb:

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

Estoy fuera de mi profundidad un poco y estoy seguro de que hay algo aquí que mi futuro yo volverá a ver y temblará (generalmente hay). Algunas partes dudosas:

en primer lugar, te darás cuenta de queApi::SessionsController hereda deDevise::RegistrationsController mientrasApi::RegistrationsController hereda deApiController (También tengo algunos otros controladores comoApi::EventsController < ApiController que tienen que ver con el material REST más estándar para mis otros modelos y no tengo mucho contacto con Devise.) Este es un arreglo bastante feo, pero no pude encontrar otra forma de acceder a los métodos que necesito enApi::RegistrationsController. El tutorial al que he vinculado arriba tiene la línea.include Devise::Controllers::InternalHelpers, pero este módulo parece haber sido eliminado en versiones más recientes de Devise.

En segundo lugar, He deshabilitado la protección CSRF con la líneaskip_before_filter :verify_authentication_token. Tengo mis dudas sobre si esto es una buena idea, veo muchascontradictorio odifícil de entender consejos sobre si las API de JSON son vulnerables a los ataques CSRF, pero agregar esa línea era la única forma en que podía hacer que funcionara la maldita cosa.

En tercer lugar, Quiero asegurarme de que entiendo cómo funciona la autenticación una vez que un usuario ha iniciado sesión. Supongamos que tengo una llamada a la APIGET /api/friends que devuelve una lista de los amigos del usuario actual. Según tengo entendido, la aplicación iOS tendría que obtener el usuarioauthentication_token de la base de datos (que es un valor fijo para cada usuario que nunca cambia?), luego envíelo como parámetro junto con cada solicitud, por ejemplo.GET /api/friends?authentication_token=abcdefgh1234, entonces miApi::FriendsController podría hacer algo comoUser.find_by(authentication_token: params[:authentication_token]) para obtener el current_user. ¿Es realmente tan simple, o me estoy perdiendo algo?

Así que, para cualquiera que haya logrado leer hasta el final de esta pregunta gigantesca, ¡gracias por su tiempo! Resumir:

¿Es seguro este sistema de inicio de sesión? ¿O hay algo que he pasado por alto o que no he entendido bien, por ejemplo? Cuando se trata de ataques CSRF?¿Mi comprensión de cómo autenticar las solicitudes una vez que los usuarios han iniciado sesión es correcta? (Vea "en tercer lugar ..." arriba).¿Hay alguna forma en que este código pueda ser limpiado o mejorado? Particularmente el diseño feo de tener un controlador heredado deDevise::RegistrationsController y los otros deApiController.

¡Gracias!

Respuestas a la pregunta(3)

Su respuesta a la pregunta