SECURITY: prevent reuse of password reset
This commit is contained in:
parent
eb2db23b40
commit
e0ff57ca75
|
@ -9,6 +9,7 @@ require_dependency 'json_error'
|
|||
require_dependency 'letter_avatar'
|
||||
require_dependency 'distributed_cache'
|
||||
require_dependency 'global_path'
|
||||
require_dependency 'secure_session'
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
include CurrentUser
|
||||
|
@ -381,6 +382,11 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
def secure_session
|
||||
SecureSession.new(session["secure_session_id"] ||= SecureRandom.hex)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def locale_from_header
|
||||
|
@ -558,6 +564,7 @@ class ApplicationController < ActionController::Base
|
|||
render_to_string status: status, layout: layout, formats: [:html], template: '/exceptions/not_found'
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
def render_post_json(post, add_raw=true)
|
||||
|
|
|
@ -389,19 +389,21 @@ class UsersController < ApplicationController
|
|||
def password_reset
|
||||
expires_now
|
||||
|
||||
if EmailToken.valid_token_format?(params[:token])
|
||||
token = params[:token]
|
||||
|
||||
if EmailToken.valid_token_format?(token)
|
||||
if request.put?
|
||||
@user = EmailToken.confirm(params[:token])
|
||||
@user = EmailToken.confirm(token)
|
||||
else
|
||||
email_token = EmailToken.confirmable(params[:token])
|
||||
email_token = EmailToken.confirmable(token)
|
||||
@user = email_token.try(:user)
|
||||
end
|
||||
|
||||
if @user
|
||||
session["password-#{params[:token]}"] = @user.id
|
||||
secure_session["password-#{token}"] = @user.id
|
||||
else
|
||||
user_id = session["password-#{params[:token]}"]
|
||||
@user = User.find(user_id) if user_id
|
||||
user_id = secure_session["password-#{token}"].to_i
|
||||
@user = User.find(user_id) if user_id > 0
|
||||
end
|
||||
else
|
||||
@invalid_token = true
|
||||
|
@ -420,7 +422,7 @@ class UsersController < ApplicationController
|
|||
@user.auth_token = nil
|
||||
if @user.save
|
||||
Invite.invalidate_for_email(@user.email) # invite link can't be used to log in anymore
|
||||
session["password-#{params[:token]}"] = nil
|
||||
secure_session["password-#{token}"] = nil
|
||||
logon_after_password_reset
|
||||
|
||||
return redirect_to(wizard_path) if Wizard.user_requires_completion?(@user)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# session that is not stored in cookie, expires after 1.hour unconditionally
|
||||
class SecureSession
|
||||
def initialize(prefix)
|
||||
@prefix = prefix
|
||||
end
|
||||
|
||||
def [](key)
|
||||
$redis.get("#{@prefix}#{key}")
|
||||
end
|
||||
|
||||
def []=(key,val)
|
||||
if val == nil
|
||||
$redis.del("#{@prefix}#{key}")
|
||||
else
|
||||
$redis.setex("#{@prefix}#{key}", 1.hour, val.to_s)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
require 'rails_helper'
|
||||
require_dependency 'secure_session'
|
||||
|
||||
describe SecureSession do
|
||||
it "operates correctly" do
|
||||
s = SecureSession.new("abc")
|
||||
|
||||
s["hello"] = "world"
|
||||
s["foo"] = "bar"
|
||||
expect(s["hello"]).to eq("world")
|
||||
expect(s["foo"]).to eq("bar")
|
||||
|
||||
s["hello"] = nil
|
||||
expect(s["hello"]).to eq(nil)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue