brute-guard 0.1.0
A Lightweight tool for preventing Brute Force Attacks
License: MIT
Requires Python: >=3.7
Keywords: security brute force attack authentications auth cybersecurity

# BruteGuard

- A Lightweight tool for preventing Brute Force Attacks.

## Features

- [Introduction](#introduction)
- [sqlite3](#sqlite3)
    - [Examples](#examples)
        - [Block IPs](#block-ips)
        - [Block usernames](#block-usernames)
    - [Attributes](#attributes)
    - [Default configs](#default-configurations)
- [Control object](#control-object)

## Introduction

- The goal of this tool is to block IPs or usernames that try to access some resource and fail consecutively in a time range.
 
- Currently this tool support the follow databases: 
    - [sqlite3](https://www.sqlite.org/docs.html)


### SQLite3

Block brute force attacks using sqlite3 under the hood.

#### Examples

##### Block IPs

- Below is an example that blocks a specific IP that fails 4 times (```failures```) consecutively in an interval of 1 second (```failure_time```). Each blocked IP will be blocked by 1 second (```blocked_expires_at```):

```python
import time

from brute_guard.sqlite3 import BruteGuard

bg = BruteGuard(
    blocked_expires_at="+1 second",
    failure_time="-1 second",
    failures=4,
    database_url="/tmp/bg.sqlite"
)
bg.control.create_tables() # Create table if exists

attacker_ip = "10.10.10.10"

# Registering fail access
bg.ip.access("fake-user1", attacker_ip, success=False)
bg.ip.access("fake-user2", attacker_ip, success=False)
bg.ip.access("some-user", attacker_ip, success=False)
bg.ip.access("some-user", attacker_ip, success=False)


if bg.ip.is_blocked(attacker_ip):
    print(f'"{attacker_ip}" is blocked.')

print("Sleeping 1 second")
time.sleep(1)


if bg.ip.is_blocked(attacker_ip) is False:
    print(f'"{attacker_ip}" is allow.')
```

##### Block usernames

- Below is an example that blocks a specific username that fails 3 times (```failures```) consecutively in an interval of 2 seconds (```failure_time```). Each blocked IP will be blocked by 2 seconds (```blocked_expires_at```):

```python
import time

from brute_guard.sqlite3 import BruteGuard

bg = BruteGuard(
    blocked_expires_at="+2 second",
    failure_time="-2 second",
    failures=3,
    database_url="/tmp/bg.sqlite"
)
bg.control.create_tables() # Create table if exists

username = "some.username"

# Registering fail access
bg.user.access(username, "3.10.24.4", success=False)
bg.user.access(username, "3.10.12.4", success=False)
bg.user.access(username, "4.15.10.4", success=False)


if bg.user.is_blocked(username):
    print(f'"{username}" is blocked.')


print("Sleeping 2 second")
time.sleep(2)


if bg.user.is_blocked(username) is False:
    print(f'"{username}" is allow.')
```


### Attributes

```python
@dataclass
class BruteGuard:
    access_expires_at: str = "+1 day"
    blocked_expires_at: str = "+1 hour"
    failure_time: str = "-10 second"
    failures: int = 8
    purge_time: Optional[timedelta] = timedelta(minutes=60)
    database_url: str = "/tmp/db.sqlite"
```

- **access_expires_at**: Time value for to expire access data using SQLite3 [Modifiers](https://www.sqlite.org/lang_datefunc.html) pattern. You must use a plus signal because the record will expire in the future.
    - Examples: "+1 day" | "+2 day" | etc.

- **blocked_expires_at**: Time value for to expire access data using SQLite3 [Modifiers](https://www.sqlite.org/lang_datefunc.html) pattern. You must use a plus signal because the record will expire in the future.
    - Examples: "+1 hour" | "+4 hour" | etc.

- **failures**: The amount of accepted consecutive failures. 

- **failure_time**: Time interval for verifying the consecutive failures.
    - Examples: "-5 second" | "-10 second" | etc.

- **purge_time**: Define time to purge expired data.
    - If you use this configuration in the next **access** will be executed a delete (with vacuum) of expired data.
    - To disable this expiration, set **purge_time** to **None**.

- **database_url**: Connection string for use in ```connect``` function. There is no any treatment in this value before passing to connect function, so, you can use any value accepted by  [connect](https://docs.python.org/3/library/sqlite3.html#sqlite3.connect) function.
    - We recommend that you use a database in a file.
    - We do not recommend that you use a memory database because if you start a multiprocessing application each process will have a different database.

**Note**: **failures** and **failure_time** defines the following: If an IP (or username) fails consecutively **failures** times in the last **failure_time** (interval time) this IP will be blocked.


#### Default configurations

- [*synchronous = off*](https://www.sqlite.org/pragma.html#pragma_synchronous)
- [*auto_vacuum = 1*](https://www.sqlite.org/pragma.html#pragma_auto_vacuum)
- [*cache_size = 2000*](https://www.sqlite.org/pragma.html#pragma_cache_size)

### Control object

- This object is used to control some operations in the database.

```python
from brute_guard.sqlite3 import BruteGuard

bg = BruteGuard()
bg.control.create_tables()
bg.control.drop_tables()
bg.control.purge_all()
bg.control.purge_expired()
```
- **create_tables**: Create ```access``` table and ```blocked``` table if not exist.
- **drop_tables**: Drop ```access``` table and ```blocked``` table if exist.
- **purge_all**: Purge all data from ```access``` table and ```blocked``` table.
- **purge_expired**: Purge all expired data from ```access``` table and ```blocked``` table.