Fix Session replay secure issue that when Rails application use CookieStore.
This commit is contained in:
parent
72d9e145db
commit
e129851fd9
|
@ -1,3 +1,10 @@
|
||||||
|
1.0.1
|
||||||
|
-----
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- Fix Session replay secure issue that when Rails application use CookieStore.
|
||||||
|
|
||||||
1.0.0
|
1.0.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
rucaptcha (1.0.0)
|
rucaptcha (1.0.1)
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
|
|
|
@ -11,7 +11,7 @@ module RuCaptcha
|
||||||
attr_accessor :cache_limit
|
attr_accessor :cache_limit
|
||||||
# Color style, default: :colorful, allows: [:colorful, :black_white]
|
# Color style, default: :colorful, allows: [:colorful, :black_white]
|
||||||
attr_accessor :style
|
attr_accessor :style
|
||||||
# session[:_rucaptcha] expire time, default 2 minutes
|
# rucaptcha expire time, default 2 minutes
|
||||||
attr_accessor :expires_in
|
attr_accessor :expires_in
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,28 +6,55 @@ module RuCaptcha
|
||||||
helper_method :verify_rucaptcha?
|
helper_method :verify_rucaptcha?
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_rucaptcha
|
def rucaptcha_sesion_key_key
|
||||||
session[:_rucaptcha] = RuCaptcha::Captcha.random_chars
|
['rucaptcha-session', session.id].join(':')
|
||||||
session[:_rucaptcha_at] = Time.now.to_i
|
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
|
end
|
||||||
|
|
||||||
def verify_rucaptcha?(resource = nil)
|
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 = (params[:_rucaptcha] || '').downcase.strip
|
||||||
|
if captcha.blank?
|
||||||
# Captcha chars in Session expire in 2 minutes
|
return add_rucaptcha_validation_error
|
||||||
valid = false
|
|
||||||
if (Time.now.to_i - rucaptcha_at) <= RuCaptcha.config.expires_in
|
|
||||||
valid = captcha.present? && captcha == session.delete(:_rucaptcha)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if resource && resource.respond_to?(:errors)
|
if captcha != store_info[:code]
|
||||||
resource.errors.add(:base, t('rucaptcha.invalid')) unless valid
|
return add_rucaptcha_validation_error
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
module RuCaptcha
|
module RuCaptcha
|
||||||
VERSION = '1.0.0'
|
VERSION = '1.0.1'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,23 +1,45 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
require 'securerandom'
|
||||||
|
|
||||||
describe RuCaptcha do
|
describe RuCaptcha do
|
||||||
|
class CustomSession
|
||||||
|
attr_accessor :id
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
self.id = SecureRandom.hex
|
||||||
|
end
|
||||||
|
end
|
||||||
class Simple < ActionController::Base
|
class Simple < ActionController::Base
|
||||||
def session
|
def session
|
||||||
@session ||= {}
|
@session ||= CustomSession.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def params
|
def params
|
||||||
@params ||= {}
|
@params ||= {}
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
let(:simple) { Simple.new }
|
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
|
describe '.generate_rucaptcha' do
|
||||||
it 'should work' do
|
it 'should work' do
|
||||||
expect(RuCaptcha::Captcha).to receive(:random_chars).and_return('abcd')
|
expect(RuCaptcha::Captcha).to receive(:random_chars).and_return('abcd')
|
||||||
expect(simple.generate_rucaptcha).not_to be_nil
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -29,7 +51,7 @@ describe RuCaptcha do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should work when session[:_rucaptcha] is nil' do
|
it 'should work when session[:_rucaptcha] is nil' do
|
||||||
simple.session[:_rucaptcha] = nil
|
simple.clean_custom_session
|
||||||
simple.params[:_rucaptcha] = 'Abcd'
|
simple.params[:_rucaptcha] = 'Abcd'
|
||||||
expect(simple.verify_rucaptcha?).to eq(false)
|
expect(simple.verify_rucaptcha?).to eq(false)
|
||||||
end
|
end
|
||||||
|
@ -37,11 +59,18 @@ describe RuCaptcha do
|
||||||
|
|
||||||
context 'Correct chars in params' do
|
context 'Correct chars in params' do
|
||||||
it 'should work' do
|
it 'should work' do
|
||||||
simple.session[:_rucaptcha_at] = Time.now.to_i
|
Rails.cache.write(simple.rucaptcha_sesion_key_key, {
|
||||||
simple.session[:_rucaptcha] = 'abcd'
|
time: Time.now.to_i,
|
||||||
|
code: 'abcd'
|
||||||
|
})
|
||||||
simple.params[:_rucaptcha] = 'Abcd'
|
simple.params[:_rucaptcha] = 'Abcd'
|
||||||
expect(simple.verify_rucaptcha?).to eq(true)
|
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'
|
simple.params[:_rucaptcha] = 'AbcD'
|
||||||
expect(simple.verify_rucaptcha?).to eq(true)
|
expect(simple.verify_rucaptcha?).to eq(true)
|
||||||
end
|
end
|
||||||
|
@ -49,17 +78,22 @@ describe RuCaptcha do
|
||||||
|
|
||||||
describe 'Incorrect chars' do
|
describe 'Incorrect chars' do
|
||||||
it 'should work' do
|
it 'should work' do
|
||||||
simple.session[:_rucaptcha_at] = Time.now.to_i - 60
|
Rails.cache.write(simple.rucaptcha_sesion_key_key, {
|
||||||
simple.session[:_rucaptcha] = 'abcd'
|
time: Time.now.to_i - 60,
|
||||||
|
code: 'abcd'
|
||||||
|
})
|
||||||
simple.params[:_rucaptcha] = 'd123'
|
simple.params[:_rucaptcha] = 'd123'
|
||||||
expect(simple.verify_rucaptcha?).to eq(false)
|
expect(simple.verify_rucaptcha?).to eq(false)
|
||||||
|
expect(simple.custom_session).to eq nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'Expires Session key' do
|
describe 'Expires Session key' do
|
||||||
it 'should work' do
|
it 'should work' do
|
||||||
simple.session[:_rucaptcha_at] = Time.now.to_i - 121
|
Rails.cache.write(simple.rucaptcha_sesion_key_key, {
|
||||||
simple.session[:_rucaptcha] = 'abcd'
|
time: Time.now.to_i - 121,
|
||||||
|
code: 'abcd'
|
||||||
|
})
|
||||||
simple.params[:_rucaptcha] = 'abcd'
|
simple.params[:_rucaptcha] = 'abcd'
|
||||||
expect(simple.verify_rucaptcha?).to eq(false)
|
expect(simple.verify_rucaptcha?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,6 +13,10 @@ module Rails
|
||||||
def root
|
def root
|
||||||
Pathname.new(File.join(File.dirname(__FILE__), '..'))
|
Pathname.new(File.join(File.dirname(__FILE__), '..'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cache
|
||||||
|
@cache ||= ActiveSupport::Cache::MemoryStore.new
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue