Skip to content

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 user
max_concurrent_queries = 100 # Per user
query_timeout_secs = 300 # 5 minutes
max_result_rows = 1000000 # 1M rows max per query
max_recursive_cte_depth = 1000 # Recursive CTE iteration limit
max_subquery_depth = 64 # Subquery nesting limit
max_expression_depth = 128 # Expression nesting limit

Or 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; -- OK

Step 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 successfully

Step 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 = 300000

When 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 300000ms

Step 7 — CRC32 Data Page Checksums

Enable CRC32 checksums to detect data corruption from disk errors or hardware faults.

[storage]
enable_checksums = true

When 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
--------
ok

When corruption is detected, the result lists the affected keys:

result
------------------------------------------
checksum mismatch: data:orders:row_42
checksum mismatch: data:users:row_108

Use 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:

ValidationLimitPurpose
SQL statement size1 MBPrevents oversized query payloads
Parameter count64KLimits bind parameter arrays
Query timeout1-600,000 msBounds 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 query
PREPARE 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 user
max_concurrent_queries = 100

When 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 = true
wal_sync_mode = "sync"
enable_checksums = true
query_timeout_ms = 60000
compression = "Zstd"
[resource_quotas]
memory_limit_per_user_mb = 512
max_concurrent_queries = 50
query_timeout_secs = 60
max_result_rows = 100000
max_recursive_cte_depth = 500
max_subquery_depth = 32
max_expression_depth = 64
[encryption]
enabled = true
key_source = { Environment = "HELIOSDB_ENCRYPTION_KEY" }
rotation_interval_days = 90

Tips 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_check after 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_ms is 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_config system view to verify active configuration at runtime:

SELECT * FROM helios_config WHERE section = 'resource_quotas';