Fix Session replay secure issue that when Rails application use CookieStore.

This commit is contained in:
Jason Lee 2016-10-14 17:33:34 +08:00
parent 72d9e145db
commit e129851fd9
7 changed files with 98 additions and 26 deletions

View File

@ -1,3 +1,10 @@
1.0.1
-----
## Security Notes
- Fix Session replay secure issue that when Rails application use CookieStore.
1.0.0
-----

View File

@ -1,7 +1,7 @@
PATH
remote: .
specs:
rucaptcha (1.0.0)
rucaptcha (1.0.1)
railties (>= 3.2)
GEM

View File

@ -11,7 +11,7 @@ module RuCaptcha
attr_accessor :cache_limit
# Color style, default: :colorful, allows: [:colorful, :black_white]
attr_accessor :style
# session[:_rucaptcha] expire time, default 2 minutes
# rucaptcha expire time, default 2 minutes
attr_accessor :expires_in
end
end

View File

@ -6,28 +6,55 @@ module RuCaptcha
helper_method :verify_rucaptcha?
end
def generate_rucaptcha
session[:_rucaptcha] = RuCaptcha::Captcha.random_chars
session[:_rucaptcha_at] = Time.now.to_i
def rucaptcha_sesion_key_key
['rucaptcha-session', session.id].join(':')
end
RuCaptcha::Captcha.create(session[:_rucaptcha])
def generate_rucaptcha
code = RuCaptcha::Captcha.random_chars
Rails.cache.write(rucaptcha_sesion_key_key, {
code: code,
time: Time.now.to_i
})
RuCaptcha::Captcha.create(code)
end
def verify_rucaptcha?(resource = nil)
rucaptcha_at = session[:_rucaptcha_at].to_i
store_info = Rails.cache.read(rucaptcha_sesion_key_key)
# make sure move used key
Rails.cache.delete(rucaptcha_sesion_key_key)
# Make sure session exist
if store_info.blank?
return add_rucaptcha_validation_error
end
# Make sure not expire
if (Time.now.to_i - store_info[:time]) > RuCaptcha.config.expires_in
return add_rucaptcha_validation_error
end
# Make sure parama have captcha
captcha = (params[:_rucaptcha] || '').downcase.strip
# Captcha chars in Session expire in 2 minutes
valid = false
if (Time.now.to_i - rucaptcha_at) <= RuCaptcha.config.expires_in
valid = captcha.present? && captcha == session.delete(:_rucaptcha)
if captcha.blank?
return add_rucaptcha_validation_error
end
if resource && resource.respond_to?(:errors)
resource.errors.add(:base, t('rucaptcha.invalid')) unless valid
if captcha != store_info[:code]
return add_rucaptcha_validation_error
end
valid
true
end
private
def add_rucaptcha_validation_error
if defined?(resource) && resource && resource.respond_to?(:errors)
resource.errors.add(:base, t('rucaptcha.invalid'))
end
false
end
end
end

View File

@ -1,3 +1,3 @@
module RuCaptcha
VERSION = '1.0.0'
VERSION = '1.0.1'
end

View File

@ -1,23 +1,45 @@
require 'spec_helper'
require 'securerandom'
describe RuCaptcha do
class CustomSession
attr_accessor :id
def initialize
self.id = SecureRandom.hex
end
end
class Simple < ActionController::Base
def session
@session ||= {}
@session ||= CustomSession.new
end
def params
@params ||= {}
end
def custom_session
Rails.cache.read(self.rucaptcha_sesion_key_key)
end
def clean_custom_session
Rails.cache.delete(self.rucaptcha_sesion_key_key)
end
end
let(:simple) { Simple.new }
describe '.rucaptcha_sesion_key_key' do
it 'should work' do
expect(simple.rucaptcha_sesion_key_key).to eq ['rucaptcha-session', simple.session.id].join(':')
end
end
describe '.generate_rucaptcha' do
it 'should work' do
expect(RuCaptcha::Captcha).to receive(:random_chars).and_return('abcd')
expect(simple.generate_rucaptcha).not_to be_nil
expect(simple.session[:_rucaptcha]).to eq('abcd')
expect(simple.custom_session[:code]).to eq('abcd')
end
end
@ -29,7 +51,7 @@ describe RuCaptcha do
end
it 'should work when session[:_rucaptcha] is nil' do
simple.session[:_rucaptcha] = nil
simple.clean_custom_session
simple.params[:_rucaptcha] = 'Abcd'
expect(simple.verify_rucaptcha?).to eq(false)
end
@ -37,11 +59,18 @@ describe RuCaptcha do
context 'Correct chars in params' do
it 'should work' do
simple.session[:_rucaptcha_at] = Time.now.to_i
simple.session[:_rucaptcha] = 'abcd'
Rails.cache.write(simple.rucaptcha_sesion_key_key, {
time: Time.now.to_i,
code: 'abcd'
})
simple.params[:_rucaptcha] = 'Abcd'
expect(simple.verify_rucaptcha?).to eq(true)
simple.session[:_rucaptcha] = 'abcd'
expect(simple.custom_session).to eq nil
Rails.cache.write(simple.rucaptcha_sesion_key_key, {
time: Time.now.to_i,
code: 'abcd'
})
simple.params[:_rucaptcha] = 'AbcD'
expect(simple.verify_rucaptcha?).to eq(true)
end
@ -49,17 +78,22 @@ describe RuCaptcha do
describe 'Incorrect chars' do
it 'should work' do
simple.session[:_rucaptcha_at] = Time.now.to_i - 60
simple.session[:_rucaptcha] = 'abcd'
Rails.cache.write(simple.rucaptcha_sesion_key_key, {
time: Time.now.to_i - 60,
code: 'abcd'
})
simple.params[:_rucaptcha] = 'd123'
expect(simple.verify_rucaptcha?).to eq(false)
expect(simple.custom_session).to eq nil
end
end
describe 'Expires Session key' do
it 'should work' do
simple.session[:_rucaptcha_at] = Time.now.to_i - 121
simple.session[:_rucaptcha] = 'abcd'
Rails.cache.write(simple.rucaptcha_sesion_key_key, {
time: Time.now.to_i - 121,
code: 'abcd'
})
simple.params[:_rucaptcha] = 'abcd'
expect(simple.verify_rucaptcha?).to eq(false)
end

View File

@ -13,6 +13,10 @@ module Rails
def root
Pathname.new(File.join(File.dirname(__FILE__), '..'))
end
def cache
@cache ||= ActiveSupport::Cache::MemoryStore.new
end
end
end