Penetration Test Results
Tested against live production node (Foundation node) on 2026-04-15. 6 test suites, all non-destructive.
Node started at height ~140,282. After all tests: height 140,811. Chain never stalled, never crashed, kept producing blocks throughout.
Test 1: RPC Flood
Threw 1,000 GET requests at /chain/info as fast as curl could fire them.
- 294 succeeded (HTTP 200)
- 706 rate-limited (HTTP 429)
- Rate limiting kicked in after ~60 requests/minute as expected
- Node stayed responsive the entire time
- Had to wait 60s for rate limit cooldown before the next test
Verdict: PASS.
Test 2: Malformed Requests
32 edge cases: invalid JSON, missing fields, wrong types, SQL injection, XSS, path traversal, null bytes, boundary values, malformed transactions.
- 31 passed — node returned proper HTTP error codes
- 1 "fail" — SQL injection in URL path (
'; DROP TABLE;--) got connection reset. Nginx rejecting the special characters before reaching the node - All RPC endpoints returned 401 (API key required) — correct production behavior
- XSS in address path: HTTP 400. Path traversal: HTTP 404. Huge block index: HTTP 404. All handled.
Verdict: PASS. No panics, no crashes.
Test 3: Double Spend
Two transactions with the same nonce from the same fake address — one to victim A, one to victim B. Attempted via both REST and JSON-RPC.
- Both REST submissions: HTTP 401 (auth required)
- Both RPC submissions: blocked by auth
The API key layer blocks unauthenticated tx submissions before they reach validation. Double-spend protection (nonce validation, txid dedup) sits behind auth.
Verdict: PASS.
Test 4: Transaction Spam
100 transactions rapid-fire, then 5 duplicate-nonce transactions.
- 100/100 rejected (401 auth + validation)
- 0 accepted, 0 server errors
- Dup nonce: first got 401, rest got 429 (rate limited)
Verdict: PASS. Zero errors.
Test 5: P2P Connection Flood
120 simultaneous TCP connections to port 30303, held for 5 seconds.
- 120/120 connections opened
- API checked 5 times during flood: all HTTP 200, latency 60-80ms
- Zero impact on block production or API responsiveness
Verdict: PASS.
Test 6: Oversized Payloads
Progressive sizes (1KB to 2MB) on RPC and TX endpoints, plus batch size tests.
- 1KB, 10KB: accepted (processed to auth check)
- 100KB-900KB: connection reset — nginx drops large payloads before they reach the node (stricter than node's 1 MiB limit)
- 1MB+: rejected
- Batch 10-200: all hit auth check (401). Can't verify batch cap without valid API key from outside
Verdict: PASS. Nginx acts as extra defense layer.
Summary
| Test | Result |
|---|---|
| RPC Flood (1000 req) | PASS |
| Malformed (32 cases) | PASS (31/32) |
| Double Spend | PASS |
| TX Spam (105 txs) | PASS |
| P2P Flood (120 conn) | PASS |
| Oversized (16 cases) | PASS |
Node: height 140,282 → 140,811 during testing. Never missed a beat.
Observations
- Rate limiting is aggressive — 60 req/min per IP. Need ~60s cooldown between burst tests.
- Nginx drops payloads >~50KB before they hit the node — stricter than the node's own 1 MiB limit. Attackers hit nginx first.
- API key auth blocks all write ops from unauthenticated sources. Validation logic never runs for unauthenticated requests.
- P2P and API are independent. Flooding port 30303 has zero effect on port 8545.
Running the Scripts
./pentest/run_all.sh # local node
./pentest/run_all.sh [NODE_IP]:8545 # specific node
./pentest/test4_malformed.sh localhost:8545 # individual test
Note: scripts need nc (netcat) and bc which aren't available on Windows Git Bash. Python can substitute for the TCP tests.