SECURITY: prevent reuse of password reset

This commit is contained in:
Sam 2016-12-19 18:00:22 +11:00
parent eb2db23b40
commit e0ff57ca75
4 changed files with 50 additions and 7 deletions

View File

@ -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)

View File

@ -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)

18
lib/secure_session.rb Normal file
View File

@ -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

View File

@ -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