|
| 1 | +# Request Body Limiting |
| 2 | + |
| 3 | +The `max_tx_bytes` feature allows you to limit the total size of HTTP requests sent to upstream servers. |
| 4 | + |
| 5 | +This is primarily designed for mitigating code exfiltration attacks through covert channels. |
| 6 | + |
| 7 | +## Size Calculation |
| 8 | + |
| 9 | +The `max_tx_bytes` limit applies to **complete** HTTP requests, including: |
| 10 | + |
| 11 | +1. **Request line**: `METHOD /path HTTP/1.1\r\n` |
| 12 | +2. **Headers**: Each header as `Name: Value\r\n` |
| 13 | +3. **Header separator**: Final `\r\n` between headers and body |
| 14 | +4. **Body**: Request body bytes |
| 15 | + |
| 16 | +## Response Format |
| 17 | + |
| 18 | +To enable request body limiting, return an object with `max_tx_bytes` in your rule response: |
| 19 | + |
| 20 | +```javascript |
| 21 | +// JavaScript engine |
| 22 | +{allow: {max_tx_bytes: 1024}} // Limit to 1KB total request size |
| 23 | +``` |
| 24 | + |
| 25 | +```json |
| 26 | +// Line processor engine |
| 27 | +{"allow": {"max_tx_bytes": 1024}} |
| 28 | +``` |
| 29 | + |
| 30 | +> **Note**: The `max_tx_bytes` feature is only available in the JavaScript (`--js`) and Line Processor (`--proc`) engines, not in Shell scripts. |
| 31 | +
|
| 32 | +## Behavior |
| 33 | + |
| 34 | +The limiting behavior depends on whether the request includes a `Content-Length` header: |
| 35 | + |
| 36 | +### With Content-Length Header |
| 37 | + |
| 38 | +When the request includes a `Content-Length` header (most standard HTTP clients): |
| 39 | + |
| 40 | +1. **Early Detection**: httpjail calculates the total request size |
| 41 | +2. **Immediate Rejection**: If it exceeds `max_tx_bytes`, the client receives a `413 Payload Too Large` error immediately |
| 42 | +3. **No Upstream Contact**: The upstream server is never contacted, preventing unnecessary load |
| 43 | +4. **Clear Feedback**: The error message indicates the actual size and limit |
| 44 | + |
| 45 | +**Example error response:** |
| 46 | +``` |
| 47 | +HTTP/1.1 413 Payload Too Large |
| 48 | +Content-Type: text/plain |
| 49 | +
|
| 50 | +Request body size (5000 bytes) exceeds maximum allowed (1024 bytes) |
| 51 | +``` |
| 52 | + |
| 53 | +### Without Content-Length Header |
| 54 | + |
| 55 | +When the request uses chunked encoding or doesn't include `Content-Length`: |
| 56 | + |
| 57 | +1. **Stream Truncation**: The request body is truncated at the limit during streaming |
| 58 | +2. **Upstream Receives Partial**: The upstream server receives exactly `max_tx_bytes` total bytes (url + headers + truncated body) |
| 59 | +3. **Connection Closes**: The connection terminates after reaching the limit |
| 60 | + |
| 61 | +## Examples |
| 62 | + |
| 63 | +### JavaScript Engine - Upload Endpoint Limiting |
| 64 | + |
| 65 | +```javascript |
| 66 | +// Limit upload endpoints to 1KB total request size |
| 67 | +const uploadHosts = ['uploads.example.com', 'upload.github.com']; |
| 68 | + |
| 69 | +uploadHosts.includes(r.host) |
| 70 | + ? {allow: {max_tx_bytes: 1024}} |
| 71 | + : r.host.endsWith('.example.com') |
| 72 | +``` |
| 73 | + |
| 74 | +### Line Processor Engine - Python Example |
| 75 | + |
| 76 | +```python |
| 77 | +#!/usr/bin/env python3 |
| 78 | +import sys, json |
| 79 | + |
| 80 | +upload_hosts = {'uploads.example.com', 'data.api.com'} |
| 81 | + |
| 82 | +for line in sys.stdin: |
| 83 | + try: |
| 84 | + req = json.loads(line) |
| 85 | + if req['host'] in upload_hosts: |
| 86 | + # Limit upload endpoints to 1KB requests |
| 87 | + # Returns 413 error if Content-Length exceeds limit |
| 88 | + # Truncates body if no Content-Length header |
| 89 | + response = {"allow": {"max_tx_bytes": 1024}} |
| 90 | + print(json.dumps(response)) |
| 91 | + elif req['host'].endswith('.example.com'): |
| 92 | + print("true") |
| 93 | + else: |
| 94 | + print("false") |
| 95 | + except: |
| 96 | + print("false") |
| 97 | + sys.stdout.flush() |
| 98 | +``` |
| 99 | + |
| 100 | + |
| 101 | +## Use Cases |
| 102 | + |
| 103 | +### 1. Limiting File Uploads |
| 104 | + |
| 105 | +Prevent users from uploading large files to specific endpoints: |
| 106 | + |
| 107 | +```javascript |
| 108 | +// JavaScript engine |
| 109 | +const uploadPaths = ['/upload', '/api/files']; |
| 110 | +uploadPaths.some(path => r.path.startsWith(path)) |
| 111 | + ? {allow: {max_tx_bytes: 10485760}} // 10MB limit |
| 112 | + : true |
| 113 | +``` |
| 114 | + |
| 115 | +### 2. API Cost Control |
| 116 | + |
| 117 | +Limit request sizes to metered APIs to prevent unexpected costs: |
| 118 | + |
| 119 | +```javascript |
| 120 | +// JavaScript engine |
| 121 | +r.host === 'api.expensive-service.com' |
| 122 | + ? {allow: {max_tx_bytes: 1024}} // 1KB limit for expensive API |
| 123 | + : true |
| 124 | +``` |
| 125 | + |
| 126 | +### 3. Data Exfiltration Prevention |
| 127 | + |
| 128 | +Prevent large data uploads that might indicate data exfiltration: |
| 129 | + |
| 130 | +```javascript |
| 131 | +// JavaScript engine |
| 132 | +const externalHosts = ['pastebin.com', 'transfer.sh', 'file.io']; |
| 133 | +externalHosts.some(host => r.host.includes(host)) |
| 134 | + ? {allow: {max_tx_bytes: 4096}} // 4KB limit for paste sites |
| 135 | + : true |
| 136 | +``` |
| 137 | + |
| 138 | +## Limitations |
| 139 | + |
| 140 | +- **Shell scripts**: The `max_tx_bytes` feature is not available when using shell script rules (`--shell`) |
| 141 | +- **HTTP wire format**: The byte count is based on HTTP wire format, not just the body size |
| 142 | +- **Partial uploads**: When truncating (no Content-Length), the upstream server receives incomplete data which may cause application errors |
| 143 | + |
| 144 | +## See Also |
| 145 | + |
| 146 | +- [JavaScript Engine](../guide/rule-engines/javascript.md) |
| 147 | +- [Line Processor Engine](../guide/rule-engines/line-processor.md) |
| 148 | +- [Configuration](../guide/configuration.md) |
0 commit comments