Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
49c614a
Added IP-based rate limiting for Copi (fixes #1877)
immortal71 Dec 3, 2025
c74db05
Add separate player creation rate limiting (addresses @sydseter feedb…
immortal71 Dec 27, 2025
c2d3f56
Bump actions/setup-node from 6.0.0 to 6.1.0
dependabot[bot] Dec 3, 2025
85d1f1b
Bump @sveltejs/kit from 2.49.0 to 2.49.1 in /cornucopia.owasp.org
dependabot[bot] Dec 3, 2025
ca372bb
Bump actions/checkout from 6.0.0 to 6.0.1
dependabot[bot] Dec 3, 2025
1c5dca2
Bump svelte from 5.45.3 to 5.45.4 in /cornucopia.owasp.org
dependabot[bot] Dec 3, 2025
032c1bc
Update acknowledgements on index.md
cw-owasp Dec 5, 2025
071dd6e
Remove duplicate 'nl' from languages list
sydseter Dec 7, 2025
21d2208
Add statistics section with SQL queries
sydseter Dec 7, 2025
1e8cac0
Bump svelte from 5.45.5 to 5.45.6 in /cornucopia.owasp.org
dependabot[bot] Dec 8, 2025
c697fc9
Bump black from 25.1.0 to 25.12.0
dependabot[bot] Dec 8, 2025
5d431e4
Bump github/codeql-action from 4.31.6 to 4.31.7
dependabot[bot] Dec 8, 2025
2ddb1a4
Bump pipenv from 2025.0.4 to 2025.1.1
dependabot[bot] Dec 8, 2025
9e86536
Bump pytest from 8.3.5 to 9.0.2
dependabot[bot] Dec 8, 2025
a912ed5
Bump urllib3 from 2.5.0 to 2.6.0 in the pip group across 1 directory
dependabot[bot] Dec 8, 2025
a777346
Bump pytest from 8.3.5 to 9.0.2
dependabot[bot] Dec 9, 2025
7ef2616
Bump coverage from 7.10.7 to 7.13.0
dependabot[bot] Dec 9, 2025
9728290
Bump mvdan/shfmt from `20597e9` to `e414177`
dependabot[bot] Dec 9, 2025
f62c2c1
Bump black from 25.1.0 to 25.12.0
dependabot[bot] Dec 9, 2025
4cf8d1d
Bump platformdirs from 4.4.0 to 4.5.1
dependabot[bot] Dec 9, 2025
b69b7ee
Bump urllib3 from 2.5.0 to 2.6.1
dependabot[bot] Dec 9, 2025
2914826
Bump urllib3 from 2.5.0 to 2.6.0 in the pip group across 1 directory
dependabot[bot] Dec 9, 2025
8dc0228
Bump hexpm/elixir in /copi.owasp.org
dependabot[bot] Dec 12, 2025
91ba5c9
Bump @types/node from 24.10.1 to 25.0.1 in /cornucopia.owasp.org
dependabot[bot] Dec 12, 2025
61daa2c
Bump actions/cache from 4.3.0 to 5.0.0
dependabot[bot] Dec 12, 2025
057be34
Bump swoosh from 1.19.8 to 1.19.9 in /copi.owasp.org
dependabot[bot] Dec 10, 2025
d96b0a8
Bump urllib3 from 2.5.0 to 2.6.1
dependabot[bot] Dec 12, 2025
81f6c72
Bump svelte from 5.45.6 to 5.45.10 in /cornucopia.owasp.org
dependabot[bot] Dec 12, 2025
93a7365
Bump step-security/harden-runner from 2.13.3 to 2.14.0
dependabot[bot] Dec 10, 2025
1b741f9
Bump ecto_sql from 3.13.2 to 3.13.3 in /copi.owasp.org
dependabot[bot] Dec 9, 2025
6d8617d
Bump phoenix from 1.8.2 to 1.8.3 in /copi.owasp.org
dependabot[bot] Dec 12, 2025
6a33309
Bump phoenix_live_reload from 1.6.1 to 1.6.2 in /copi.owasp.org
dependabot[bot] Dec 12, 2025
4d76020
Bump @sveltejs/kit from 2.49.1 to 2.49.2 in /cornucopia.owasp.org
dependabot[bot] Dec 12, 2025
8f14efb
Update copi.owasp.org/lib/copi_web/plugs/rate_limiter.ex
sydseter Dec 22, 2025
eefba03
Update copi.owasp.org/lib/copi_web/live/game_live/index.ex
sydseter Dec 22, 2025
2522145
Update copi.owasp.org/SECURITY.md
sydseter Dec 22, 2025
87afaf7
Update copi.owasp.org/lib/copi/rate_limiter.ex
sydseter Dec 24, 2025
800d112
Update copi.owasp.org/lib/copi_web/plugs/rate_limiter.ex
sydseter Dec 24, 2025
ab9b6fb
Update copi.owasp.org/lib/copi_web/live/game_live/create_game_form.ex
sydseter Dec 24, 2025
0198d83
Update copi.owasp.org/lib/copi_web/plugs/rate_limiter.ex
sydseter Dec 24, 2025
b0b9e4c
Update copi.owasp.org/test/copi/rate_limiter_test.exs
sydseter Dec 24, 2025
4918309
Fixed rate limiter issues: add atomic check_and_record, use IPHelper,…
immortal71 Jan 31, 2026
378f566
docs: move RATE_LIMITING_IMPLEMENTATION.md to copi.owasp.org/ as requ…
immortal71 Feb 7, 2026
021d68e
docs: remove RATE_LIMITING_IMPLEMENTATION.md from root (moved to copi…
immortal71 Feb 7, 2026
ff372b9
revert: restore Pipfile, Dockerfile, and package.json to match OWASP:…
immortal71 Feb 7, 2026
3d4d6e3
revert: removed unrelated changes from workflows and dependency files
immortal71 Feb 7, 2026
e1c9c66
test: add IPHelper LiveView integration tests and window expiration test
immortal71 Feb 7, 2026
939ed1c
fix: resolve template and component bugs exposed by new tests
immortal71 Feb 7, 2026
5708985
fix: rewrite IPHelper tests to not depend on /games page
immortal71 Feb 7, 2026
e19273d
fix: guard connected? before get_connect_ip and handle nil peer_data
immortal71 Feb 7, 2026
8d0b292
Merge remote-tracking branch 'upstream/master' into feat/rate-limitin…
immortal71 Feb 8, 2026
93b9ff5
fix: assign games: nil in connected mount to match original template …
immortal71 Feb 8, 2026
ee48918
Fix test database password configuration with fallback
immortal71 Feb 8, 2026
18123ac
Merge branch 'master' into feat/rate-limiting-clean
immortal71 Feb 8, 2026
521f620
Update configuration for testing and rate limiting in test.exs
immortal71 Feb 8, 2026
f9a7159
Fix test database configuration after branch update
immortal71 Feb 8, 2026
3838040
Update rate limiter test to add setup blocks and improve coverage
immortal71 Feb 8, 2026
7d18c63
Update rate_limiter_test.exs file
immortal71 Feb 8, 2026
b205dae
Update rate_limiter_test.exs with new tests for rate limiting
immortal71 Feb 8, 2026
77aa692
fix: restore rate_limiter_test.exs with correct content (removed plac…
immortal71 Feb 8, 2026
9d6b86b
fix: move rate_limiter_test.exs to correct location (copi.owasp.org/t…
immortal71 Feb 8, 2026
74cc2d0
fix: add RateLimiter.clear_ip calls and remove timeout tags to preven…
immortal71 Feb 8, 2026
3ab0919
fix: remove BOM from rate_limiter_test.exs
immortal71 Feb 8, 2026
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
108 changes: 108 additions & 0 deletions copi.owasp.org/RATE_LIMITING_IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Rate Limiting Implementation for Player Creation

## Overview
This implementation addresses GitHub Issue #1877 by adding IP-based rate limiting for player creation, separate from the existing game creation rate limit, to protect against CAPEC 212 (Functionality Misuse) attacks.

## Changes Made

### 1. Rate Limiter Module (`lib/copi/rate_limiter.ex`)

**Added player_creation action:**
- Updated `check_rate/2` to accept `:player_creation` action
- Updated `record_action/2` to accept `:player_creation` action
- Added player creation configuration with default limits:
- Maximum players per IP: 20
- Time window: 3600 seconds (1 hour)

**Configuration:**
```elixir
player_creation: %{
max_requests: get_env(:max_players_per_ip, 20),
window_seconds: get_env(:player_creation_window_seconds, 3600)
}
```

### 2. Player Form Component (`lib/copi_web/live/player_live/form_component.ex`)

**Added rate limiting to player creation:**
- Added `get_connect_ip/1` helper function to extract IP address from socket
- Modified `save_player/3` for `:new` action to:
1. Get the connecting IP address
2. Check rate limit before creating player
3. Only create player if rate limit is not exceeded
4. Record the action after successful creation
5. Display user-friendly error message if rate limited

**Error message shown to users:**
```
"Rate limit exceeded. Too many players created from your IP address.
Please try again in X seconds. This limit helps ensure service
availability for all users."
```

### 3. Tests (`test/copi/rate_limiter_test.exs`)

**Added comprehensive test coverage:**
- Test that player creation is allowed under the limit
- Test that player creation is blocked when limit is exceeded
- Test that player creation limit is separate from game creation limit
- Updated configuration test to verify player_creation config exists

## Key Features

### Separate Limits
The player creation rate limit is maintained **separately** from the game creation rate limit. This means:
- An IP that has exhausted its game creation quota can still create players
- An IP that has exhausted its player creation quota can still create games
- Each limit is tracked independently in the GenServer state

### Configurable Limits
The limits can be configured via application environment:
```elixir
config :copi, Copi.RateLimiter,
max_players_per_ip: 20,
player_creation_window_seconds: 3600
```

### User-Friendly Error Handling
When rate limited, users receive:
- Clear explanation of why they were blocked
- Time until they can try again (retry_after in seconds)
- Explanation that this protects service availability

## Security Benefits

1. **CAPEC 212 Mitigation**: Prevents functionality misuse by limiting the rate of player creation from a single IP
2. **DoS Protection**: Helps maintain service availability under attack
3. **Resource Conservation**: Prevents database and system resource exhaustion
4. **Granular Control**: Separate limits allow fine-tuned protection for different actions

## Default Limits Summary

| Action | Max Requests | Time Window |
|--------|--------------|-------------|
| Game Creation | 10 | 1 hour |
| Player Creation | 20 | 1 hour |
| Connection | 50 | 5 minutes |

## Testing

Run the test suite:
```bash
mix test test/copi/rate_limiter_test.exs
```

All tests should pass, including the new player creation rate limiting tests.

## Future Enhancements

As mentioned in the issue, if this is still insufficient, the next step would be:
- Implement authentication
- Associate rate limits with user accounts in addition to IP addresses
- Track browser fingerprints along with IP addresses

---

**Issue Reference:** OWASP/cornucopia#1877
**Related Security Control:** CAPEC-212 (Functionality Misuse)
**Implemented by:** @immortal71
203 changes: 203 additions & 0 deletions copi.owasp.org/SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# Security Implementation for Copi

This document describes the security measures implemented in Copi to protect against abuse and ensure service availability.

## Overview

Copi has implemented IP-based rate limiting to protect against **CAPEC 212 (Functionality Misuse)** attacks. These protections help ensure that the service remains available for all legitimate users by preventing a single source from overwhelming the system.

## Implemented Protections

### 1. Game Creation Rate Limiting

**Purpose**: Prevents a single IP address from creating an excessive number of games, which could lead to database exhaustion or denial of service.

**Default Configuration**:
- Maximum games per IP: 10
- Time window: 3600 seconds (1 hour)

**Behavior**:
- Tracks game creation attempts by IP address
- When the limit is exceeded, users receive a clear error message
- The limit resets after the time window expires
- Different IP addresses have independent limits

**Configuration**:
```bash
export MAX_GAMES_PER_IP=10
export GAME_CREATION_WINDOW_SECONDS=3600
```

### 2. WebSocket Connection Rate Limiting

**Purpose**: Prevents a single IP address from opening excessive WebSocket connections, which could exhaust server resources.

**Default Configuration**:
- Maximum connections per IP: 50
- Time window: 300 seconds (5 minutes)

**Behavior**:
- Tracks connection attempts by IP address
- When the limit is exceeded, connections are rejected with an error message
- The limit resets after the time window expires
- Does not affect existing active connections

**Configuration**:
```bash
export MAX_CONNECTIONS_PER_IP=50
export CONNECTION_WINDOW_SECONDS=300
```

## Technical Implementation

### Architecture

The rate limiting system consists of several components:

1. **Copi.RateLimiter** (`lib/copi/rate_limiter.ex`)
- GenServer that maintains rate limit state
- Tracks requests per IP address and action type
- Automatically cleans up expired entries every 5 minutes
- Provides a simple API for checking and recording actions

2. **CopiWeb.Plugs.RateLimiter** (`lib/copi_web/plugs/rate_limiter.ex`)
- Plug for HTTP request rate limiting
- Extracts IP addresses from connections
- Handles X-Forwarded-For headers for reverse proxies
- Returns proper HTTP 429 status codes when limits are exceeded

3. **LiveView Integration**
- Game creation rate limiting in `CopiWeb.GameLive.CreateGameForm`
- Connection rate limiting in `CopiWeb.GameLive.Index`
- Provides user-friendly error messages

### IP Address Handling

The system correctly handles:
- IPv4 addresses (e.g., 192.168.1.1)
- IPv6 addresses (e.g., 2001:db8::1)
- X-Forwarded-For headers (for reverse proxy deployments)
- Multiple IP addresses in X-Forwarded-For (uses the first one)

### Rate Limit Response Headers

When rate limiting is active, the following headers are included in responses:

- `X-RateLimit-Remaining`: Number of requests remaining in the current window
- `Retry-After`: Seconds until the rate limit resets (only included when rate limited)

### Error Messages

Users who exceed rate limits receive clear, informative error messages:

```
Rate limit exceeded. Too many games created from your IP address.
Please try again in 3600 seconds.
This limit helps ensure service availability for all users.
```

## Deployment Considerations

### Reverse Proxy Configuration

If deploying behind a reverse proxy (nginx, HAProxy, Cloudflare, etc.), ensure the real client IP is passed through:

**Nginx**:
```nginx
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
```

**Apache**:
```apache
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s"
```

**Cloudflare**:
Cloudflare automatically sets the CF-Connecting-IP header. The X-Forwarded-For header should also be present.

### Monitoring

Monitor the following metrics to detect abuse or adjust rate limits:

- Rate limit rejections (logged as warnings)
- Rate limit state size (number of tracked IPs)
- 429 HTTP responses
- Flash messages about rate limiting

### Adjusting Rate Limits

Rate limits can be adjusted based on your deployment needs:

**For Development/Testing**:
```bash
export MAX_GAMES_PER_IP=100
export MAX_CONNECTIONS_PER_IP=200
```

**For High-Traffic Production**:
```bash
export MAX_GAMES_PER_IP=5
export GAME_CREATION_WINDOW_SECONDS=7200 # 2 hours
export MAX_CONNECTIONS_PER_IP=30
export CONNECTION_WINDOW_SECONDS=600 # 10 minutes
```

**For Low-Traffic/Internal Use**:
```bash
export MAX_GAMES_PER_IP=50
export MAX_CONNECTIONS_PER_IP=100
```

## Testing

The implementation includes comprehensive tests:

- **Unit tests** for the RateLimiter GenServer (`test/copi/rate_limiter_test.exs`)
- **Integration tests** for the RateLimiter Plug (`test/copi_web/plugs/rate_limiter_test.exs`)

Run tests:
```bash
mix test
```

Run specific rate limiter tests:
```bash
mix test test/copi/rate_limiter_test.exs
mix test test/copi_web/plugs/rate_limiter_test.exs
```

## Future Enhancements

Potential future security enhancements (not currently implemented):

1. **Authentication Integration**
- Associate rate limits with authenticated users
- Different limits for authenticated vs. anonymous users
- Per-user rate limiting instead of just per-IP

2. **Geographic Rate Limiting**
- Different limits based on geographic location
- Blocking or stricter limits for high-risk regions

3. **Adaptive Rate Limiting**
- Automatically adjust limits based on system load
- Stricter limits during high traffic periods

4. **CAPTCHA Integration**
- Require CAPTCHA after repeated rate limit violations
- Optional CAPTCHA for game creation

5. **Rate Limit Dashboard**
- Admin interface to view current rate limit state
- Ability to manually block/unblock IPs
- Real-time monitoring of rate limit violations

## Reporting Security Issues

If you discover a security vulnerability in Copi, please report it to the OWASP Cornucopia team through the [GitHub Security Advisories](https://github.com/OWASP/cornucopia/security/advisories) page.

## References

- [CAPEC-212: Functionality Misuse](https://capec.mitre.org/data/definitions/212.html)
- [OWASP API Security Top 10 - API4:2023 Unrestricted Resource Consumption](https://owasp.org/API-Security/editions/2023/en/0xa4-unrestricted-resource-consumption/)
- [Phoenix Framework Security](https://hexdocs.pm/phoenix/security.html)
11 changes: 11 additions & 0 deletions copi.owasp.org/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ config :copi, CopiWeb.Endpoint,

config :copi, env: Mix.env()

# Configure rate limiting to prevent abuse (CAPEC 212 - Functionality Misuse)
config :copi, Copi.RateLimiter,
# Maximum number of games that can be created from a single IP in the time window
max_games_per_ip: System.get_env("MAX_GAMES_PER_IP", "10") |> String.to_integer(),
# Time window in seconds for game creation rate limiting (default: 1 hour)
game_creation_window_seconds: System.get_env("GAME_CREATION_WINDOW_SECONDS", "3600") |> String.to_integer(),
# Maximum number of WebSocket connections from a single IP in the time window
max_connections_per_ip: System.get_env("MAX_CONNECTIONS_PER_IP", "50") |> String.to_integer(),
# Time window in seconds for connection rate limiting (default: 5 minutes)
connection_window_seconds: System.get_env("CONNECTION_WINDOW_SECONDS", "300") |> String.to_integer()

# Configure tailwind (the version is required)
config :tailwind,
version: "3.4.0",
Expand Down
8 changes: 7 additions & 1 deletion copi.owasp.org/config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Config
# Run `mix help test` for more information.
config :copi, Copi.Repo,
username: "postgres",
password: System.get_env("POSTGRES_TEST_PWD"),
password: System.get_env("POSTGRES_TEST_PWD") || "postgres",
database: "copi_test#{System.get_env("MIX_TEST_PARTITION")}",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox
Expand All @@ -18,5 +18,11 @@ config :copi, CopiWeb.Endpoint,
http: [port: 4002],
server: false

# Configure rate limiter with very high limits for testing to prevent blocking test execution
config :copi, Copi.RateLimiter,
max_games_per_ip: 100_000,
max_players_per_ip: 100_000,
max_connections_per_ip: 100_000

# Print only warnings and errors during test
config :logger, level: :warning
2 changes: 2 additions & 0 deletions copi.owasp.org/lib/copi/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ defmodule Copi.Application do
{Phoenix.PubSub, name: Copi.PubSub},
# Start the DNS clustering
{DNSCluster, query: Application.get_env(:copi, :dns_cluster_query) || :ignore},
# Start the Rate Limiter for security
Copi.RateLimiter,
# Start the Endpoint (http/https)
CopiWeb.Endpoint
# Start a worker by calling: Copi.Worker.start_link(arg)
Expand Down
Loading
Loading