Security Hardening & Resource Limits
Security Hardening & Resource Limits
HeliosDB-Lite includes defense-in-depth features to prevent runaway queries, injection attacks, and data corruption. This tutorial covers resource quotas, depth limits, integrity checks, and API validation.
Prerequisites
- HeliosDB-Lite v3.5 or later
- Access to the configuration file (
heliosdb.toml) - Access to the SQL shell (REPL, PostgreSQL wire protocol, or REST API)
Step 1 — Resource Quota Configuration
All resource limits are configured under [resource_quotas] in heliosdb.toml:
[resource_quotas]memory_limit_per_user_mb = 1024 # 1 GB per usermax_concurrent_queries = 100 # Per userquery_timeout_secs = 300 # 5 minutesmax_result_rows = 1000000 # 1M rows max per querymax_recursive_cte_depth = 1000 # Recursive CTE iteration limitmax_subquery_depth = 64 # Subquery nesting limitmax_expression_depth = 128 # Expression nesting limitOr set them programmatically (Rust API):
let mut config = Config::in_memory();config.resource_quotas.max_result_rows = 10_000;config.resource_quotas.max_recursive_cte_depth = 500;let db = EmbeddedDatabase::with_config(config)?;Step 2 — Max Result Rows
The max_result_rows limit prevents queries from returning unbounded result
sets that exhaust memory.
-- With max_result_rows = 100:CREATE TABLE big_table (id INT, val TEXT);-- Insert 200 rows...
SELECT * FROM big_table;-- ERROR: Query result exceeds max_result_rows limit (100)Add a LIMIT clause to stay within bounds:
SELECT * FROM big_table LIMIT 50; -- OKStep 3 — Recursive CTE Depth Limit
Prevents infinite recursion in recursive CTEs. When the iteration count exceeds
max_recursive_cte_depth, HeliosDB aborts the query.
-- With max_recursive_cte_depth = 50:WITH RECURSIVE counter(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM counter WHERE n < 100)SELECT * FROM counter;-- ERROR: Recursive CTE exceeded maximum depth (50)Fix by adding a tighter termination condition:
WITH RECURSIVE counter(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM counter WHERE n < 40 -- Under the limit)SELECT * FROM counter;-- Returns 40 rows successfullyStep 4 — Subquery Nesting Depth
Deeply nested subqueries can cause stack overflows or excessive planning time.
The max_subquery_depth limit (default: 64) rejects queries that nest too
deeply.
-- This will be rejected if nesting exceeds 64 levels:SELECT * FROM ( SELECT * FROM ( SELECT * FROM ( -- ... 65+ levels deep ... SELECT * FROM t ) ));-- ERROR: Subquery nesting depth exceeds limit (64)In practice, queries rarely need more than a few levels of nesting. If you hit this limit, refactor with CTEs or temporary tables.
Step 5 — Expression Depth Limit
Deeply nested expressions (e.g., ((((1+1)+1)+1)...)) are limited to prevent
stack exhaustion during evaluation. The default limit is 128 levels.
-- Rejected if expression nesting exceeds 128 levels:SELECT ((((((1+1)+1)+1)+1)+1)+1)... -- 200 levels-- ERROR: Expression nesting depth exceeds limit (128)Step 6 — Query Timeout
Set a maximum execution time to prevent long-running queries from monopolizing resources:
[storage]query_timeout_ms = 300000 # 5 minutes (milliseconds)statement_timeout_ms = 300000When a query exceeds the timeout, HeliosDB cancels it and returns an error.
-- A query that takes too long:SELECT * FROM very_large_table WHERE expensive_function(col) > 0;-- ERROR: Query timed out after 300000msStep 7 — CRC32 Data Page Checksums
Enable CRC32 checksums to detect data corruption from disk errors or hardware faults.
[storage]enable_checksums = trueWhen enabled, a 4-byte CRC32 checksum is prepended to each stored value. On read, the checksum is verified and an error is raised if it does not match.
Step 8 — PRAGMA integrity_check
Run a full integrity check that scans all data keys and verifies CRC32 checksums:
PRAGMA integrity_check;When all data is consistent:
result-------- okWhen corruption is detected, the result lists the affected keys:
result------------------------------------------ checksum mismatch: data:orders:row_42 checksum mismatch: data:users:row_108Use this after restoring from backup, hardware events, or as a periodic health check.
Step 9 — API Input Validation
The REST API enforces input size limits to prevent abuse:
| Validation | Limit | Purpose |
|---|---|---|
| SQL statement size | 1 MB | Prevents oversized query payloads |
| Parameter count | 64K | Limits bind parameter arrays |
| Query timeout | 1-600,000 ms | Bounds execution time |
These are enforced at the API layer before the SQL engine processes the request:
POST /api/v1/query{ "sql": "...", // max 1 MB "params": [...], // max 65,536 entries "timeout_ms": 30000 // 1 to 600,000}Requests exceeding these limits receive a 400 Bad Request response.
Step 10 — SQL Injection Prevention
HeliosDB uses parameterized queries throughout its API to prevent SQL injection. Always use parameters instead of string concatenation:
-- Safe: parameterized queryPREPARE find_user AS SELECT * FROM users WHERE name = $1;EXECUTE find_user('Alice');
-- Unsafe: string concatenation (never do this in application code)-- SELECT * FROM users WHERE name = '" + userInput + "'The REST API and PostgreSQL wire protocol both support parameterized queries natively.
Step 11 — Memory Limits
Per-user memory limits prevent a single user from exhausting server memory:
[resource_quotas]memory_limit_per_user_mb = 1024 # 1 GB per usermax_concurrent_queries = 100When a user’s queries exceed their memory allocation, new queries are queued or rejected until memory is freed.
Putting It All Together
A production-hardened configuration:
[storage]wal_enabled = truewal_sync_mode = "sync"enable_checksums = truequery_timeout_ms = 60000compression = "Zstd"
[resource_quotas]memory_limit_per_user_mb = 512max_concurrent_queries = 50query_timeout_secs = 60max_result_rows = 100000max_recursive_cte_depth = 500max_subquery_depth = 32max_expression_depth = 64
[encryption]enabled = truekey_source = { Environment = "HELIOSDB_ENCRYPTION_KEY" }rotation_interval_days = 90Tips and Troubleshooting
-
Tuning limits: Start with conservative limits and relax them based on actual workload patterns. Monitor query errors to identify when legitimate queries hit limits.
-
Integrity check frequency: Run
PRAGMA integrity_checkafter system crashes, storage migrations, or as part of a weekly health check cron job. -
Checksum overhead: Enabling CRC32 checksums adds 4 bytes per stored value and a small CPU cost on every read. For most workloads, this overhead is negligible.
-
Timeout granularity: The
query_timeout_msis checked between operator iterations, so a query may slightly exceed the timeout before being cancelled. -
Defense in depth: Combine resource limits with TDE encryption, GRANT/REVOKE access control, and network-level security (TLS, firewall rules) for comprehensive hardening.
-
Monitoring: Use the
helios_configsystem view to verify active configuration at runtime:
SELECT * FROM helios_config WHERE section = 'resource_quotas';