Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
549 changes: 549 additions & 0 deletions .pylintrc

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ install:
- pip install -r requirements.txt
# command to run tests
script:
- python -m unittest discover -s ./tests -p 'px*'
- python -m unittest discover -s ./test -p 'test*'
- pylint -E perimeterx/
95 changes: 22 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
[![Build Status](https://travis-ci.org/PerimeterX/perimeterx-python-wsgi.svg?branch=master)](https://travis-ci.org/PerimeterX/perimeterx-python-wsgi)

![image](https://s.perimeterx.net/logo.png)

[PerimeterX](http://www.perimeterx.com) Python Middleware
=============================================================

> Latest stable version: [v2.0.0](link to package)

Table of Contents
-----------------
- [Installation](#installation)
- [Basic Usage Example](#basic_usage)
- [Required Configuration](#required_config)
- [Upgrading](#upgrading)
- [Advanced Blocking Response](#advanced_blocking_response)
- [Advanced Configuration Options](#configuration)
- [Optional Configuration](#configuration)
* [Module Enabled](#module_enabled)
* [Module Mode](#module_mode)
* [Blocking Score](#blocking_score)
Expand All @@ -22,205 +19,157 @@ Table of Contents
* [Whitelist Routes](#whitelist_routes)
* [Sensitive Headers](#sensitive_headers)
* [IP Headers](#ip_headers)
* [First Party Enabled](#first_party_enabled)
* [First-Party Enabled](#first_party_enabled)
* [Custom Request Handler](#custom_request_handler)
* [Additional Activity Handler](#additional_activity_handler)

## <a name="installation"></a> Installation
PerimeterX python middleware is installed via PIP:
PerimeterX Python middleware is installed via PIP:
`$ pip install perimeterx-python-wsgi`

## <a name="basic_usage"></a> Basic Usage Example
## <a name="upgrading"></a> Upgrading
Contact [PerimeterX Support](mailto: support@perimeterx.com) for details.
## <a name="required_config"></a> Required Configurations
To use PerimeterX middleware on a specific route follow this example:

```python
px_config = {
'app_id': 'APP_ID',
'cookie_key': 'COOKIE_KEY',
'auth_token': 'AUTH_TOKEN',
}

application = get_wsgi_application()
application = PerimeterX(application, px_config)

**Note:** app id, cookie secret and auth token are required fields.


```



- The PerimeterX **Application ID** / **AppId** and PerimeterX **Token** / **Auth Token** can be found in the Portal, in [Applications](https://console.perimeterx.com/#/app/applicationsmgmt).
- PerimeterX **Risk Cookie** / **Cookie Key** can be found in the portal, in [Policies](https://console.perimeterx.com/#/app/policiesmgmt).
The Policy from where the **Risk Cookie** / **Cookie Key** is taken must correspond with the Application from where the **Application ID** / **AppId** and PerimeterX **Token** / **Auth Token**.
For details on how to create a custom Captcha page, refer to the [documentation](https://console.perimeterx.com/docs/server_integration_new.html#custom-captcha-section)

## <a name="configuration"></a>Advanced Configuration Options

In addition to the basic installation configuration [above](#basicUsage), the following configurations options are available:

## <a name="configuration"></a>Optional Configuration
In addition to the basic installation configuration [above](#required_config), the following configurations options are available:
#### <a name="module_enabled"></a>Module Enabled
A boolean flag to enable/disable the PerimeterX Enforcer.

**Default:** true

```python
config = {
...
module_enabled: False
...
}
```

#### <a name="module_mode"></a>Module Mode
Sets the working mode of the Enforcer.

Possible values:

* `active_blocking` - Blocking Mode
* `monitor` - Monitoring Mode

**Default:** `monitor` - Monitor Mode

```python
config = {
...
module_mode: 'active_blocking'
...
}
```

#### <a name="blocking_score"></a>Blocking Score
Sets the minimum blocking score of a request.

Possible values:

* Any integer between 0 and 100.

**Default:** 100

```python
config = {
...
blocking_score: 100
...
}
```

#### <a name="send_page_activities"></a>Send Page Activities
A boolean flag to enable/disable sending activities and metrics to PerimeterX with each request. <br/>
Enable/disable sending activities and metrics to PerimeterX with each request. <br/>
Enabling this feature allows data to populate the PerimeterX Portal with valuable information, such as the number of requests blocked and additional API usage statistics.

**Default:** true

```python
config = {
...
send_page_activities: True
...
}
```


#### <a name="debug_mode"></a>Debug Mode
A boolean flag to enable/disable the debug log messages.

Enable/disable the debug log messages.
**Default:** False

```python
config = {
...
debug_mode: True
...
}
```

#### <a name="sensitive_routes"></a> Sensitive Routes
An array of route prefixes that trigger a server call to PerimeterX servers every time the page is viewed, regardless of viewing history.

**Default:** Empty

```python
const config = {
...
sensitive_routes: ['/login', '/user/checkout']
...
}
```

#### <a name="whitelist_routes"></a> Whitelist Routes
An array of route prefixes which will bypass enforcement (will never get scored).

**Default:** Empty

```python
config = {
...
whitelist_routes: ['/about-us', '/careers']
...
}
```

#### <a name="sensitive_headers"></a>Sensitive Headers
An array of headers that are not sent to PerimeterX servers on API calls.

**Default:** ['cookie', 'cookies']

```python
config = {
...
sensitive_headers: ['cookie', 'cookies', 'x-sensitive-header']
...
}
```

#### <a name="ip_headers"></a>IP Headers
An array of trusted headers that specify an IP to be extracted.

**Default:** Empty

```python
config = {
...
ip_headers: ['x-user-real-ip']
...
}
```

#### <a name="first_party_enabled"></a>First Party Enabled
A boolean flag to enable/disable first party mode.

#### <a name="first_party_enabled"></a>First-Party Enabled
Enable/disable First-Party mode.
**Default:** True

```python
const pxConfig = {
...
first_party_enabled: False
...
}
```

#### <a name="custom_request_handler"></a>Custom Request Handler
A Python function that adds a custom response handler to the request.
Do not forget to declare the function before using it in the config.
Custom request handler is triggered after PerimeterX's verification.
The custom function should handle the response (probably create a new one)
A Python function that adds a custom response handler to the request.</br>
You must declare the function before using it in the config.</br>
The Custom Request Handler is triggered after PerimeterX's verification.
The custom function should handle the response (most likely it will create a new response)
**Default:** Empty

```python
config = {
...
custom_request_handler: custom_request_handler_function,
...
}
```

#### <a name="additional_activity_handler"></a>Additional Activity Handler
A Python function that allows interaction with the request data collected by PerimeterX before the data is returned to the PerimeterX servers. Does not alter the response.

**Default:** Empty

```python
config = {
...
additional_activity_handler: additional_activity_handler_function,
...
}
```
```
2 changes: 1 addition & 1 deletion perimeterx/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import px_api
import px_constants
import px_utils
from perimeterx.px_proxy_handler import PXProxy
from perimeterx.px_proxy import PXProxy
from px_config import PxConfig


Expand Down
28 changes: 27 additions & 1 deletion perimeterx/px_blocker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import px_template
import px_constants
import json
import base64


class PXBlocker(object):
Expand Down Expand Up @@ -31,9 +32,23 @@ def handle_blocking(self, ctx, config):
blocking_props = self.prepare_properties(ctx, config)
blocking_response = self.mustache_renderer.render(px_template.get_template(px_constants.BLOCK_TEMPLATE),
blocking_props)

if ctx.is_mobile:
page_response = json.dumps({
'action': parse_action(ctx.block_action),
'uuid': ctx.uuid,
'vid': ctx.vid,
'appId': config.app_id,
'page': base64.b64encode(blocking_response),
'collectorURL': 'https://' + config.collector_host
})
return page_response, headers, status

if is_json_response:
blocking_response = json.dumps(blocking_props)
return str(blocking_response), headers, status

blocking_response = str(blocking_response)
return blocking_response, headers, status

def prepare_properties(self, ctx, config):
app_id = config.app_id
Expand Down Expand Up @@ -78,3 +93,14 @@ def is_json_response(self, ctx):
if header_item.strip() == 'application/json':
return True
return False


def parse_action(action):
if 'b' == action:
return 'block'
elif 'j' == action:
return 'challege'
elif 'r' == action:
return 'ratelimit'
else:
return 'captcha'
2 changes: 1 addition & 1 deletion perimeterx/px_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def __init__(self, environ, config):
if key == 'HTTP_' + MOBILE_SDK_HEADER.replace('-', '_').upper():
headers[MOBILE_SDK_HEADER] = environ.get(key, '')

mobile_header = headers.get(MOBILE_SDK_HEADER)
original_token = ''
mobile_header = headers.get(MOBILE_SDK_HEADER)
if mobile_header is None:
cookies = Cookie.SimpleCookie(environ.get('HTTP_COOKIE', ''))
cookie_keys = cookies.keys()
Expand Down
2 changes: 1 addition & 1 deletion perimeterx/px_httpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ def send(full_url, body, headers, config, method):

logger.debug('PerimeterX server call took ' + str(time.time() - start) + 'ms')
return response
except requests.exceptions as e:
except requests.exceptions.RequestException as e:
logger.debug('Received RequestException, message: ' + e.message)
return False
2 changes: 1 addition & 1 deletion perimeterx/px_original_token_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def verify(ctx, config):
original_token = ctx.original_token
version, no_version_token = original_token.split(':', 1)
px_cookie_builder = PxCookie(config)
px_cookie = px_cookie_builder.build_px_cookie({version: no_version_token},'')
px_cookie = px_cookie_builder.build_px_cookie({version: no_version_token}, '')

if not px_cookie.deserialize():
logger.error('Original token decryption failed, value:' + px_cookie.raw_cookie)
Expand Down
1 change: 0 additions & 1 deletion perimeterx/px_proxy_handler.py → perimeterx/px_proxy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import px_constants
import px_logger
import px_httpc
import px_utils
import base64
Expand Down
32 changes: 0 additions & 32 deletions perimeterx/px_token_v1.py

This file was deleted.

Loading