Skip to content

SQL Security

SQL injection is one of the most dangerous attack vectors against database systems. Redshift Spectra implements comprehensive SQL validation to prevent malicious queries from reaching your data warehouse.

Defense in Depth

SQL security in Redshift Spectra is implemented at multiple layers:

flowchart TB
    subgraph Layers["SQL Security Layers"]
        direction TB

        subgraph L1["Layer 1: Request Validation"]
            REQ[Request Model<br/>Basic pattern blocking]
        end

        subgraph L2["Layer 2: SQL Validator"]
            VAL[Comprehensive Analysis<br/>Pattern matching · Complexity limits]
        end

        subgraph L3["Layer 3: Parameterization"]
            PARAM[Parameter Binding<br/>Value separation]
        end

        subgraph L4["Layer 4: Database Permissions"]
            PERM[RBAC/RLS<br/>Query execution limits]
        end
    end

    SQL[User SQL] --> L1 --> L2 --> L3 --> L4 --> RS[(Redshift)]

Even if an attacker bypasses one layer, others continue to protect your data.

SQL Validator

The SQL Validator is the primary defense against SQL injection and query abuse. It analyzes queries before execution using multiple techniques.

Security Levels

Three security levels are available, each with different trade-offs:

flowchart LR
    subgraph Levels["Security Levels"]
        direction TB

        STRICT["STRICT<br/>━━━━━━━━<br/>• SELECT only<br/>• No subqueries<br/>• No CTEs<br/>• Minimal functions"]

        STANDARD["STANDARD<br/>━━━━━━━━<br/>• SELECT only<br/>• Subqueries allowed<br/>• CTEs allowed<br/>• Safe functions"]

        PERMISSIVE["PERMISSIVE<br/>━━━━━━━━<br/>• SELECT only<br/>• All subqueries<br/>• All CTEs<br/>• More functions"]
    end

    STRICT -.->|"More restrictive"| STANDARD -.->|"Less restrictive"| PERMISSIVE
Level Use Case Query Complexity
STRICT Untrusted external users Simple queries only
STANDARD Internal applications Most analytical queries
PERMISSIVE Trusted data engineers Complex analytical queries

Blocked Patterns

The validator blocks dangerous SQL patterns regardless of security level:

flowchart TB
    subgraph Blocked["❌ Always Blocked"]
        direction TB

        DDL["DDL Statements<br/>DROP, CREATE, ALTER, TRUNCATE"]
        DML["DML Statements<br/>INSERT, UPDATE, DELETE"]
        ADMIN["Admin Operations<br/>GRANT, REVOKE, COPY"]
        SYSTEM["System Access<br/>pg_catalog, stl_*, stv_*"]
        DANGEROUS["Dangerous Functions<br/>pg_read_file, pg_terminate"]
    end

    SQL[User SQL] --> CHECK{Contains Blocked Pattern?}
    CHECK -->|Yes| REJECT[Reject with Error]
    CHECK -->|No| CONTINUE[Continue Validation]

Pattern Detection

The validator uses multiple detection techniques:

1. Statement Type Detection

Only SELECT statements (and WITH...SELECT for CTEs) are allowed:

flowchart LR
    SQL[Query] --> PARSE[Parse First Token]
    PARSE --> CHECK{SELECT or WITH?}
    CHECK -->|No| DENY[Deny]
    CHECK -->|Yes| ALLOW[Allow]

2. Dangerous Pattern Matching

Regular expressions detect dangerous patterns:

Category Patterns Blocked
DDL DROP TABLE, CREATE TABLE, ALTER TABLE
DML INSERT INTO, UPDATE SET, DELETE FROM
Stacked queries ; DROP, ; DELETE, ; INSERT
Comment injection /*...*/DROP, --...DELETE
System tables pg_catalog., information_schema.
Hex encoding 0x44524f50 (encoded DROP)

3. Complexity Analysis

Queries are analyzed for complexity to prevent resource abuse:

flowchart TB
    SQL[Query] --> ANALYZE[Analyze Complexity]

    ANALYZE --> JOINS{JOIN count > limit?}
    JOINS -->|Yes| DENY1[Deny: Too many JOINs]
    JOINS -->|No| SUBQ{Subquery depth > limit?}

    SUBQ -->|Yes| DENY2[Deny: Too many subqueries]
    SUBQ -->|No| LENGTH{Query length > limit?}

    LENGTH -->|Yes| DENY3[Deny: Query too long]
    LENGTH -->|No| ALLOW[Allow]

Complexity Limits

Limit Default Purpose
Max JOINs 10 Prevent expensive cross-joins
Max Subqueries 5 Limit query complexity
Max Query Length 100KB Prevent buffer overflow
Max UNION clauses 0 (blocked) Prevent result set manipulation

LIMIT Enforcement

The Query API automatically enforces result limits to prevent memory exhaustion:

flowchart TB
    subgraph LimitLogic["LIMIT Injection Logic"]
        direction TB

        Q1["Query without LIMIT"] --> ADD["Add LIMIT (threshold+1)"]
        Q2["Query with LIMIT > threshold"] --> REPLACE["Replace with LIMIT (threshold+1)"]
        Q3["Query with LIMIT <= threshold"] --> KEEP["Keep original LIMIT"]
    end

    subgraph Detection["Truncation Detection"]
        direction TB

        EXEC["Execute Query"] --> CHECK{Rows > threshold?}
        CHECK -->|Yes| TRUNCATE["Truncate to threshold<br/>Set truncated=true"]
        CHECK -->|No| RETURN["Return all rows"]
    end

    LimitLogic --> Detection

The LIMIT+1 strategy allows detection of truncation without executing the query twice.

Parameter Binding

Parameterized queries separate SQL structure from data values:

flowchart TB
    subgraph Vulnerable["❌ String Concatenation"]
        %% 使用 #quot; 代替内部的双引号
        BAD["SELECT * FROM users WHERE id = '#quot; + user_input + #quot;'"]
        ATTACK["user_input = ' OR 1=1 --"]
        RESULT1["SELECT * FROM users WHERE id = '' OR 1=1 --'"]
    end

    subgraph Safe["✅ Parameter Binding"]
        GOOD["SELECT * FROM users WHERE id = :user_id"]
        PARAM["user_id = ' OR 1=1 --"]
        RESULT2["Parameter treated as literal string<br/>No injection possible"]
    end

Use parameters for any user-provided values:

Parameter Type Supported
String
Integer
Float
Boolean
Null
Date/Time ✓ (as string)

Attack Prevention Examples

SQL Injection Attempts

flowchart TB
    subgraph Attacks["Attack Attempts"]
        A1["SELECT * FROM users; DROP TABLE users"]
        A2["SELECT * FROM users WHERE 1=1 --"]
        A3["SELECT * FROM users UNION SELECT * FROM secrets"]
        A4["SELECT * FROM pg_catalog.pg_user"]
    end

    subgraph Detection["Detection & Response"]
        D1["Stacked query detected"] --> DENY1[403 Forbidden]
        D2["Comment injection detected"] --> DENY2[403 Forbidden]
        D3["UNION not allowed"] --> DENY3[403 Forbidden]
        D4["System table access blocked"] --> DENY4[403 Forbidden]
    end

    A1 --> D1
    A2 --> D2
    A3 --> D3
    A4 --> D4

Real-World Attack Patterns

Attack Detection Method Response
'; DROP TABLE -- Stacked query pattern Blocked
UNION SELECT password FROM users UNION pattern Blocked
' OR '1'='1 Parameterization prevents No effect
0x44524f50205441424c45 Hex encoding detection Blocked
SELECT * FROM stl_query System table pattern Blocked

Validation Response

When validation fails, detailed error information is returned:

flowchart TB
    FAIL[Validation Failed] --> ERROR[Error Response]

    ERROR --> CODE["error_code:<br/>FORBIDDEN_STATEMENT"]
    ERROR --> MSG["message:<br/>SQL contains forbidden pattern: DROP"]
    ERROR --> DETAILS["details:<br/>pattern_matched, position"]

Error codes:

Code Description
FORBIDDEN_STATEMENT Blocked statement type detected
FORBIDDEN_PATTERN Dangerous pattern detected
COMPLEXITY_EXCEEDED Query too complex
EMPTY_QUERY No SQL provided
SYSTEM_TABLE_ACCESS Attempt to access system tables

Best Practices

Always Use Parameters

Never concatenate user input into SQL strings:

  • Use named parameters (:param_name)
  • Let the validator handle escaping
  • Audit parameter usage in logs

Start with STRICT Mode

For external-facing APIs:

  • Begin with STRICT security level
  • Relax only if specific queries require it
  • Document why relaxation is needed

Monitor Validation Failures

High validation failure rates may indicate:

  • Attack attempts in progress
  • Misconfigured client applications
  • Need for user education

Validation is Not a Replacement

SQL validation complements, but doesn't replace:

  • Row-Level Security (data isolation)
  • RBAC (permission control)
  • Parameter binding (injection prevention)

Configuration

Setting Default Description
SPECTRA_SQL_SECURITY_LEVEL standard Security level: strict, standard, permissive
SPECTRA_SQL_MAX_QUERY_LENGTH 100000 Maximum query length in characters
SPECTRA_SQL_MAX_JOINS 10 Maximum JOIN clauses
SPECTRA_SQL_MAX_SUBQUERIES 5 Maximum subquery depth
SPECTRA_SQL_ALLOW_CTE true Allow WITH clauses (CTEs)
SPECTRA_SQL_ALLOW_UNION false Allow UNION (disabled by default)

Audit Trail

All SQL validation events are logged:

  • Passed validations — With normalized SQL and warnings
  • Failed validations — With error code and matched pattern
  • Complexity metrics — JOIN count, subquery depth, query length