perimeter_x 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/changelog.md +11 -1
- data/lib/perimeter_x.rb +35 -28
- data/lib/perimeterx/configuration.rb +1 -1
- data/lib/perimeterx/internal/perimeter_x_context.rb +3 -0
- data/lib/perimeterx/internal/validators/perimeter_x_captcha_validator.rb +15 -15
- data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +27 -10
- data/lib/perimeterx/utils/px_constants.rb +3 -2
- data/lib/perimeterx/utils/px_template_factory.rb +1 -1
- data/lib/perimeterx/utils/templates/block.mobile.mustache +2 -2
- data/lib/perimeterx/utils/templates/block.mustache +1 -1
- data/lib/perimeterx/utils/templates/funcaptcha.mobile.mustache +178 -0
- data/lib/perimeterx/utils/templates/funcaptcha.mustache +191 -0
- data/lib/perimeterx/utils/templates/{captcha.mobile.mustache → recaptcha.mobile.mustache} +2 -2
- data/lib/perimeterx/utils/templates/recaptcha.mustache +176 -0
- data/lib/perimeterx/version.rb +1 -1
- data/readme.md +47 -26
- metadata +6 -4
- data/lib/perimeterx/utils/templates/captcha.mustache +0 -185
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3dca9964af5aa39c20643f7e5d6f00271862f0de
|
4
|
+
data.tar.gz: e0f2a81bce3aa7c8b8eb0b0b73d70cc29f272dbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 257d51330a1dc51e1ca2559559d7eb4e0ca6ebc891fd8bfcdd0171b4e31a5a3b4455f4c3b4227b6db7d403eeecb3bb1bb4b62e4e7cf72024d6efa89f5636bd3f
|
7
|
+
data.tar.gz: 4cd74c725090a46718716b782551e1e5bae7efb939818c1b34970bde070897d6d53334b5c54bb6df7f4a24291dd5ed608e2e39723e008647a07c4e2da8de6b07
|
data/changelog.md
CHANGED
@@ -5,7 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
7
7
|
|
8
|
-
## [1.
|
8
|
+
## [1.4.0] - 2018-03-18
|
9
|
+
### Fixed
|
10
|
+
- Incorrect assigment for s2s_call_reason
|
11
|
+
- Fixed empty token result correct s2s reason
|
12
|
+
|
13
|
+
### Added
|
14
|
+
- Added support to captcha api v2
|
15
|
+
- Mobile sdk support for special tokens 1/2/3
|
16
|
+
|
17
|
+
|
18
|
+
## [1.3.0] - 2017-07-27
|
9
19
|
### Added
|
10
20
|
- Sending client_uuid on page_requested activities
|
11
21
|
- Supporting mobile sdk
|
data/lib/perimeter_x.rb
CHANGED
@@ -15,37 +15,47 @@ require 'perimeterx/internal/validators/perimeter_x_captcha_validator'
|
|
15
15
|
module PxModule
|
16
16
|
# Module expose API
|
17
17
|
def px_verify_request
|
18
|
-
|
18
|
+
px_ctx = PerimeterX.instance.verify(request.env)
|
19
|
+
px_config = PerimeterX.instance.px_config
|
20
|
+
msg_title = 'PxModule[px_verify_request]'
|
21
|
+
|
22
|
+
# In case custom verification handler is in use
|
23
|
+
if px_config.key?(:custom_verification_handler)
|
24
|
+
px_config[:logger].debug("#{msg_title}: custom_verification_handler triggered")
|
25
|
+
return instance_exec(px_ctx, &px_config[:custom_verification_handler])
|
26
|
+
end
|
19
27
|
|
20
28
|
# Invalidate _pxCaptcha, can be done only on the controller level
|
21
29
|
cookies[:_pxCaptcha] = {value: "", expires: -1.minutes.from_now}
|
22
30
|
|
23
|
-
unless verified
|
24
|
-
# In case
|
25
|
-
if
|
26
|
-
|
27
|
-
|
31
|
+
unless px_ctx.nil? || px_ctx.context[:verified]
|
32
|
+
# In case custom block handler exists (soon to be deprecated)
|
33
|
+
if px_config.key?(:custom_block_handler)
|
34
|
+
px_config[:logger].debug("#{msg_title}: custom_block_handler triggered")
|
35
|
+
px_config[:logger].debug(
|
36
|
+
"#{msg_title}: Please note that custom_block_handler is deprecated. Use custom_verification_handler instead.")
|
37
|
+
return instance_exec(px_ctx, &px_config[:custom_block_handler])
|
28
38
|
else
|
29
39
|
# Generate template
|
30
|
-
|
31
|
-
html = PxTemplateFactory.get_template(px_ctx,
|
40
|
+
px_config[:logger].debug("#{msg_title}: sending default block page")
|
41
|
+
html = PxTemplateFactory.get_template(px_ctx, px_config)
|
32
42
|
response.headers['Content-Type'] = 'text/html'
|
33
43
|
response.status = 403
|
34
44
|
# Web handler
|
35
45
|
if px_ctx.context[:cookie_origin] == 'cookie'
|
36
|
-
|
46
|
+
px_config[:logger].debug('#{msg_title}: web block')
|
37
47
|
response.headers['Content-Type'] = 'text/html'
|
38
48
|
render :html => html
|
39
49
|
else # Mobile SDK
|
40
|
-
|
50
|
+
px_config[:logger].debug("#{msg_title}: mobile sdk block")
|
41
51
|
response.headers['Content-Type'] = 'application/json'
|
42
52
|
hash_json = {
|
43
53
|
:action => px_ctx.context[:block_action],
|
44
54
|
:uuid => px_ctx.context[:uuid],
|
45
55
|
:vid => px_ctx.context[:vid],
|
46
|
-
:appId =>
|
56
|
+
:appId => px_config[:app_id],
|
47
57
|
:page => Base64.strict_encode64(html),
|
48
|
-
:collectorUrl => "https://collector-#{
|
58
|
+
:collectorUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net"
|
49
59
|
}
|
50
60
|
render :json => hash_json
|
51
61
|
end
|
@@ -53,7 +63,7 @@ module PxModule
|
|
53
63
|
end
|
54
64
|
|
55
65
|
# Request was verified
|
56
|
-
return verified
|
66
|
+
return px_ctx.nil? ? true : px_ctx.context[:verified]
|
57
67
|
end
|
58
68
|
|
59
69
|
def self.configure(params)
|
@@ -90,34 +100,30 @@ module PxModule
|
|
90
100
|
def verify(env)
|
91
101
|
begin
|
92
102
|
@logger.debug('PerimeterX[pxVerify]')
|
93
|
-
if
|
103
|
+
if !@px_config[:module_enabled]
|
94
104
|
@logger.warn('Module is disabled')
|
95
|
-
return
|
105
|
+
return nil
|
96
106
|
end
|
97
107
|
req = ActionDispatch::Request.new(env)
|
98
108
|
px_ctx = PerimeterXContext.new(@px_config, req)
|
99
109
|
|
100
110
|
# Captcha phase
|
101
111
|
captcha_verified, px_ctx = @px_captcha_validator.verify(px_ctx)
|
102
|
-
if
|
112
|
+
if captcha_verified
|
103
113
|
return handle_verification(px_ctx)
|
104
114
|
end
|
105
115
|
|
106
116
|
# Cookie phase
|
107
117
|
cookie_verified, px_ctx = @px_cookie_validator.verify(px_ctx)
|
108
|
-
if
|
118
|
+
if !cookie_verified
|
109
119
|
@px_s2s_validator.verify(px_ctx)
|
110
120
|
end
|
111
121
|
|
112
|
-
|
113
|
-
return @px_config[:custom_verification_handler].call(px_ctx.context)
|
114
|
-
else
|
115
|
-
return handle_verification(px_ctx)
|
116
|
-
end
|
122
|
+
return handle_verification(px_ctx)
|
117
123
|
rescue Exception => e
|
118
124
|
@logger.error("#{e.backtrace.first}: #{e.message} (#{e.class})")
|
119
125
|
e.backtrace.drop(1).map {|s| @logger.error("\t#{s}")}
|
120
|
-
return
|
126
|
+
return nil
|
121
127
|
end
|
122
128
|
end
|
123
129
|
|
@@ -139,25 +145,26 @@ module PxModule
|
|
139
145
|
@logger.debug("PerimeterX[handle_verification]: processing ended - score:#{px_ctx.context[:score]}, uuid:#{px_ctx.context[:uuid]}")
|
140
146
|
|
141
147
|
score = px_ctx.context[:score]
|
148
|
+
px_ctx.context[:verified] = score < @px_config[:blocking_score]
|
142
149
|
# Case PASS request
|
143
|
-
if
|
150
|
+
if px_ctx.context[:verified]
|
144
151
|
@logger.debug("PerimeterX[handle_verification]: score:#{score} < blocking score, passing request")
|
145
152
|
@px_activity_client.send_page_requested_activity(px_ctx)
|
146
|
-
return
|
153
|
+
return px_ctx
|
147
154
|
end
|
148
155
|
|
149
156
|
# Case blocking activity
|
150
157
|
@px_activity_client.send_block_activity(px_ctx)
|
151
158
|
|
152
159
|
# In case were in monitor mode, end here
|
153
|
-
if
|
160
|
+
if @px_config[:module_mode] == PxModule::MONITOR_MODE
|
154
161
|
@logger.debug('PerimeterX[handle_verification]: monitor mode is on, passing request')
|
155
|
-
return
|
162
|
+
return px_ctx
|
156
163
|
end
|
157
164
|
|
158
165
|
@logger.debug('PerimeterX[handle_verification]: verification ended, the request should be blocked')
|
159
166
|
|
160
|
-
return
|
167
|
+
return px_ctx
|
161
168
|
end
|
162
169
|
|
163
170
|
private_class_method :new
|
@@ -14,6 +14,7 @@ module PxModule
|
|
14
14
|
@context[:px_cookie] = Hash.new
|
15
15
|
@context[:headers] = Hash.new
|
16
16
|
@context[:cookie_origin] = 'cookie'
|
17
|
+
@context[:made_s2s_risk_api_call] = false
|
17
18
|
cookies = req.cookies
|
18
19
|
|
19
20
|
# Get token from header
|
@@ -24,6 +25,8 @@ module PxModule
|
|
24
25
|
exploded_token = token.split(':', 2)
|
25
26
|
cookie_sym = "v#{exploded_token[0]}".to_sym
|
26
27
|
@context[:px_cookie][cookie_sym] = exploded_token[1]
|
28
|
+
else # TOKEN_HEADER exists yet there's no ':' delimiter - may indicate an error (storing original value)
|
29
|
+
@context[:px_cookie] = req.headers[PxModule::TOKEN_HEADER]
|
27
30
|
end
|
28
31
|
elsif !cookies.empty? # Get cookie from jar
|
29
32
|
# Prepare hashed cookies
|
@@ -7,17 +7,19 @@ module PxModule
|
|
7
7
|
super(px_config, http_client)
|
8
8
|
end
|
9
9
|
|
10
|
-
def send_captcha_request(
|
10
|
+
def send_captcha_request(captcha, px_ctx)
|
11
11
|
|
12
12
|
request_body = {
|
13
13
|
:request => {
|
14
14
|
:ip => px_ctx.context[:ip],
|
15
15
|
:headers => format_headers(px_ctx),
|
16
|
-
:uri => px_ctx.context[:uri]
|
16
|
+
:uri => px_ctx.context[:uri],
|
17
|
+
:captchaType => @px_config[:captcha_provider]
|
18
|
+
},
|
19
|
+
:additional => {
|
20
|
+
:module_version => @px_config[:sdk_name]
|
17
21
|
},
|
18
22
|
:pxCaptcha => captcha,
|
19
|
-
:vid => vid,
|
20
|
-
:uuid => uuid,
|
21
23
|
:hostname => px_ctx.context[:hostname]
|
22
24
|
}
|
23
25
|
|
@@ -25,30 +27,28 @@ module PxModule
|
|
25
27
|
headers = {
|
26
28
|
"Authorization" => "Bearer #{@px_config[:auth_token]}" ,
|
27
29
|
"Content-Type" => "application/json"
|
28
|
-
}
|
30
|
+
}
|
29
31
|
|
30
|
-
return @http_client.post(PxModule::
|
32
|
+
return @http_client.post(PxModule::API_CAPTCHA, request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
|
31
33
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def verify(px_ctx)
|
35
37
|
captcha_validated = false
|
36
38
|
begin
|
37
|
-
if
|
39
|
+
if !px_ctx.context.key?(:px_captcha)
|
38
40
|
return captcha_validated, px_ctx
|
39
41
|
end
|
40
|
-
captcha
|
41
|
-
if captcha.nil?
|
42
|
+
captcha = px_ctx.context[:px_captcha]
|
43
|
+
if captcha.nil?
|
42
44
|
return captcha_validated, px_ctx
|
43
45
|
end
|
44
46
|
|
45
|
-
|
46
|
-
px_ctx.context[:uuid] = uuid
|
47
|
-
response = send_captcha_request(vid, uuid, captcha, px_ctx)
|
47
|
+
response = send_captcha_request(captcha, px_ctx)
|
48
48
|
|
49
|
-
if
|
49
|
+
if response.success?
|
50
50
|
response_body = eval(response.body)
|
51
|
-
if
|
51
|
+
if response_body[:status] == 0
|
52
52
|
captcha_validated = true
|
53
53
|
end
|
54
54
|
end
|
@@ -56,7 +56,7 @@ module PxModule
|
|
56
56
|
return captcha_validated, px_ctx
|
57
57
|
|
58
58
|
rescue Exception => e
|
59
|
-
@logger.error("PerimeterxCaptchaValidator[verify]: failed, returning false")
|
59
|
+
@logger.error("PerimeterxCaptchaValidator[verify]: failed, returning false => #{e.message}")
|
60
60
|
return captcha_validated, px_ctx
|
61
61
|
end
|
62
62
|
end
|
@@ -18,16 +18,34 @@ module PxModule
|
|
18
18
|
|
19
19
|
def verify(px_ctx)
|
20
20
|
begin
|
21
|
-
#
|
22
|
-
if px_ctx.context[:
|
23
|
-
|
21
|
+
# Mobile Error cases
|
22
|
+
if px_ctx.context[:cookie_origin] == 'header'
|
23
|
+
if px_ctx.context[:px_cookie].to_s.empty?
|
24
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: Empty token value - decryption failed")
|
25
|
+
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
26
|
+
return false, px_ctx
|
27
|
+
elsif px_ctx.context[:px_cookie] == "1"
|
28
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
|
29
|
+
px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
|
30
|
+
return false, px_ctx
|
31
|
+
elsif px_ctx.context[:px_cookie] == "2" # Mobile SDK connection error
|
32
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: mobile sdk connection error")
|
33
|
+
px_ctx.context[:s2s_call_reason] = PxModule::MOBILE_SDK_CONNECTION_ERROR
|
34
|
+
return false, px_ctx
|
35
|
+
elsif px_ctx.context[:px_cookie] == "3" # Mobile SDK pinning error
|
36
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: mobile sdk pinning error")
|
37
|
+
px_ctx.context[:s2s_call_reason] = PxModule::MOBILE_SDK_PINNING_ERROR
|
38
|
+
return false, px_ctx
|
39
|
+
end
|
40
|
+
elsif px_ctx.context[:px_cookie].empty?
|
41
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
|
24
42
|
px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
|
25
43
|
return false, px_ctx
|
26
44
|
end
|
27
45
|
|
28
46
|
# Deserialize cookie start
|
29
47
|
cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
|
30
|
-
if
|
48
|
+
if !cookie.deserialize()
|
31
49
|
@logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
|
32
50
|
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
33
51
|
return false, px_ctx
|
@@ -39,26 +57,25 @@ module PxModule
|
|
39
57
|
px_ctx.context[:block_action] = px_ctx.set_block_action_type(cookie.cookie_block_action())
|
40
58
|
px_ctx.context[:cookie_hmac] = cookie.cookie_hmac()
|
41
59
|
|
42
|
-
if
|
60
|
+
if cookie.expired?
|
43
61
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie expired")
|
44
62
|
px_ctx.context[:s2s_call_reason] = PxModule::EXPIRED_COOKIE
|
45
63
|
return false, px_ctx
|
46
64
|
end
|
47
65
|
|
48
|
-
if
|
66
|
+
if cookie.high_score?
|
49
67
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie high score")
|
50
|
-
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_HIGH_SCORE
|
51
68
|
px_ctx.context[:blocking_reason] = 'cookie_high_score'
|
52
69
|
return true, px_ctx
|
53
70
|
end
|
54
71
|
|
55
|
-
if
|
72
|
+
if !cookie.secured?
|
56
73
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie invalid hmac")
|
57
|
-
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_VALIDATION_FAILED
|
74
|
+
px_ctx.context[:s2s_call_reason] = PxModule:: COOKIE_VALIDATION_FAILED
|
58
75
|
return false, px_ctx
|
59
76
|
end
|
60
77
|
|
61
|
-
if
|
78
|
+
if px_ctx.context[:sensitive_route]
|
62
79
|
@logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
|
63
80
|
px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
|
64
81
|
return false, px_ctx
|
@@ -10,7 +10,7 @@ module PxModule
|
|
10
10
|
|
11
11
|
# Routes
|
12
12
|
API_V1_S2S = '/api/v1/collector/s2s'
|
13
|
-
|
13
|
+
API_CAPTCHA = '/api/v2/risk/captcha'
|
14
14
|
API_V2_RISK = '/api/v2/risk'
|
15
15
|
|
16
16
|
# Activity Types
|
@@ -28,7 +28,6 @@ module PxModule
|
|
28
28
|
|
29
29
|
# Templates
|
30
30
|
BLOCK_TEMPLATE = 'block'
|
31
|
-
CAPTCHA_TEMPLATE = 'captcha'
|
32
31
|
TEMPLATE_EXT = '.mustache'
|
33
32
|
|
34
33
|
|
@@ -48,4 +47,6 @@ module PxModule
|
|
48
47
|
|
49
48
|
# Mobile SDK
|
50
49
|
TOKEN_HEADER = 'X-PX-AUTHORIZATION'
|
50
|
+
MOBILE_SDK_CONNECTION_ERROR = 'mobile_sdk_connection_error'
|
51
|
+
MOBILE_SDK_PINNING_ERROR = 'mobile_sdk_pinning_error'
|
51
52
|
end
|
@@ -11,7 +11,7 @@ module PxModule
|
|
11
11
|
end
|
12
12
|
|
13
13
|
logger.debug('PxTemplateFactory[get_template]: rendering template')
|
14
|
-
template_type = px_ctx.context[:block_action] == 'captcha' ?
|
14
|
+
template_type = px_ctx.context[:block_action] == 'captcha' ? px_config[:captcha_provider].downcase : BLOCK_TEMPLATE
|
15
15
|
|
16
16
|
template_postfix = ''
|
17
17
|
if px_ctx.context[:cookie_origin] == 'header'
|
@@ -119,7 +119,7 @@
|
|
119
119
|
<div class="page-footer">
|
120
120
|
<p>
|
121
121
|
Powered by
|
122
|
-
<a href="https://www.perimeterx.com">PerimeterX</a>
|
122
|
+
<a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
|
123
123
|
, Inc.
|
124
124
|
</p>
|
125
125
|
</div>
|
@@ -130,4 +130,4 @@
|
|
130
130
|
<script src="{{jsRef}}"></script>
|
131
131
|
{{/ jsRef }}
|
132
132
|
</body>
|
133
|
-
</html>
|
133
|
+
</html>
|
@@ -0,0 +1,178 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
<title>Access to this page has been denied.</title>
|
7
|
+
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300" rel="stylesheet">
|
8
|
+
<style>
|
9
|
+
html, body {
|
10
|
+
margin: 0;
|
11
|
+
padding: 0;
|
12
|
+
font-family: 'Open Sans', sans-serif;
|
13
|
+
color: #000;
|
14
|
+
}
|
15
|
+
|
16
|
+
a {
|
17
|
+
color: #c5c5c5;
|
18
|
+
text-decoration: none;
|
19
|
+
}
|
20
|
+
|
21
|
+
.container {
|
22
|
+
align-items: center;
|
23
|
+
display: flex;
|
24
|
+
flex: 1;
|
25
|
+
justify-content: space-between;
|
26
|
+
flex-direction: column;
|
27
|
+
height: 100%;
|
28
|
+
}
|
29
|
+
|
30
|
+
.container > div {
|
31
|
+
width: 100%;
|
32
|
+
display: flex;
|
33
|
+
justify-content: center;
|
34
|
+
}
|
35
|
+
|
36
|
+
.container > div > div {
|
37
|
+
display: flex;
|
38
|
+
width: 80%;
|
39
|
+
}
|
40
|
+
|
41
|
+
.customer-logo-wrapper {
|
42
|
+
padding-top: 2rem;
|
43
|
+
flex-grow: 0;
|
44
|
+
background-color: #fff;
|
45
|
+
visibility: {{logoVisibility}};
|
46
|
+
}
|
47
|
+
|
48
|
+
.customer-logo {
|
49
|
+
border-bottom: 1px solid #000;
|
50
|
+
}
|
51
|
+
|
52
|
+
.customer-logo > img {
|
53
|
+
padding-bottom: 1rem;
|
54
|
+
max-height: 50px;
|
55
|
+
max-width: 100%;
|
56
|
+
}
|
57
|
+
|
58
|
+
.page-title-wrapper {
|
59
|
+
flex-grow: 2;
|
60
|
+
}
|
61
|
+
|
62
|
+
.page-title {
|
63
|
+
flex-direction: column-reverse;
|
64
|
+
}
|
65
|
+
|
66
|
+
.content-wrapper {
|
67
|
+
flex-grow: 5;
|
68
|
+
}
|
69
|
+
|
70
|
+
.content {
|
71
|
+
flex-direction: column;
|
72
|
+
}
|
73
|
+
|
74
|
+
.page-footer-wrapper {
|
75
|
+
align-items: center;
|
76
|
+
flex-grow: 0.2;
|
77
|
+
background-color: #000;
|
78
|
+
color: #c5c5c5;
|
79
|
+
font-size: 70%;
|
80
|
+
}
|
81
|
+
|
82
|
+
@media (min-width: 768px) {
|
83
|
+
html, body {
|
84
|
+
height: 100%;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
</style>
|
88
|
+
<!-- Custom CSS -->
|
89
|
+
{{# cssRef }}
|
90
|
+
<link rel="stylesheet" type="text/css" href="{{ . }}"/>
|
91
|
+
{{/ cssRef }}
|
92
|
+
<script src="https://funcaptcha.com/fc/api/?onload=loadFunCaptcha" async defer></script>
|
93
|
+
</head>
|
94
|
+
|
95
|
+
<body>
|
96
|
+
<section class="container">
|
97
|
+
<div class="customer-logo-wrapper">
|
98
|
+
<div class="customer-logo">
|
99
|
+
<img src="{{customLogo}}" alt="Logo"/>
|
100
|
+
</div>
|
101
|
+
</div>
|
102
|
+
<div class="page-title-wrapper">
|
103
|
+
<div class="page-title">
|
104
|
+
<h1>Please verify you are a human</h1>
|
105
|
+
</div>
|
106
|
+
</div>
|
107
|
+
<div class="content-wrapper">
|
108
|
+
<div class="content">
|
109
|
+
<p>
|
110
|
+
Please click "Verify" to continue
|
111
|
+
</p>
|
112
|
+
<div id="CAPTCHA"></div>
|
113
|
+
<p>
|
114
|
+
Access to this page has been denied because we believe you are using automation tools to browse the
|
115
|
+
website.
|
116
|
+
</p>
|
117
|
+
<p>
|
118
|
+
This may happen as a result of the following:
|
119
|
+
</p>
|
120
|
+
<ul>
|
121
|
+
<li>
|
122
|
+
Javascript is disabled or blocked by an extension (ad blockers for example)
|
123
|
+
</li>
|
124
|
+
<li>
|
125
|
+
Your browser does not support cookies
|
126
|
+
</li>
|
127
|
+
</ul>
|
128
|
+
<p>
|
129
|
+
Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
|
130
|
+
them from loading.
|
131
|
+
</p>
|
132
|
+
<p>
|
133
|
+
Reference ID: #{{refId}}
|
134
|
+
</p>
|
135
|
+
</div>
|
136
|
+
</div>
|
137
|
+
<div class="page-footer-wrapper">
|
138
|
+
<div class="page-footer">
|
139
|
+
<p>
|
140
|
+
Powered by
|
141
|
+
<a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
|
142
|
+
, Inc.
|
143
|
+
</p>
|
144
|
+
</div>
|
145
|
+
</div>
|
146
|
+
</section>
|
147
|
+
<!-- Captcha -->
|
148
|
+
<script>
|
149
|
+
|
150
|
+
function loadFunCaptcha() {
|
151
|
+
var vid = '{{vid}}';
|
152
|
+
var uuid = '{{uuid}}';
|
153
|
+
|
154
|
+
new FunCaptcha({
|
155
|
+
public_key: "19E4B3B8-6CBE-35CC-4205-FC79ECDDA765",
|
156
|
+
target_html: "CAPTCHA",
|
157
|
+
callback: function () {
|
158
|
+
var expiryUtc = new Date(Date.now() + 1000 * 10).toUTCString();
|
159
|
+
var pxCaptcha = "_pxCaptcha=" + btoa(JSON.stringify({r: document.getElementById("FunCaptcha-Token").value, u: uuid, v: vid}));
|
160
|
+
var cookieParts = [
|
161
|
+
pxCaptcha,
|
162
|
+
"; expires=",
|
163
|
+
expiryUtc,
|
164
|
+
"; path=/"
|
165
|
+
];
|
166
|
+
|
167
|
+
document.cookie = cookieParts.join("");
|
168
|
+
location.reload();
|
169
|
+
}
|
170
|
+
});
|
171
|
+
}
|
172
|
+
</script>
|
173
|
+
<!-- Custom Script -->
|
174
|
+
{{# jsRef }}
|
175
|
+
<script src="{{ . }}"></script>
|
176
|
+
{{/ jsRef }}
|
177
|
+
</body>
|
178
|
+
</html>
|