Token de autenticidade do Rails

Oi,

Desejo explicar o Token de Lembrete / Token de Autenticidade com base em um exemplo (Signin / Signout).
Ruby fornece um recurso de módulo para empacotar funções juntas e incluí-las em vários lugares, e esse é o plano para as funções de autenticação.
Você poderia fazer um módulo totalmente novo para autenticação, mas o controlador Sessions já vem equipado com um módulo, a saber, SessionsHelper. Além disso, esses helpers são incluídos automaticamente nas visualizações do Rails, então tudo o que precisamos fazer para usar as funções do helper Sessions nos controladores é incluir o módulo no controlador de aplicativo

app / controllers / sessions_controller.rb

class SessionsController < ApplicationController

def new
end

def create
user
= User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
# Sign the user in and redirect to the user's show page.
else
flash
.now[:error] = 'Invalid email/password combination'
render
'new'
end
end

def destroy
end
end

app / controllers / application_controller.rb

class ApplicationController < ActionController::Base
protect_from_forgery
with: :exception
include
SessionsHelper
end

Por padrão, todos os auxiliares estão disponíveis nas visualizações, mas não nos controladores. Você precisa dos métodos do auxiliar Sessions em ambos os lugares, portanto, temos que incluí-lo explicitamente.

Como o HTTP é um protocolo sem estado, os aplicativos da web que requerem login do usuário devem implementar uma maneira de rastrear o progresso de cada usuário de uma página para outra. Uma técnica para manter o status de login do usuário é usar uma sessão Rails tradicional (por meio da função de sessão especial) para armazenar um token de lembrança igual ao id do usuário:

sessão [: lembrar_token] = user.id

Este objeto de sessão disponibiliza o ID do usuário de página em página, armazenando-o em um cookie que expira no fechamento do navegador. Em cada página, o aplicativo pode simplesmente chamar

User.find (sessão [: lembrar_token])

para recuperar o usuário. Por causa da maneira como o Rails lida com as sessões, esse processo é seguro; se um usuário malicioso tentar falsificar o id do usuário, o Rails detectará uma incompatibilidade com base em um id de sessão especial gerado para cada sessão.

Para a escolha de design de nosso aplicativo, que envolve sessões persistentes – ou seja, o status de login que dura mesmo após o fechamento do navegador – você precisa usar um identificador permanente para o usuário conectado. Para fazer isso, você vai gerar um token de lembrança único e seguro para cada usuário e armazená-lo como um cookie permanente em vez de um que expira no fechamento do navegador.

O token de lembrança precisa ser associado a um usuário e armazenado para uso futuro, então vamos adicioná-lo como um atributo ao modelo de usuário, conforme mostrado na Figura

Agora você precisa decidir o que usar como token de lembrança . Existem muitas possibilidades equivalentes – essencialmente, qualquer string aleatória grande servirá bem, contanto que seja única. O urlsafe_base64 método do módulo SecureRandom na biblioteca padrão rubi encaixa a factura ele retorna uma sequência aleatória de 16 comprimento composto por caracteres A-Z, a-z, 0-9, “-”, e “_” (para uma total de 64 possibilidades, portanto “base64”). Isso significa que a probabilidade de dois tokens lembrar colidirem é desprezivelmente pequeno 1/6416 = 2−96≈10−29.

Nosso plano é armazenar o token base64 no navegador e, em seguida, armazenar uma versão criptografada no banco de dados. Em seguida, você pode conectar os usuários automaticamente, recuperando o token do cookie, criptografando-o e, em seguida, procurando um token de lembrança que corresponda ao valor criptografado. O motivo para armazenar apenas tokens criptografados é que, mesmo se todo o nosso banco de dados estiver comprometido, o invasor ainda não será capaz de usar os tokens de lembrança para fazer login. Para tornar nosso token de lembrança ainda mais seguro, planejamos alterá-lo cada vez que um usuário cria uma nova sessão, o que significa que qualquer sequestrado sessões – nas quais um invasor usa um cookie roubado para fazer login como um usuário específico – irão expirar na próxima vez que um usuário entrar. (O sequestro de sessão foi amplamente divulgado pelo aplicativo Firesheep, que mostrou que lembrar tokens em muitos sites importantes eram visíveis quando conectados a redes públicas Wi-Fi. A solução é configurar o aplicativo para usar SSL na produção.

SampleApp::Application.configure do
.
.
.
# Force all access to the app over SSL, use Strict-Transport-Security,
# and use secure cookies.
config
.force_ssl = true
.
.
.
end

Primeiro, adicionamos um método de retorno de chamada para criar um token de lembrança imediatamente antes de criar um novo usuário no banco de dados: app / models / user.rb

antes de criar: criar Remember_token

Este código, chamado de referência de método, faz com que o Rails procure um método chamado create Remember token e execute-o antes de salvar o usuário.

private

def create_remember_token
# Create the token.
end

Todos os métodos definidos em uma classe após private são automaticamente ocultados, de modo que

$ rails console
>> User.first.create_remember_token

irá gerar uma exceção NoMethodError.

Finalmente, o método create Remember token precisa ser atribuído a um dos atributos do usuário e, neste contexto, é necessário usar a palavra-chave self na frente de remember_token:

def User.new_remember_token
SecureRandom.urlsafe_base64
end

def User.encrypt(token)
Digest::SHA1.hexdigest(token.to_s)
end

private

def create_remember_token
self.remember_token = User.encrypt(User.new_remember_token)
end

Por causa da maneira como Ruby lida com atribuições dentro de objetos, sem self a atribuição criaria uma variável local chamada recordar token, que não é o que queremos. O uso de self garante que a atribuição defina o token de lembrança do usuário e, como resultado, será gravado no banco de dados junto com os outros atributos quando o usuário for salvo.

Agora estamos prontos para escrever o primeiro elemento signin, a própria função sign_in. Conforme observado acima, nosso método de autenticação desejado é colocar um token de memória (recém-criado) como um cookie no navegador do usuário e, em seguida, usar o token para localizar o registro do usuário no banco de dados conforme o usuário passa de uma página para outra.

app / helpers / sessions_helper.rb

module SessionsHelper

def sign_in(user)
remember_token
= User.new_remember_token
cookies
.permanent[:remember_token] = remember_token
user
.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
end

Aqui, seguimos as etapas desejadas: primeiro, crie um novo token; em segundo lugar, coloque o token não criptografado nos cookies do navegador; terceiro, salve o token criptografado no banco de dados; quarto, defina o usuário atual igual ao usuário fornecido.

Cada elemento no cookie é um hash de dois elementos, um valor e uma data de expiração opcional. Por exemplo, podemos implementar o login do usuário colocando um cookie com valor igual ao token de lembrança que expira em 20 anos a partir de agora:

cookies[:remember_token] = { value:   remember_token,
expires
: 20.years.from_now.utc }

Este padrão de definir um cookie que expira 20 anos no futuro tornou-se tão comum que Rails adicionou um método permanente especial para implementá-lo, para que possamos simplesmente escrever

cookies.permanent [: lembrar token] = lembrar token

Nos bastidores, usar o permanente faz com que o Rails defina a expiração para 20.years.from_now automaticamente.

Depois que o cookie é definido, nas visualizações de página subsequentes, podemos recuperar o usuário com um código como:

User.find by (lembrar token: remember_token)

Tendo discutido como armazenar o token de lembrança do usuário em um cookie para uso posterior, agora precisamos aprender como recuperar o usuário em visualizações de página subsequentes. Vamos olhar novamente para a função sign_in para ver onde estamos:

module SessionsHelper

def sign_in(user)
remember_token
= User.new_remember_token
cookies
.permanent[:remember_token] = remember_token
user
.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
end

O único código que não está funcionando atualmente é

self.current_user = user

Este código nunca será realmente usado no presente aplicativo devido ao redirecionamento imediato, mas seria perigoso para o método sign_in confiar nisso.

Para começar a escrever o código para current_user, observe que a linha

self.current_user = user

é uma atribuição, que devemos definir. Ruby tem uma sintaxe especial para definir essa função de atribuição, mostrada: app / helpers / sessions_helper.rb

module SessionsHelper

def sign_in(user)
.
.
.
end

def current_user=(user)
@current_user = user
end

def current_user
@current_user # Useless! Don't use this line.
end
end

Isso pode parecer confuso – a maioria das linguagens não permite que você use o sinal de igual em uma definição de método – mas simplesmente define um método current user = expressamente projetado para lidar com a atribuição ao usuário atual . Em outras palavras, o código

self.current_user = …

é automaticamente convertido para

current_user = (…)

invocando assim o método user = atual . Seu único argumento é o lado direito da atribuição, neste caso o usuário a ser conectado. O corpo do método de uma linha apenas define uma variável de instância @ usuário atual , armazenando efetivamente o usuário para uso posterior.

Se fizéssemos isso, replicaríamos efetivamente a funcionalidade do accessor attr . O problema é que ele falha totalmente em resolver nosso problema: com o código em “SessionsHelper” acima, o status de login do usuário seria esquecido: assim que o usuário fosse para outra página – puf! – a sessão terminaria e o usuário ser desconectado automaticamente. Isso se deve à natureza sem estado das interações HTTP – quando o usuário faz uma segunda solicitação, todas as variáveis ​​são definidas para seus padrões, o que, por exemplo, variáveis ​​como @ usuário atual é nulo. Portanto, quando um usuário acessa outra página, mesmo no mesmo aplicativo, Rails configurou @current_user para nil, e o código acima não fará o que você deseja.

Para evitar esse problema, podemos encontrar o usuário correspondente ao token de lembrança criado pelo código em SessionsHelper, conforme mostrado acima. Observe que, como o token de lembrete no banco de dados é criptografado, primeiro precisamos criptografar o token do cookie antes de usá-lo para localizar o usuário no banco de dados. Conseguimos isso com o método User.encrypt definido abaixo.
Encontrar o usuário atual usando o token de
lembrança . app / helpers / sessions helper.rb

module SessionsHelper
.
.
.
def current_user=(user)
@current_user = user
end

def current_user
remember_token
= User.encrypt(cookies[:remember_token])
@current_user ||= User.find_by(remember_token: remember_token)
end
end

Espero que você tenha aprendido o básico de token / sessões de autenticidade em ruby ​​on rails

Créditos: https://www.railstutorial.org/ – Michael Hartl