Skip to content

Commit

Permalink
Merge pull request #17 from PerimeterX/dev
Browse files Browse the repository at this point in the history
Releasing version 1.3.0
  • Loading branch information
nitzanpx authored Jul 27, 2017
2 parents 8c6539a + 2c74fab commit 4ecb80e
Show file tree
Hide file tree
Showing 30 changed files with 824 additions and 98 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.rbc
*.iml
capybara-*.html
.rspec
/log
Expand Down
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
language: ruby
rvm:
- 2.3
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
perimeter_x (1.2.0)
perimeter_x (1.3.0)
activesupport (>= 4.2.0)
concurrent-ruby (~> 1.0, >= 1.0.5)
mustache (~> 1.0, >= 1.0.3)
Expand All @@ -20,7 +20,7 @@ GEM
ethon (0.10.1)
ffi (>= 1.3.0)
ffi (1.9.18)
i18n (0.8.1)
i18n (0.8.6)
metaclass (0.0.4)
minitest (5.10.1)
mocha (1.2.1)
Expand Down
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ begin

RSpec::Core::RakeTask.new(:spec)

task :default => :spec
task :test => :spec
rescue LoadError
# no rspec available
Expand Down
8 changes: 8 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [1.3.0] - 2017-06-04
### Added
- Sending client_uuid on page_requested activities
- Supporting mobile sdk
### Fixed
- Using `request.env` instead of `env`

## [1.2.0] - 2017-06-04
### Fixed
- Default timeouts for post api requests
Expand All @@ -27,3 +34,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Constants on px_constants
- Cookie Validation flow when cookie score was over the configured threshold
- Using symbols instead of strings for requests body

2 changes: 1 addition & 1 deletion examples/app/controllers/home_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class HomeController < ApplicationController
include PxModule

before_filter :px_verify_request
before_action :px_verify_request

def index
end
Expand Down
58 changes: 39 additions & 19 deletions lib/perimeter_x.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
require 'concurrent'
require 'json'
require 'base64'
require 'perimeterx/configuration'
require 'perimeterx/utils/px_logger'
require 'perimeterx/utils/px_constants'
Expand All @@ -10,29 +13,46 @@
require 'perimeterx/internal/validators/perimeter_x_captcha_validator'

module PxModule

# Module expose API
def px_verify_request
verified, px_ctx = PerimeterX.instance.verify(env)
verified, px_ctx = PerimeterX.instance.verify(request.env)

# Invalidate _pxCaptcha, can be done only on the controller level
cookies[:_pxCaptcha] = { value: "", expires: -1.minutes.from_now }
cookies[:_pxCaptcha] = {value: "", expires: -1.minutes.from_now}

if (!verified)
unless verified
# In case custon block handler exists
if (PerimeterX.instance.px_config.key?(:custom_block_handler))
PerimeterX.instance.px_config[:logger].debug("PxModule[px_verify_request]: custom_block_handler triggered")
PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: custom_block_handler triggered')
return instance_exec(px_ctx, &PerimeterX.instance.px_config[:custom_block_handler])
else
# Generate template
PerimeterX.instance.px_config[:logger].debug("PxModule[px_verify_request]: sending default block page")
PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: sending default block page')
html = PxTemplateFactory.get_template(px_ctx, PerimeterX.instance.px_config)
response.headers["Content-Type"] = "text/html"
response.headers['Content-Type'] = 'text/html'
response.status = 403
render :html => html
# Web handler
if px_ctx.context[:cookie_origin] == 'cookie'
PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: web block')
response.headers['Content-Type'] = 'text/html'
render :html => html
else # Mobile SDK
PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: mobile sdk block')
response.headers['Content-Type'] = 'application/json'
hash_json = {
:action => px_ctx.context[:block_action],
:uuid => px_ctx.context[:uuid],
:vid => px_ctx.context[:vid],
:appId => PerimeterX.instance.px_config[:app_id],
:page => Base64.strict_encode64(html),
:collectorUrl => "https://collector-#{PerimeterX.instance.px_config[:app_id]}.perimeterx.net"
}
render :json => hash_json
end
end
end

# Request was verified
return verified
end

Expand All @@ -41,7 +61,7 @@ def self.configure(params)
end


# PerimtereX Module
# PerimeterX Module
class PerimeterX
@@__instance = nil
@@mutex = Mutex.new
Expand All @@ -62,19 +82,19 @@ def self.configure(params)

def self.instance
return @@__instance if !@@__instance.nil?
raise Exception.new("Please initialize perimeter x first")
raise Exception.new('Please initialize perimeter x first')
end


#Instance Methods
def verify(env)
begin
@logger.debug("PerimeterX[pxVerify]")
req = ActionDispatch::Request.new(env)
@logger.debug('PerimeterX[pxVerify]')
if (!@px_config[:module_enabled])
@logger.warn("Module is disabled")
@logger.warn('Module is disabled')
return true
end
req = ActionDispatch::Request.new(env)
px_ctx = PerimeterXContext.new(@px_config, req)

# Captcha phase
Expand All @@ -96,7 +116,7 @@ def verify(env)
end
rescue Exception => e
@logger.error("#{e.backtrace.first}: #{e.message} (#{e.class})")
e.backtrace.drop(1).map { |s| @logger.error("\t#{s}") }
e.backtrace.drop(1).map {|s| @logger.error("\t#{s}")}
return true
end
end
Expand All @@ -111,11 +131,11 @@ def verify(env)
@px_cookie_validator = PerimeterxCookieValidator.new(@px_config)
@px_s2s_validator = PerimeterxS2SValidator.new(@px_config, @px_http_client)
@px_captcha_validator = PerimeterxCaptchaValidator.new(@px_config, @px_http_client)
@logger.debug("PerimeterX[initialize]")
@logger.debug('PerimeterX[initialize]Z')
end

private def handle_verification(px_ctx)
@logger.debug("PerimeterX[handle_verification]")
@logger.debug('PerimeterX[handle_verification]')
@logger.debug("PerimeterX[handle_verification]: processing ended - score:#{px_ctx.context[:score]}, uuid:#{px_ctx.context[:uuid]}")

score = px_ctx.context[:score]
Expand All @@ -130,12 +150,12 @@ def verify(env)
@px_activity_client.send_block_activity(px_ctx)

# In case were in monitor mode, end here
if(@px_config[:module_mode] == PxModule::MONITOR_MODE)
@logger.debug("PerimeterX[handle_verification]: monitor mode is on, passing request")
if (@px_config[:module_mode] == PxModule::MONITOR_MODE)
@logger.debug('PerimeterX[handle_verification]: monitor mode is on, passing request')
return true
end

@logger.debug("PerimeterX[handle_verification]: verification ended, the request should be blocked")
@logger.debug('PerimeterX[handle_verification]: verification ended, the request should be blocked')

return false, px_ctx
end
Expand Down
4 changes: 2 additions & 2 deletions lib/perimeterx/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Configuration
:api_connect_timeout => 1,
:api_timeout => 1,
:max_buffer_len => 10,
:send_page_activities => false,
:send_page_activities => true,
:send_block_activities => true,
:sdk_name => PxModule::SDK_NAME,
:debug => false,
Expand All @@ -31,7 +31,7 @@ class Configuration

def initialize(params)
PX_DEFAULT[:perimeterx_server_host] = "https://sapi-#{params[:app_id].downcase}.perimeterx.net"
@configuration = PX_DEFAULT.merge(params);
@configuration = PX_DEFAULT.merge(params)
@configuration[:logger] = PxLogger.new(@configuration[:debug])
end
end
Expand Down
11 changes: 7 additions & 4 deletions lib/perimeterx/internal/clients/perimeter_x_activity_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def send_to_perimeterx(activity_type, px_ctx, details = [])
end

details[:module_version] = @px_config[:sdk_name]
details[:cookie_origin] = px_ctx.context[:cookie_origin]

px_data = {
:type => activity_type,
:headers => format_headers(px_ctx),
Expand Down Expand Up @@ -53,9 +55,9 @@ def send_block_activity(px_ctx)
end

details = {
:block_uuid => px_ctx.context[:uuid],
:block_score => px_ctx.context[:score],
:block_reason => px_ctx.context[:block_reason]
:block_uuid => px_ctx.context[:uuid],
:block_score => px_ctx.context[:score],
:block_reason => px_ctx.context[:blocking_reason]
}

send_to_perimeterx(PxModule::BLOCK_ACTIVITY, px_ctx, details)
Expand All @@ -70,7 +72,8 @@ def send_page_requested_activity(px_ctx)

details = {
:http_version => px_ctx.context[:http_version],
:http_method => px_ctx.context[:http_method]
:http_method => px_ctx.context[:http_method],
:client_uuid => px_ctx.context[:uuid]
}

if (px_ctx.context.key?(:decoded_cookie))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module PxModule
class PerimeterxCookieV1 < PerimeterxCookie
class PerimeterxCookieV1 < PerimeterxPayload

attr_accessor :px_config, :px_ctx

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module PxModule
class PerimeterxCookieV3 < PerimeterxCookie
class PerimeterxCookieV3 < PerimeterxPayload

attr_accessor :px_config, :px_ctx, :cookie_hash

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
require 'perimeterx/internal/exceptions/px_cookie_decryption_exception'

module PxModule
class PerimeterxCookie
class PerimeterxPayload
attr_accessor :px_cookie, :px_config, :px_ctx, :cookie_secret, :decoded_cookie

def initialize(px_config)
Expand All @@ -13,7 +13,12 @@ def initialize(px_config)
end

def self.px_cookie_factory(px_ctx, px_config)
if (px_ctx.context[:px_cookie].key?(:v3))
if px_ctx.context[:cookie_origin] == 'header'
if (px_ctx.context[:px_cookie].key?(:v3))
return PerimeterxTokenV3.new(px_config,px_ctx)
end
return PerimeterxTokenV1.new(px_config,px_ctx)
elsif (px_ctx.context[:px_cookie].key?(:v3))
return PerimeterxCookieV3.new(px_config, px_ctx)
end
return PerimeterxCookieV1.new(px_config, px_ctx)
Expand Down Expand Up @@ -131,8 +136,8 @@ def hmac_valid?(hmac_str, cookie_hmac)
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @cookie_secret, hmac_str)
# ref: https://thisdata.com/blog/timing-attacks-against-string-comparison/
password_correct = ActiveSupport::SecurityUtils.secure_compare(
::Digest::SHA256.hexdigest(cookie_hmac),
::Digest::SHA256.hexdigest(hmac)
::Digest::SHA256.hexdigest(cookie_hmac),
::Digest::SHA256.hexdigest(hmac)
)

end
Expand Down
38 changes: 38 additions & 0 deletions lib/perimeterx/internal/payload/perimeter_x_token_v1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module PxModule
class PerimeterxTokenV1 < PerimeterxPayload

attr_accessor :px_config, :px_ctx

def initialize(px_config, px_ctx)
super(px_config)
@px_ctx = px_ctx
@px_cookie = px_ctx.get_px_cookie
@cookie_secret = px_config[:cookie_key]
@logger.debug('PerimeterxTokenV1[initialize]')
end

def cookie_score
return @decoded_cookie[:s][:b]
end

def cookie_hmac
return @decoded_cookie[:h]
end

def valid_format?(cookie)
return cookie.key?(:t) && cookie.key?(:s) && cookie[:s].key?(:b) && cookie.key?(:s) && cookie.key?(:v) && cookie.key?(:h)
end

def cookie_block_action
return 'c'
end

def secured?
hmac_str = "#{cookie_time}#{@decoded_cookie[:s][:a]}#{cookie_score}#{cookie_uuid}#{cookie_vid}"

return hmac_valid?(hmac_str, cookie_hmac)
end

end

end
36 changes: 36 additions & 0 deletions lib/perimeterx/internal/payload/perimeter_x_token_v3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module PxModule
class PerimeterxTokenV3 < PerimeterxPayload

attr_accessor :px_config, :px_ctx, :cookie_hash

def initialize(px_config, px_ctx)
super(px_config)
hash, cookie = px_ctx.get_px_cookie().split(':', 2)
@px_cookie = cookie
@cookie_hash = hash
@px_ctx = px_ctx
@cookie_secret = px_config[:cookie_key]
@logger.debug('PerimeterxTokenV3[initialize]')
end

def cookie_score
return @decoded_cookie[:s]
end

def cookie_hmac
return @cookie_hash
end

def valid_format?(cookie)
return cookie.key?(:t) && cookie.key?(:s) && cookie.key?(:u) && cookie.key?(:u) && cookie.key?(:a)
end

def cookie_block_action
@decoded_cookie[:a]
end

def secured?
return hmac_valid?(@px_cookie, cookie_hmac)
end
end
end
Loading

0 comments on commit 4ecb80e

Please sign in to comment.