BlackRed

BlackRed is a dynamic blacklisting library using Redis as a fast and reliable storage backend.

https://coveralls.io/repos/edelbluth/blackred/badge.svg?branch=master https://travis-ci.org/edelbluth/blackred.svg?branch=master https://readthedocs.org/projects/blackred/badge/?version=latest https://img.shields.io/badge/juergen-rocks-000033.svg?style=flat

How does it work?

Example: A user tries to log on a system and fails because of bad credentials or an inactivated account. This failure can be recorded with BlackRed. After three failures within a certain time the account gets locked for an extended period of time. This limits brute force attacks. All time periods are configurable.

In a desktop application you would record the username in question with BlackRed. In a web environment, the requester’s IP address would be the perfect.

In the redis database, two lists are kept: A watchlist that records the failures, and the blacklist that contains blocked items.

Requirements

BlackRed runs only under Python 3.3, 3.4, 3.5 and PyPy3. There’s no support for Python 2.

The only thing BlackRed needs is the redis package >= 2.10.

Jump Start

Installation can be done with pip install blackred. Usage is as easy, here an example for a simple user login:

import blackred

def login(username, password, request_ip):
    br = blackred.BlackRed()
    if br.is_blocked(request_ip):
        return False
    if not authenticate(username, password):
        br.log_fail(request_ip)
        return False
    return True

More examples: Example Usage.

Usage

Settings and Defaults

The default settings for new instances of BlackRed can be defined with the attributes of the Settings subclass.

For all available options, see the Settings documentation at the blackred API docs.

Example Usage

BlackRed features a very simple and intuitive API. Go to API documentation.

Simple Use

With everything in defaults (Host is localhost, port is 6379 and so on), you can use BlackRed right out of the box:

import blackred

def login(username, password, request_ip):
    br = blackred.BlackRed()
    if br.is_blocked(request_ip):
        return False
    if not authenticate(username, password):
        br.log_fail(request_ip)
        return False
    return True

The BlackRed constructor offers a wide range of on-the-fly settings for the new instance. To keep your code nice and clean when you use the same settings most of the time, you can use the Settings Predefinition.

Item Selection

For a web or network based application, a good item to check and block is the client IP address of the requestor.

In other environments, you might choose anything else that identifies a request source uniqually. Most important: The item must be represented by a str.

Settings Predefinition

To make the using of BlackRed easier, you can change the default settings for new instances by changing the values of the attributes of the BlackRed.Settings class. Changes are only valid for new instances created after the changes. Already existing instances are not changed.

Note

BlackRed versions before 0.3.0 had a different settings approach. Settings where separated into global settings and settings for new instances. With 0.3.0, all settings are only valid for new instances. Keep that in mind when upgrading from an older version.

Example: Enable Anonymization by default:

import blackred

blackred.BlackRed.Settings.ANONYMIZATION = True

def login(username, password, request_ip):
    br = blackred.BlackRed()
    if br.is_blocked(request_ip):
        return False
    if not authenticate(username, password):
        br.log_fail(request_ip)
        return False
    return True

Use Unix Socket instead of TCP/IP connection

If you want to use a unix socket to connect, set the redis_use_socket constructor parameter to True and provide the absolute path to the socket with the redis_host parameter, example:

import blackred

br = blackred.BlackRed(redis_host='/var/run/redis/redis.sock', redis_use_socket=True)

As always, those settings can be predefined (see Settings Predefinition) as defaults for all new instances:

import blackred

blackred.BlackRed.Settings.REDIS_USE_SOCKET = True
blackred.BlackRed.Settings.REDIS_HOST = '/var/run/redis/redis.sock'

Anonymization

Sometimes it’s necessary to hash the item’s values to ensure privacy of the requester. BlackRed can easily support you. Just set the ANONYMIZATION attribute to True:

import blackred

blackred.BlackRed.Settings.ANONYMIZATION = True

Use a Redis password (aka Redis AUTH feature)

To activate authentication, you can use the redis_auth parameter of the constructor:

import blackred

br = blackred.BlackRed(redis_auth='my_password')

As always, this settings can be predefined (see Settings Predefinition) as default for all new instances:

import blackred

blackred.BlackRed.Settings.REDIS_AUTH = 'my_password'

API Documentation

blackred package

The complete blackred.blackred module is imported in the __init__.py of the blackred module. So you can use blackred instead of blackred.blackred in your code.

For usage examples, please see the Examples Page.

blackred.blackred module

Copyright 2015 Juergen Edelbluth

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

class blackred.blackred.BlackRed(redis_host: str=None, redis_port: int=None, redis_db: int=None, redis_use_socket: bool=None, redis_auth: str=None)[source]

Bases: object

Create an Instance of BlackRed with configuration for Redis connection

Parameters:
  • redis_host (str) – Hostname, IP Address or Socket
  • redis_port (int) – Port Number
  • redis_db (int) – DB Number
  • redis_use_socket (bool) – True, when a socket should be used instead the TCP/IP connection
  • redis_auth (str) – If set, the redis AUTH command will be used with the given password
class Settings[source]

Bases: object

All the attributes of this class are used as default settings for new BlackRed instances.

ANONYMIZATION = False

If required, you can turn this on (set it to True) when BlackRed should not store IP addresses, usernames etc. as plain text. A hash value is used instead.

This setting might be necessary to get data protection policy compliant.

Type:bool
BLACKLIST_KEY_TEMPLATE = 'BlackRed:BlackList:{:s}'

Template for generating blacklist entries.

Defaults to 'BlackRed:BlackList:{:s}' and should only be changed when this collides with one of your namespaces.

Type:str
BLACKLIST_REFRESH_TTL_ON_HIT = True

If an item is already on the blacklist and is checked with BlackRed.is_blocked or BlackRed.is_not_blocked while on the blacklist, the time to live for the blacklist entry is reset to BlackRed.Settings.BLACKLIST_TTL_SECONDS.

So if this is set to True (that’s the default value) and a blocked user tries to login after 12 hours after blacklisting, his blacklist time is increased to another 24 hours.

Type:bool
BLACKLIST_TTL_SECONDS = 86400

Time in seconds for an item to stay on the blacklist.

Defaults to 86400 (24 hours). After that time, the entry is deleted automatically.

Type:int
REDIS_AUTH = None

If the redis connection requires authentication, the password can be predefined here.

If set to None (which is the default), authentication will not be used.

Type:str
REDIS_DB = 0

The Redis database number, defaults to 0

Type:int
REDIS_HOST = 'localhost'

Hostname, IP Address or socket, defaults to 'localhost'

Type:str
REDIS_PORT = 6379

TCP-Port for Redis, defaults to 6379

Type:int
REDIS_USE_SOCKET = False

Tell the BlackRed class to use a unix socket instead of a TCP/IP connection.

Defaults to False

Type:bool
SALT_KEY = 'BlackRed:AnonymizationListSecret'

The key for saving the individual salt for anonymization.

Defaults to BlackRed:AnonymizationListSecret and should only be changed when it collides with one of your namespaces.

Type:str
WATCHLIST_KEY_TEMPLATE = 'BlackRed:WatchList:{:s}'

Template for generating watchlist entries.

Defaults to 'BlackRed:WatchList:{:s}' and should only be changed when this collides with one of your namespaces.

Type:str
WATCHLIST_TO_BLACKLIST_THRESHOLD = 3

Number of fails for an item to get from the watchlist to the blacklist. Defaults to 3.

Type:int
WATCHLIST_TTL_SECONDS = 180

Time in seconds for an item to stay on the watchlist.

If after a logged failure a new failure appears within this time frame, the fail count is increased. If not, the entry is deleted automatically. Defaults to 180 (3 minutes).

Type:int
BlackRed.get_blacklist_ttl(item: str) → int[source]

Get the amount of time a specific item will remain on the blacklist.

Parameters:item (str) – The item to get the TTL for on the blacklist
Returns:Time in seconds. Returns None for a non-existing element.
Return type:int
BlackRed.get_watchlist_ttl(item: str) → int[source]

Get the amount of time a specific item will remain on the watchlist.

Parameters:item (str) – The item to get the TTL for on the watchlist
Returns:Time in seconds. Returns None for a non-existing element
Return type:int
BlackRed.is_blocked(item: str) → bool[source]

Check if an item is on the blacklist

Parameters:item (str) – The item to check
Returns:True, when the item is on the blacklist
Return type:bool
BlackRed.is_not_blocked(item: str) → bool[source]

Check if an item is _not_ already on the blacklist

Parameters:item (str) – The item to check
Returns:True, when the item is _not_ on the blacklist
Return type:bool
BlackRed.log_fail(item: str) → None[source]

Log a failed action for an item. If the fail count for this item reaches the threshold, the item is moved to the blacklist.

Parameters:item (str) – The item to log
BlackRed.unblock(item: str) → None[source]

Unblock an item and/or reset it’s fail count

Parameters:item (str) – The item to unblock
blackred.blackred.create_salt(length: int=128) → bytes[source]

Create a new salt

Parameters:length (int) – How many bytes should the salt be long?
Returns:The salt
Return type:bytes

Indices and tables