Skip to content

Commit abdc48b

Browse files
author
Daniel Madsen
committed
Add SQLInjectionValidator script for user input validation against SQL injection
1 parent 95ca253 commit abdc48b

File tree

2 files changed

+326
-0
lines changed

2 files changed

+326
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
SQLInjectionValidator
2+
3+
Script Include for detecting potential SQL injection attempts in user-provided strings.
4+
5+
Purpose:
6+
Validates user input against a comprehensive list of SQL injection patterns including
7+
keywords, operators, comment syntax, and common attack vectors.
8+
9+
Usage:
10+
var validator = new SQLInjectionValidator();
11+
var isSafe = validator.isSafeFromSQLInjection(userInput);
12+
13+
Performance Considerations:
14+
- Uses efficient string methods (toLowerCase, includes) for keyword detection
15+
- Regex patterns are pre-compiled for performance
16+
- Early exit on first match to minimize processing
17+
- Suitable for high-volume input validation
18+
19+
Security Note:
20+
This function provides pattern-based detection and should be used as ONE LAYER
21+
of defense. Always use parameterized queries and prepared statements in your
22+
database interactions as the PRIMARY defense against SQL injection.
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
/**
2+
* SQLInjectionValidator
3+
*
4+
* Script Include for detecting potential SQL injection attempts in user-provided strings.
5+
*
6+
* Purpose:
7+
* Validates user input against a comprehensive list of SQL injection patterns including
8+
* keywords, operators, comment syntax, and common attack vectors.
9+
*
10+
* Usage:
11+
* var validator = new SQLInjectionValidator();
12+
* var isSafe = validator.isSafeFromSQLInjection(userInput);
13+
*
14+
* Performance Considerations:
15+
* - Uses efficient string methods (toLowerCase, includes) for keyword detection
16+
* - Regex patterns are pre-compiled for performance
17+
* - Early exit on first match to minimize processing
18+
* - Suitable for high-volume input validation
19+
*
20+
* Security Note:
21+
* This function provides pattern-based detection and should be used as ONE LAYER
22+
* of defense. Always use parameterized queries and prepared statements in your
23+
* database interactions as the PRIMARY defense against SQL injection.
24+
*
25+
* @class SQLInjectionValidator
26+
*/
27+
var SQLInjectionValidator = Class.create();
28+
29+
SQLInjectionValidator.prototype = {
30+
31+
/**
32+
* Checks if a string appears safe from SQL injection attempts
33+
*
34+
* @param {string} inputString - The user-provided string to validate
35+
* @returns {boolean} true if string appears safe, false if suspicious patterns detected
36+
*/
37+
isSafeFromSQLInjection: function(inputString) {
38+
// Input validation: ensure we have a string
39+
if (typeof inputString !== 'string') {
40+
gs.debug('SQLInjectionValidator: Input is not a string, converting to string');
41+
inputString = String(inputString);
42+
}
43+
44+
// Empty strings are considered safe
45+
if (inputString.length === 0) {
46+
return true;
47+
}
48+
49+
// Convert to lowercase for case-insensitive comparison
50+
var lowerInput = inputString.toLowerCase();
51+
52+
// ========== CHECK 1: SQL Keywords ==========
53+
// Detects common SQL commands that could indicate injection attempts
54+
var sqlKeywords = [
55+
'select', 'insert', 'update', 'delete', 'drop', 'create', 'alter',
56+
'truncate', 'exec', 'execute', 'union', 'declare', 'cast', 'convert'
57+
];
58+
59+
for (var i = 0; i < sqlKeywords.length; i++) {
60+
// Use word boundary regex to avoid false positives (e.g., "selected" vs "select")
61+
var keywordRegex = new RegExp('\\b' + sqlKeywords[i] + '\\b', 'i');
62+
if (keywordRegex.test(inputString)) {
63+
gs.debug('SQLInjectionValidator: Detected SQL keyword: ' + sqlKeywords[i]);
64+
return false;
65+
}
66+
}
67+
68+
// ========== CHECK 2: SQL Clauses ==========
69+
// Detects FROM, WHERE, ORDER BY, GROUP BY, HAVING clauses
70+
var sqlClauses = [
71+
'from', 'where', 'order by', 'group by', 'having', 'join', 'inner join',
72+
'left join', 'right join', 'cross join', 'on'
73+
];
74+
75+
for (var j = 0; j < sqlClauses.length; j++) {
76+
var clauseRegex = new RegExp('\\b' + sqlClauses[j] + '\\b', 'i');
77+
if (clauseRegex.test(inputString)) {
78+
gs.debug('SQLInjectionValidator: Detected SQL clause: ' + sqlClauses[j]);
79+
return false;
80+
}
81+
}
82+
83+
// ========== CHECK 3: Comment Patterns ==========
84+
// Detects SQL comment syntax: --, /* */, ;
85+
var commentPatterns = [
86+
/--\s/, // SQL line comment: -- followed by space
87+
/\/\*/, // SQL block comment start: /*
88+
/\*\//, // SQL block comment end: */
89+
/;\s*$/, // Semicolon at end (statement terminator)
90+
/;\s*select/i, // Semicolon followed by select
91+
/;\s*insert/i, // Semicolon followed by insert
92+
/;\s*update/i, // Semicolon followed by update
93+
/;\s*delete/i // Semicolon followed by delete
94+
];
95+
96+
for (var k = 0; k < commentPatterns.length; k++) {
97+
if (commentPatterns[k].test(inputString)) {
98+
gs.debug('SQLInjectionValidator: Detected comment pattern');
99+
return false;
100+
}
101+
}
102+
103+
// ========== CHECK 4: Boolean-based Injection Patterns ==========
104+
// Detects common boolean-based SQL injection: OR 1=1, AND 1=1, etc.
105+
var booleanPatterns = [
106+
/or\s+1\s*=\s*1/i, // OR 1=1
107+
/and\s+1\s*=\s*1/i, // AND 1=1
108+
/or\s+'1'\s*=\s*'1'/i, // OR '1'='1'
109+
/and\s+'1'\s*=\s*'1'/i, // AND '1'='1'
110+
/or\s+true/i, // OR TRUE
111+
/and\s+true/i, // AND TRUE
112+
/or\s+1/i, // OR 1 (generic)
113+
/and\s+1/i, // AND 1 (generic)
114+
/or\s+''/i, // OR ''
115+
/and\s+''/i // AND ''
116+
];
117+
118+
for (var l = 0; l < booleanPatterns.length; l++) {
119+
if (booleanPatterns[l].test(inputString)) {
120+
gs.debug('SQLInjectionValidator: Detected boolean-based injection pattern');
121+
return false;
122+
}
123+
}
124+
125+
// ========== CHECK 5: Comparison Operators with Suspicious Values ==========
126+
// Detects patterns like: = 1, != 0, <> 0, > 0, < 1
127+
var comparisonPatterns = [
128+
/=\s*1\s*$/i, // Ends with = 1
129+
/!=\s*0/i, // != 0
130+
/<>\s*0/i, // <> 0
131+
/>\s*0/i, // > 0
132+
/<\s*1/i // < 1
133+
];
134+
135+
for (var m = 0; m < comparisonPatterns.length; m++) {
136+
if (comparisonPatterns[m].test(inputString)) {
137+
gs.debug('SQLInjectionValidator: Detected suspicious comparison pattern');
138+
return false;
139+
}
140+
}
141+
142+
// ========== CHECK 6: SQL Functions ==========
143+
// Detects SQL functions commonly used in injection: CHAR, ASCII, SUBSTRING, WAITFOR, SLEEP, BENCHMARK
144+
var sqlFunctions = [
145+
'char(', 'ascii(', 'substring(', 'waitfor(', 'sleep(', 'benchmark(',
146+
'concat(', 'length(', 'mid(', 'instr(', 'load_file(', 'into outfile'
147+
];
148+
149+
for (var n = 0; n < sqlFunctions.length; n++) {
150+
if (lowerInput.includes(sqlFunctions[n])) {
151+
gs.debug('SQLInjectionValidator: Detected SQL function: ' + sqlFunctions[n]);
152+
return false;
153+
}
154+
}
155+
156+
// ========== CHECK 7: System Variables and Commands ==========
157+
// Detects: @@VERSION, xp_cmdshell, sp_executesql, etc.
158+
var systemPatterns = [
159+
/@@version/i, // SQL Server version
160+
/@@servername/i, // SQL Server name
161+
/xp_cmdshell/i, // SQL Server command shell
162+
/sp_executesql/i, // SQL Server execute SQL
163+
/information_schema/i, // Database schema enumeration
164+
/mysql\.user/i, // MySQL user table
165+
/pg_catalog/i // PostgreSQL catalog
166+
];
167+
168+
for (var o = 0; o < systemPatterns.length; o++) {
169+
if (systemPatterns[o].test(inputString)) {
170+
gs.debug('SQLInjectionValidator: Detected system variable or command');
171+
return false;
172+
}
173+
}
174+
175+
// ========== CHECK 8: Quote Escaping and Concatenation ==========
176+
// Detects: '', "", \', \", ||, +, CONCAT
177+
var escapePatterns = [
178+
/''/, // Double single quote (escape)
179+
/""/, // Double double quote (escape)
180+
/\\'/ , // Backslash single quote
181+
/\\"/, // Backslash double quote
182+
/\|\|/, // Oracle/PostgreSQL concatenation
183+
/\+\\s*'/i // SQL Server concatenation
184+
];
185+
186+
for (var p = 0; p < escapePatterns.length; p++) {
187+
if (escapePatterns[p].test(inputString)) {
188+
gs.debug('SQLInjectionValidator: Detected quote escaping or concatenation pattern');
189+
return false;
190+
}
191+
}
192+
193+
// ========== CHECK 9: UNION-based Injection ==========
194+
// Detects UNION SELECT patterns
195+
var unionPatterns = [
196+
/union\s+select/i, // UNION SELECT
197+
/union\s+all\s+select/i, // UNION ALL SELECT
198+
/union\s+distinct\s+select/i // UNION DISTINCT SELECT
199+
];
200+
201+
for (var q = 0; q < unionPatterns.length; q++) {
202+
if (unionPatterns[q].test(inputString)) {
203+
gs.debug('SQLInjectionValidator: Detected UNION-based injection pattern');
204+
return false;
205+
}
206+
}
207+
208+
// ========== CHECK 10: Time-based Blind Injection ==========
209+
// Detects: SLEEP, BENCHMARK, WAITFOR DELAY, pg_sleep
210+
var timeBasedPatterns = [
211+
/sleep\s*\(/i, // SLEEP function
212+
/benchmark\s*\(/i, // BENCHMARK function
213+
/waitfor\s+delay/i, // WAITFOR DELAY
214+
/pg_sleep\s*\(/i // PostgreSQL sleep
215+
];
216+
217+
for (var r = 0; r < timeBasedPatterns.length; r++) {
218+
if (timeBasedPatterns[r].test(inputString)) {
219+
gs.debug('SQLInjectionValidator: Detected time-based blind injection pattern');
220+
return false;
221+
}
222+
}
223+
224+
// ========== CHECK 11: Stacked Queries ==========
225+
// Detects multiple statements separated by semicolons
226+
var stackedQueryPattern = /;\s*[a-z]/i;
227+
if (stackedQueryPattern.test(inputString)) {
228+
gs.debug('SQLInjectionValidator: Detected stacked query pattern');
229+
return false;
230+
}
231+
232+
// ========== CHECK 12: Hex Encoding ==========
233+
// Detects: 0x (hex encoding used to bypass filters)
234+
var hexPattern = /0x[0-9a-f]+/i;
235+
if (hexPattern.test(inputString)) {
236+
gs.debug('SQLInjectionValidator: Detected hex encoding pattern');
237+
return false;
238+
}
239+
240+
// ========== CHECK 13: Comment-based Injection ==========
241+
// Detects: #, --, /*, */ patterns
242+
var commentInjectionPatterns = [
243+
/#/, // MySQL comment
244+
/--/, // SQL comment
245+
/\/\*/, // Block comment start
246+
/\*\// // Block comment end
247+
];
248+
249+
for (var s = 0; s < commentInjectionPatterns.length; s++) {
250+
if (commentInjectionPatterns[s].test(inputString)) {
251+
gs.debug('SQLInjectionValidator: Detected comment-based injection pattern');
252+
return false;
253+
}
254+
}
255+
256+
// ========== CHECK 14: Wildcard Patterns ==========
257+
// Detects: %, _ (SQL wildcards that could be used in LIKE injection)
258+
// Note: This is a loose check - legitimate data may contain these
259+
// Only flag if combined with suspicious patterns
260+
var wildcardPattern = /[%_]\s*(or|and|union|select)/i;
261+
if (wildcardPattern.test(inputString)) {
262+
gs.debug('SQLInjectionValidator: Detected wildcard with SQL keyword pattern');
263+
return false;
264+
}
265+
266+
// ========== CHECK 15: Parentheses Nesting ==========
267+
// Detects excessive parentheses which could indicate subquery injection
268+
var openParens = (inputString.match(/\(/g) || []).length;
269+
var closeParens = (inputString.match(/\)/g) || []).length;
270+
271+
// Flag if more than 3 levels of nesting or mismatched parentheses
272+
if (openParens > 5 || closeParens > 5 || openParens !== closeParens) {
273+
gs.debug('SQLInjectionValidator: Detected excessive or mismatched parentheses');
274+
return false;
275+
}
276+
277+
// ========== CHECK 16: Case Sensitivity Bypass ==========
278+
// Detects: sElEcT, UnIoN, etc. (mixed case SQL keywords)
279+
var mixedCasePattern = /[a-z]{2,}\s+[a-z]{2,}/i;
280+
if (mixedCasePattern.test(inputString)) {
281+
// Check if it matches known SQL keywords in mixed case
282+
var mixedCaseKeywords = [
283+
/s[eE][lL][eE][cC][tT]/,
284+
/u[nN][iI][oO][nN]/,
285+
/[iI][nN][sS][eE][rR][tT]/,
286+
/[uU][pP][dD][aA][tT][eE]/,
287+
/[dD][eE][lL][eE][tT][eE]/
288+
];
289+
290+
for (var t = 0; t < mixedCaseKeywords.length; t++) {
291+
if (mixedCaseKeywords[t].test(inputString)) {
292+
gs.debug('SQLInjectionValidator: Detected mixed-case SQL keyword');
293+
return false;
294+
}
295+
}
296+
}
297+
298+
// ========== All checks passed ==========
299+
gs.debug('SQLInjectionValidator: Input passed all SQL injection checks');
300+
return true;
301+
},
302+
303+
type: 'SQLInjectionValidator'
304+
};

0 commit comments

Comments
 (0)