Fix regressions due to SQL Injection Vulnerability CVE-2016-20018 #6274
+154
−12
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR provides a comprehensive fix for the SQL injection vulnerability CVE-2016-20018 reported in issue #1227. The vulnerability allowed attackers to inject SQL by passing objects or arrays as values in WHERE clauses, which MySQL would interpret as column references rather than literal values.
The Vulnerability
Original Issue
When objects were passed as values in WHERE clauses, Knex would generate invalid SQL that MySQL would interpret dangerously:
Attack Vectors
[0]would cause MySQL to convert string columns to 0knex.raw()bindings weren't validatedImpact
Previous Attempts and Their Issues
PR #5417 (January 2023)
The initial fix added blanket rejection of ALL objects and arrays in WHERE clauses:
Problems:
This Solution
Design Principles
Implementation Details
1. Enhanced Validation Functions (
lib/util/is.js)Added sophisticated validation that distinguishes between safe and unsafe values:
Security Features:
2. Query Builder Validation (
lib/dialects/mysql/query/mysql-querycompiler.js)Validates at query compilation time for immediate feedback:
Allows:
where({ id: 1 })where({ id: [1, 2, 3] })where({ data: { name: 'John', age: 30 } })Blocks:
where({ data: { user: { name: 'admin' } } })where({ data: [1, { hack: true }] })where({ data: () => 'admin' })3. Raw Query Validation (
lib/raw.js)Raw queries have STRICTER validation - no objects allowed at all:
Why Stricter for Raw Queries?
WHERE INclauses4. Execution-Time Validation (
lib/dialects/mysql/index.js)Final safety net at query execution:
Benefits:
Testing
Security Test Suite
Comprehensive test coverage for all attack vectors:
CVE Proof of Concept Tests
All original attack vectors from the issue are blocked:
Backward Compatibility
What Still Works
whereIn('id', [1, 2, 3])where({ data: { key: 'value' } })where({ file: Buffer.from('data') })raw('WHERE id IN (?)', [[1,2,3]])What's Now Blocked
Performance Impact
Migration Guide
For Users Upgrading
Most code will work without changes. If you encounter errors:
For nested objects in WHERE clauses:
For objects in raw queries:
For legitimate array uses that break:
Why This Approach?
Better Than Previous Attempts
Security Considerations
Files Modified
1.
lib/util/is.js2.
lib/dialects/mysql/query/mysql-querycompiler.js3.
lib/dialects/mysql/index.js4.
lib/raw.jsRelated Issues and PRs
Checklist
Credits
Summary
This PR successfully fixes CVE-2016-20018 while maintaining backward compatibility. It uses a sophisticated validation approach that distinguishes between safe and unsafe values based on context, providing security without breaking legitimate use cases. The fix has been thoroughly tested against all known attack vectors and maintains compatibility with existing code patterns.
The key innovation is the context-aware validation:
This approach balances security with usability, ensuring Knex remains both safe and developer-friendly.