parent
4f941ea83d
commit
9b60d01456
@ -0,0 +1,465 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @author Robert Strutts <Robert@TryingToScale.com> |
||||
* @copyright Copyright (c) 2022, Robert Strutts. |
||||
* @license MIT |
||||
*/ |
||||
|
||||
/** |
||||
* @todo This is just for PLAY for now!!!! |
||||
* Too many FALSE positives and too many FALSE Negatives!!! |
||||
*/ |
||||
|
||||
namespace CodeHydrater; |
||||
|
||||
enum SQL_SAFETY_FLAG { |
||||
case good; // All Okey |
||||
case filtered; // Found isseues but tried to filter them out |
||||
case dangerious; // May still be bad |
||||
} |
||||
|
||||
class safer_sql { |
||||
|
||||
/** |
||||
* List of SQL attacks: |
||||
* @link https://github.com/payloadbox/sql-injection-payload-list |
||||
* @link https://www.neuralegion.com/blog/sql-injection-payloads/ |
||||
* @link https://ismailtasdelen.medium.com/sql-injection-payload-list-b97656cfd66b |
||||
*/ |
||||
|
||||
// NoSQL attacks and now SQL attacks as JSON is used EveryWhere |
||||
private static function found_bad_json(string $string): bool { |
||||
if (function_exists("str_contains")) { |
||||
if (str_contains($string, '[]') === true) return true; // GET Array |
||||
} |
||||
if (preg_match('/.match/', $string) === 1) return true; |
||||
if (preg_match('/&&/', $string) === 1) return true; |
||||
if (preg_match('/\|\|/', $string) === 1) return true; |
||||
if (preg_match('/\$where/', $string) === 1) return true; |
||||
if (preg_match("/mapReduce/", $string) === 1) return true; |
||||
if (preg_match('/\$group/', $string) === 1) return true; |
||||
if (preg_match('/\$regex/', $string) === 1) return true; |
||||
if (preg_match('/\$exists/', $string) === 1) return true; |
||||
if (preg_match('/\$ne/', $string) === 1) return true; |
||||
if (preg_match('/\$gt/', $string) === 1) return true; |
||||
if (preg_match('/\$lt/', $string) === 1) return true; |
||||
if (preg_match('/\$eq/', $string) === 1) return true; |
||||
if (preg_match('/\$nin/', $string) === 1) return true; |
||||
if (preg_match('/\$where/', $string) === 1) return true; |
||||
if (preg_match('/\$or/', $string) === 1) return true; |
||||
if (preg_match('/\$and/', $string) === 1) return true; |
||||
if (preg_match('/\$in/', $string) === 1) return true; |
||||
if (preg_match('/\$accumulator/', $string) === 1) return true; |
||||
if (preg_match('/\$function/', $string) === 1) return true; |
||||
if (preg_match("/:/", $string) === 1 && |
||||
preg_match("/{/", $string) === 1) return true; |
||||
return false; |
||||
} |
||||
|
||||
// Stacked queries can be used to execute multiple queries in succession. |
||||
private static function found_sql_stacker(string $string): bool { |
||||
if (preg_match("/;/", $string) === 1) return true; |
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_compair(string $string): bool { |
||||
if (preg_match("/=/", $string) === 1) return true; |
||||
return false; |
||||
} |
||||
|
||||
private static function found_quotes_in(string $string): bool { |
||||
if (preg_match("/'/", $string) === 1) return true; |
||||
if (preg_match("/\"/", $string) === 1) return true; |
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_comment(string $string): bool { |
||||
if (preg_match("/\#/", $string) === 1) return true; // Hash |
||||
if (preg_match("/\/\*/", $string) === 1) return true; // C-style comment |
||||
if (preg_match("/--/", $string) === 1) return true; // SQL comment |
||||
if (preg_match("/\`/", $string) === 1) return true; // Backtick |
||||
//if (preg_match("/\x00/", $string) === 1) return true; // NULL Byte, !errors out! |
||||
return false; |
||||
} |
||||
|
||||
private static function found_string_cat(string $string): bool { |
||||
if (preg_match("/\+/i", $string) === 1) return true; // addition, concatenate (or space in url) |
||||
if (preg_match("/\|\|/i", $string) === 1) return true; // (double pipe) concatenate |
||||
if (preg_match("/\%/", $string) === 1) return true; // wildcard attribute indicator |
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_time_based_attack(string $string): bool { |
||||
if (preg_match("/union/i", $string) === 1) { |
||||
if (preg_match("/all/i", $string) === 1) { |
||||
return true; |
||||
} |
||||
if (preg_match("/select/i", $string) === 1) { |
||||
return true; |
||||
} |
||||
if (preg_match("/null/i", $string) === 1) { |
||||
return true; |
||||
} |
||||
} |
||||
if (preg_match("/sleep\s*\(/i", $string) === 1) return true; |
||||
if (preg_match("/waitfor delay/i", $string) === 1) return true; |
||||
if (preg_match("/benchmark\s*\(/i", $string) === 1) return true; |
||||
if (preg_match("/pg_sleep\s*\(/i", $string) === 1) return true; |
||||
if (preg_match("/randomblob/i", $string) === 1) return true; |
||||
|
||||
if (preg_match("/convert/i", $string) === 1) { |
||||
if (preg_match("/int/i", $string) === 1) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_suspicious(string $string): bool { |
||||
if (self::found_sql_compair($string) === true) return true; |
||||
if (self::found_sql_stacker($string) === true) return true; |
||||
if (self::found_sql_comment($string) === true) return true; |
||||
if (self::found_quotes_in($string) === true) return true; |
||||
if (self::found_string_cat($string) === true) return true; |
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_admin_attack(string $string): bool { |
||||
if (preg_match("/admin/i", $string) === 1) { |
||||
if (preg_match("/ or /i", $string) === 1) { |
||||
if (preg_match("/1/", $string) === 1) { |
||||
return true; |
||||
} |
||||
if (preg_match("/0/", $string) === 1) { |
||||
return true; |
||||
} |
||||
} |
||||
if (self::found_sql_suspicious($string) === true) return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_statement(string $string): bool { |
||||
if (preg_match("/or/i", $string) === 1 || preg_match("/and/i", $string) === 1) { |
||||
if (self::found_sql_suspicious($string) === true) return true; |
||||
} |
||||
if (preg_match("/drop database/i", $string) === 1) return true; |
||||
if (preg_match("/drop table/i", $string) === 1) return true; |
||||
if (preg_match("/drop user/i", $string) === 1) return true; |
||||
if (preg_match("/drop view/i", $string) === 1) return true; |
||||
if (preg_match("/drop column/i", $string) === 1) return true; |
||||
if (preg_match("/drop procedure/i", $string) === 1) return true; |
||||
if (preg_match("/drop/i", $string) === 1) { |
||||
if (self::found_sql_suspicious($string) === true) return true; |
||||
} |
||||
if (preg_match("/where/i", $string) === 1) { |
||||
if (self::found_sql_suspicious($string) === true) return true; |
||||
} |
||||
if (preg_match("/select/i", $string) === 1) { |
||||
if (self::found_sql_suspicious($string) === true) return true; |
||||
} |
||||
if (preg_match("/having/i", $string) === 1) { |
||||
if (self::found_sql_suspicious($string) === true) return true; |
||||
} |
||||
if (preg_match("/distinct/i", $string) === 1) { |
||||
if (self::found_sql_suspicious($string) === true) return true; |
||||
} |
||||
// NOTE: \s* is for no-space or spaces!!! /i is to ignore Case! |
||||
if (preg_match("/\'\s*or/i", $string) === 1) return true; |
||||
if (preg_match("/\'\)\s*or/i", $string) === 1) return true; |
||||
if (preg_match("/\'\s*and/i", $string) === 1) return true; |
||||
if (preg_match("/\'\)\s*and/i", $string) === 1) return true; |
||||
if (preg_match("/or\s*true\-\-/", $string) === 1) return true; |
||||
if (preg_match("/if\s*\(/i", $string) === 1) return true; |
||||
if (preg_match("/\@\@version/i", $string) === 1) return true; // Get DB Ver |
||||
if (preg_match("/\@\@hostname/i", $string) === 1) return true; |
||||
if (preg_match("/\@\@datadir/i", $string) === 1) return true; |
||||
if (preg_match("/connection_id/i", $string) === 1) return true; // MySQL DB ID |
||||
if (preg_match("/crc32\s*\(/i", $string) === 1) return true; // MySQL DB ID |
||||
if (preg_match("/conv\s*\(/i", $string) === 1) return true; // MySQL DB ID |
||||
if (preg_match("/\@\@connections/i", $string) === 1) return true; // MSSQL DB ID |
||||
if (preg_match("/\@\@cpu_busy/i", $string) === 1) return true; // MSSQL DB ID |
||||
if (preg_match("/user_id\s*\(/i", $string) === 1) return true; // MSSQL DB ID |
||||
if (preg_match("/rownum/i", $string) === 1) return true; // Oracle DB ID |
||||
if (preg_match("/rawtohex\s*\(/i", $string) === 1) return true; // Oracle DB ID |
||||
if (preg_match("/lnnvl\s*\(/i", $string) === 1) return true; // Oracle DB ID |
||||
if (preg_match("/::int/i", $string) === 1) return true; // PostgreSQL DB ID |
||||
if (preg_match("/pg_client_encoding\s*\(/i", $string) === 1) return true; // PostgreSQL DB ID |
||||
if (preg_match("/get_current_ts_config\s*\(/i", $string) === 1) return true; // PostgreSQL DB ID |
||||
if (preg_match("/quote_literal\s*\(/i", $string) === 1) return true; // PostgreSQL DB ID |
||||
if (preg_match("/current_database\s*\(/i", $string) === 1) return true; // PostgreSQL DB ID |
||||
if (preg_match("/sqlite_version\s*\(/i", $string) === 1) return true; // SQLite DB ID |
||||
if (preg_match("/last_insert_rowid\s*\(/i", $string) === 1) return true; // SQLite DB ID |
||||
if (preg_match("/last_insert_rowid\s*\(/i", $string) === 1) return true; // SQLite DB ID |
||||
if (preg_match("/val\s*\(/i", $string) === 1) return true; // MSAccess DB ID |
||||
if (preg_match("/cvar\s*\(/i", $string) === 1) return true; // MSAccess DB ID |
||||
if (preg_match("/iif\s*\(/i", $string) === 1) return true; // MSAccess DB ID |
||||
if (preg_match("/atn\s*\(/i", $string) === 1) return true; // MSAccess DB ID |
||||
if (preg_match("/cdbl\s*\(/i", $string) === 1) return true; // MSAccess DB ID |
||||
if (preg_match("/grant\s*all/i", $string) === 1) return true; |
||||
if (preg_match("/into\s*dumpfile/i", $string) === 1) return true; |
||||
if (preg_match("/load\s*data\s*infile/i", $string) === 1) return true; |
||||
if (preg_match("/into\s*outfile/i", $string) === 1) return true; |
||||
if (preg_match("/load_file/i", $string) === 1) return true; |
||||
if (preg_match("/delete\s*from/i", $string) === 1) return true; |
||||
if (preg_match("/group_concat/i", $string) === 1) return true; |
||||
if (preg_match("/order by/i", $string) === 1) return true; |
||||
if (preg_match("/case when/i", $string) === 1) return true; |
||||
if (preg_match("/extractvalue/i", $string) === 1) return true; |
||||
if (preg_match("/\/etc\/passwd/i", $string) === 1) return true; |
||||
if (preg_match("/\/var\/log/i", $string) === 1) return true; |
||||
if (preg_match("/binary_checksum\s*\(/i", $string) === 1) return true; // ID MSSQL DB Engine |
||||
if (preg_match("/user\s*\(\)/i", $string) === 1) return true; // Get current user |
||||
if (preg_match("/system_user[\s]+\(\)/i", $string) === 1) return true; // Get current user |
||||
if (preg_match("/mysql.user/i", $string) === 1) return true; // List Users |
||||
if (preg_match("/mysql.db/i", $string) === 1) return true; // List Databases |
||||
if (preg_match("/database\s*\(/i", $string) === 1) return true; |
||||
if (preg_match("/information_schema/i", $string) === 1) return true; // List Columns |
||||
if (preg_match("/table_schema/i", $string) === 1) return true; |
||||
if (preg_match("/table_name/i", $string) === 1) return true; |
||||
if (preg_match("/\.columns/i", $string) === 1) return true; |
||||
if (preg_match("/\.tables/i", $string) === 1) return true; |
||||
|
||||
// https://www.w3schools.com/mysql/mysql_ref_functions.asp |
||||
$fns = [ |
||||
// String FNs |
||||
'ascii', 'char_length', 'character_length', 'concat', 'concat_ws', |
||||
'field', 'find_in_set', 'format', 'insert', 'instr', 'lcase', 'left', |
||||
'length', 'locate', 'lower', 'lpad', 'ltrim', 'mid', 'position', |
||||
'repeat', 'replace', 'reverse', 'right', 'rpad', 'rtrim', 'space', |
||||
'strcmp', 'substr', 'substring', 'substring_index', 'trim', 'ucase', |
||||
'upper', |
||||
// Numeric FNs |
||||
'abs','acos', 'asin', 'atan', 'atan2', 'avg', 'ceil', 'ceiling', 'cos', |
||||
'cot', 'count', 'degrees', 'div', 'exp', 'floor', 'greatest', 'least', |
||||
'ln', 'log', 'log10', 'log2', 'max', 'min', 'mod', 'pi', 'pow', 'power', |
||||
'radians', 'rand', 'round', 'sign', 'sin', 'sqrt', 'sum', 'tan', 'truncate', |
||||
// ADV FNs |
||||
'bin', 'binary', 'case', 'cast', 'coalesce', 'connection_id', 'conv', |
||||
'convert', 'current_user', 'database', 'if', 'ifnull', 'isnull', |
||||
'last_insert_id', 'nullif', 'session_user', 'system_user', 'user', |
||||
'version', |
||||
// OP FNs |
||||
'and', 'between', 'cast', 'char', 'count', 'div', 'in', 'is', 'is_not', |
||||
'is_not_null', 'is_null', 'like', 'member_of', 'not', 'not_between', |
||||
'not_in', 'not_like', 'not_regexp', 'or', 'regexp', 'sounds_like', |
||||
'floor', 'md5', 'rand', 'rlike', 'row', 'xor', |
||||
// https://dev.mysql.com/doc/refman/8.0/en/string-functions.html |
||||
'bit_length', 'let', 'export_set', 'from_base64', 'hex', 'load_file', |
||||
'make_set', 'match', 'oct', 'octet_length', 'ord', 'quote', 'regexp', |
||||
'regexp_instr', 'regexp_like', 'regexp_replace', 'regexp_substr', |
||||
'select', 'soundex', 'to_base64', 'unhex', 'weight_string' |
||||
]; |
||||
|
||||
foreach($fns as $fn_keyword) { |
||||
if (preg_match("/{$fn_keyword}\s*\(/i", $string) === 1) return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_evil_symbols(string $string): bool { |
||||
if (preg_match("/\-/", $string) === 1) return true; |
||||
if (preg_match("/\&/", $string) === 1) return true; |
||||
if (preg_match("/\*/", $string) === 1) return true; |
||||
if (preg_match("/\^/", $string) === 1) return true; |
||||
if (preg_match("/\'\s*\'/", $string) === 1) return true; |
||||
if (preg_match("/\"\s*\"/", $string) === 1) return true; |
||||
if (preg_match("/\)\)/", $string) === 1) return true; |
||||
if (preg_match("/\//", $string) === 1) return true; |
||||
if (function_exists("str_contains")) { |
||||
if (str_contains($string, '\')') === true) return true; |
||||
if (str_contains($string, '\")') === true) return true; |
||||
if (str_contains($string, '\`)') === true) return true; |
||||
if (str_contains($string, '\\') === true) return true; |
||||
if (str_contains($string, '%00') === true) return true; |
||||
if (str_contains($string, '\' or 1=1-- -') === true) return true; // Normal SQL |
||||
if (str_contains($string, '\' || 1==1//') === true) return true; // Mongo SQL |
||||
if (str_contains($string, '\' || 1==1%00') === true) return true; // Mongo SQL |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_bad_symbols(string $string): bool { |
||||
$ltc = ltrim($string); |
||||
$ltc_len = strlen($ltc); |
||||
$left_most = ($ltc_len < 6) ? $ltc_len : 6; |
||||
$first_chars = substr($ltc, 0, $left_most); |
||||
if ($first_chars !== false) { |
||||
if (function_exists("str_contains")) { |
||||
if (str_contains($first_chars, 'or') === true) return true; |
||||
if (str_contains($first_chars, 'and') === true) return true; |
||||
} |
||||
if (self::found_sql_suspicious($first_chars) === true) return true; |
||||
if (self::found_sql_evil_symbols($first_chars) === true) return true; |
||||
} |
||||
if (self::found_sql_suspicious($string) === false) return false; |
||||
|
||||
if (function_exists("str_contains")) { |
||||
if (str_contains($first_chars, '1') === true) return true; |
||||
if (str_contains($first_chars, '0') === true) return true; |
||||
if (str_contains($first_chars, '1337') === true) return true; |
||||
} |
||||
if (self::found_sql_evil_symbols($string) === true) return true; |
||||
return false; |
||||
} |
||||
|
||||
public static function found_sql_injection_cmds(string $string): bool { |
||||
/* |
||||
* There are a number of ways to prevent Poison Null Byte injections |
||||
* within PHP. These include escaping the NULL byte with a backslash, |
||||
* however, the most recommended way to do so is to completely remove |
||||
* the byte by using code similar to the following: |
||||
*/ |
||||
$string = str_replace(chr(0), '', $string); |
||||
if (self::found_sql_time_based_attack($string) === true) return true; |
||||
if (self::found_sql_admin_attack($string) === true) return true; |
||||
if (self::found_sql_bad_symbols($string) === true) return true; |
||||
if (self::found_sql_statement($string) === true) return true; |
||||
if (self::found_bad_json($string) === true) return true; |
||||
if (self::found_sql_comment($string) === true && |
||||
self::found_quotes_in($string) === true) { |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
private static function found_sql_keyword(string $string): bool { |
||||
$words = [ |
||||
'ACCESSIBLE','ACCOUNT','ACTION','ACTIVE','ADD','ADMIN','AFTER','AGAINST','AGGREGATE', |
||||
'ALGORITHM','ALL','ALTER','ALWAYS','ANALYSE','ANALYZE','AND','ANY','ARRAY','AS', |
||||
'ASC','ASCII','ASENSITIVE','AT','AUTOEXTEND_SIZE','AUTO_INCREMENT','AVG','AVG_ROW_LENGTH', |
||||
'BACKUP','BEFORE','BEGIN','BETWEEN','BIGINT','BINARY','BINLOG','BIT','BLOB','BLOCK', |
||||
'BOOL','BOOLEAN','BOTH','BTREE','BUCKETS','BY','BYTE','CACHE','CALL','CASCADE','CASCADED', |
||||
'CASE','CATALOG_NAME','CHAIN','CHANGE','CHANGED','CHANNEL','CHAR','CHARACTER','CHARSET', |
||||
'CHECK','CHECKSUM','CIPHER','CLASS_ORIGIN','CLIENT','CLONE','CLOSE','COALESCE','CODE', |
||||
'COLLATE','COLLATION','COLUMN','COLUMNS','COLUMN_FORMAT','COLUMN_NAME','COMMENT','COMMIT', |
||||
'COMMITTED','COMPACT','COMPLETION','COMPONENT','COMPRESSED','COMPRESSION','CONCURRENT', |
||||
'CONDITION','CONNECTION','CONSISTENT','CONSTRAINT','CONSTRAINT_CATALOG','CONSTRAINT_NAME', |
||||
'CONSTRAINT_SCHEMA','CONTAINS','CONTEXT','CONTINUE','CONVERT','CPU','CREATE','CROSS', |
||||
'CUBE','CUME_DIST','CURRENT','CURRENT_DATE','CURRENT_TIME','CURRENT_TIMESTAMP','CURRENT_USER', |
||||
'CURSOR','CURSOR_NAME','DATA','DATABASE','DATABASES','DATAFILE','DATE','DATETIME','DAY', |
||||
'DAY_HOUR','DAY_MICROSECOND','DAY_MINUTE','DAY_SECOND','DEALLOCATE','DEC','DECIMAL', |
||||
'DECLARE','DEFAULT','DEFAULT_AUTH','DEFINER','DEFINITION','DELAYED','DELAY_KEY_WRITE', |
||||
'DELETE','DENSE_RANK','DESC','DESCRIBE','DESCRIPTION','DES_KEY_FILE','DETERMINISTIC', |
||||
'DIAGNOSTICS','DIRECTORY','DISABLE','DISCARD','DISK','DISTINCT','DISTINCTROW','DIV', |
||||
'DO','DOUBLE','DROP','DUAL','DUMPFILE','DUPLICATE','DYNAMIC','EACH','ELSE','ELSEIF', |
||||
'EMPTY','ENABLE','ENCLOSED','ENCRYPTION','END','ENDS','ENFORCED','ENGINE','ENGINES', |
||||
'ENUM','ERROR','ERRORS','ESCAPE','ESCAPED','EVENT','EVENTS','EVERY','EXCEPT', |
||||
'EXCHANGE','EXCLUDE','EXECUTE','EXISTS','EXIT','EXPANSION','EXPIRE','EXPLAIN', |
||||
'EXPORT','EXTENDED','EXTENT_SIZE','FAILED_LOGIN_ATTEMPTS','FALSE','FAST','FAULTS', |
||||
'FETCH','FIELDS','FILE','FILE_BLOCK_SIZE','FILTER','FIRST','FIRST_VALUE','FIXED', |
||||
'FLOAT','FLOAT4','FLOAT8','FLUSH','FOLLOWING','FOLLOWS','FOR','FORCE','FOREIGN', |
||||
'FORMAT','FOUND','FROM','FULL','FULLTEXT','FUNCTION','GENERAL','GENERATED', |
||||
'GEOMCOLLECTION','GEOMETRY','GEOMETRYCOLLECTION','GET','GET_FORMAT','GET_MASTER_PUBLIC_KEY', |
||||
'GLOBAL','GRANT','GRANTS','GROUP','GROUPING','GROUPS','GROUP_REPLICATION','HANDLER', |
||||
'HASH','HAVING','HELP','HIGH_PRIORITY','HISTOGRAM','HISTORY','HOST','HOSTS','HOUR', |
||||
'HOUR_MICROSECOND','HOUR_MINUTE','HOUR_SECOND','IDENTIFIED','IF','IGNORE', |
||||
'IGNORE_SERVER_IDS','IMPORT','IN','INACTIVE','INDEX','INDEXES','INFILE','INITIAL_SIZE', |
||||
'INNER','INOUT','INSENSITIVE','INSERT','INSERT_METHOD','INSTALL','INSTANCE','INT','INT1', |
||||
'INT2','INT3','INT4','INT8','INTEGER','INTERVAL','INTO','INVISIBLE','INVOKER','IO', |
||||
'IO_AFTER_GTIDS','IO_BEFORE_GTIDS','IO_THREAD','IPC','IS','ISOLATION','ISSUER','ITERATE', |
||||
'JOIN','JSON','JSON_TABLE','JSON_VALUE','KEY','KEYS','KEY_BLOCK_SIZE','KILL','LAG', |
||||
'LANGUAGE','LAST','LAST_VALUE','LATERAL','LEAD','LEADING','LEAVE','LEAVES','LEFT', |
||||
'LESS','LEVEL','LIKE','LIMIT','LINEAR','LINES','LINESTRING','LIST','LOAD','LOCAL', |
||||
'LOCALTIME','LOCALTIMESTAMP','LOCK','LOCKED','LOCKS','LOGFILE','LOGS','LONG', |
||||
'LONGBLOB','LONGTEXT','LOOP','LOW_PRIORITY','MASTER','MASTER_AUTO_POSITION', |
||||
'MASTER_BIND','MASTER_COMPRESSION_ALGORITHMS','MASTER_CONNECT_RETRY','MASTER_DELAY', |
||||
'MASTER_HEARTBEAT_PERIOD','MASTER_HOST','MASTER_LOG_FILE', |
||||
'MASTER_LOG_POS','MASTER_PASSWORD','MASTER_PORT','MASTER_PUBLIC_KEY_PATH', |
||||
'MASTER_RETRY_COUNT','MASTER_SERVER_ID','MASTER_SSL','MASTER_SSL_CA', |
||||
'MASTER_SSL_CAPATH','MASTER_SSL_CERT','MASTER_SSL_CIPHER','MASTER_SSL_CRL', |
||||
'MASTER_SSL_CRLPATH','MASTER_SSL_KEY','MASTER_SSL_VERIFY_SERVER_CERT', |
||||
'MASTER_TLS_CIPHERSUITES','MASTER_TLS_VERSION','MASTER_USER', |
||||
'MASTER_ZSTD_COMPRESSION_LEVEL','MATCH','MAXVALUE','MAX_CONNECTIONS_PER_HOUR', |
||||
'MAX_QUERIES_PER_HOUR','MAX_ROWS','MAX_SIZE','MAX_UPDATES_PER_HOUR','MAX_USER_CONNECTIONS', |
||||
'MEDIUM','MEDIUMBLOB','MEDIUMINT','MEDIUMTEXT','MEMBER','MEMORY','MERGE','MESSAGE_TEXT', |
||||
'MICROSECOND','MIDDLEINT','MIGRATE','MINUTE','MINUTE_MICROSECOND','MINUTE_SECOND', |
||||
'MIN_ROWS','MOD','MODE','MODIFIES','MODIFY','MONTH','MULTILINESTRING','MULTIPOINT', |
||||
'MULTIPOLYGON','MUTEX','MYSQL_ERRNO','NAME','NAMES','NATIONAL','NATURAL','NCHAR', |
||||
'NDB','NDBCLUSTER','NESTED','NETWORK_NAMESPACE','NEVER','NEW','NEXT','NO','NODEGROUP', |
||||
'NONE','NOT','NOWAIT','NO_WAIT','NO_WRITE_TO_BINLOG','NTH_VALUE','NTILE','NULL','NULLS', |
||||
'NUMBER','NUMERIC','NVARCHAR','OF','OFF','OFFSET','OJ','OLD','ON','ONE','ONLY','OPEN', |
||||
'OPTIMIZE','OPTIMIZER_COSTS','OPTION','OPTIONAL','OPTIONALLY','OPTIONS','OR','ORDER', |
||||
'ORDINALITY','ORGANIZATION','OTHERS','OUT','OUTER','OUTFILE','OVER','OWNER','PACK_KEYS', |
||||
'PAGE','PARSER','PARTIAL','PARTITION','PARTITIONING','PARTITIONS','PASSWORD', |
||||
'PASSWORD_LOCK_TIME','PATH','PERCENT_RANK','PERSIST','PERSIST_ONLY','PHASE','PLUGIN', |
||||
'PLUGINS','PLUGIN_DIR','POINT','POLYGON','PORT','PRECEDES','PRECEDING','PRECISION', |
||||
'PREPARE','PRESERVE','PREV','PRIMARY','PRIVILEGES','PRIVILEGE_CHECKS_USER','PROCEDURE', |
||||
'PROCESS','PROCESSLIST','PROFILE','PROFILES','PROXY','PURGE','QUARTER','QUERY','QUICK', |
||||
'RANDOM','RANGE','RANK','READ','READS','READ_ONLY','READ_WRITE','REAL','REBUILD', |
||||
'RECOVER','RECURSIVE','REDOFILE','REDO_BUFFER_SIZE','REDUNDANT','REFERENCE','REFERENCES', |
||||
'REGEXP','RELAY','RELAYLOG','RELAY_LOG_FILE','RELAY_LOG_POS','RELAY_THREAD','RELEASE', |
||||
'RELOAD','REMOTE','REMOVE','RENAME','REORGANIZE','REPAIR','REPEAT','REPEATABLE','REPLACE', |
||||
'REPLICATE_DO_DB','REPLICATE_DO_TABLE','REPLICATE_IGNORE_DB','REPLICATE_IGNORE_TABLE', |
||||
'REPLICATE_REWRITE_DB','REPLICATE_WILD_DO_TABLE','REPLICATE_WILD_IGNORE_TABLE', |
||||
'REPLICATION','REQUIRE','REQUIRE_ROW_FORMAT','RESET','RESIGNAL','RESOURCE','RESPECT', |
||||
'RESTART','RESTORE','RESTRICT','RESUME','RETAIN','RETURN','RETURNED_SQLSTATE','RETURNING', |
||||
'RETURNS','REUSE','REVERSE','REVOKE','RIGHT','RLIKE','ROLE','ROLLBACK','ROLLUP','ROTATE', |
||||
'ROUTINE','ROW','ROWS','ROW_COUNT','ROW_FORMAT','ROW_NUMBER','RTREE','SAVEPOINT','SCHEDULE', |
||||
'SCHEMA','SCHEMAS','SCHEMA_NAME','SECOND','SECONDARY','SECONDARY_ENGINE','SECONDARY_LOAD', |
||||
'SECONDARY_UNLOAD','SECOND_MICROSECOND','SECURITY','SELECT','SENSITIVE','SEPARATOR','SERIAL', |
||||
'SERIALIZABLE','SERVER','SESSION','SET','SHARE','SHOW','SHUTDOWN','SIGNAL','SIGNED','SIMPLE', |
||||
'SKIP','SLAVE','SLOW','SMALLINT','SNAPSHOT','SOCKET','SOME','SONAME','SOUNDS','SOURCE', |
||||
'SPATIAL','SPECIFIC','SQL','SQLEXCEPTION','SQLSTATE','SQLWARNING','SQL_AFTER_GTIDS', |
||||
'SQL_AFTER_MTS_GAPS','SQL_BEFORE_GTIDS','SQL_BIG_RESULT','SQL_BUFFER_RESULT','SQL_CACHE', |
||||
'SQL_CALC_FOUND_ROWS','SQL_NO_CACHE','SQL_SMALL_RESULT','SQL_THREAD','SQL_TSI_DAY', |
||||
'SQL_TSI_HOUR','SQL_TSI_MINUTE','SQL_TSI_MONTH','SQL_TSI_QUARTER','SQL_TSI_SECOND', |
||||
'SQL_TSI_WEEK','SQL_TSI_YEAR','SRID','SSL','STACKED','START','STARTING','STARTS', |
||||
'STATS_AUTO_RECALC','STATS_PERSISTENT','STATS_SAMPLE_PAGES','STATUS','STOP','STORAGE', |
||||
'STORED','STRAIGHT_JOIN','STREAM','STRING','SUBCLASS_ORIGIN','SUBJECT','SUBPARTITION', |
||||
'SUBPARTITIONS','SUPER','SUSPEND','SWAPS','SWITCHES','SYSTEM','TABLE','TABLES','TABLESPACE', |
||||
'TABLE_CHECKSUM','TABLE_NAME','TEMPORARY','TEMPTABLE','TERMINATED','TEXT','THAN','THEN', |
||||
'THREAD_PRIORITY','TIES','TIME','TIMESTAMP','TIMESTAMPADD','TIMESTAMPDIFF','TINYBLOB', |
||||
'TINYINT','TINYTEXT','TLS','TO','TRAILING','TRANSACTION','TRIGGER','TRIGGERS','TRUE', |
||||
'TRUNCATE','TYPE','TYPES','UNBOUNDED','UNCOMMITTED','UNDEFINED','UNDO','UNDOFILE', |
||||
'UNDO_BUFFER_SIZE','UNICODE','UNINSTALL','UNION','UNIQUE','UNKNOWN','UNLOCK','UNSIGNED', |
||||
'UNTIL','UPDATE','UPGRADE','USAGE','USE','USER','USER_RESOURCES','USE_FRM','USING', |
||||
'UTC_DATE','UTC_TIME','UTC_TIMESTAMP','VALIDATION','VALUE','VALUES','VARBINARY', |
||||
'VARCHAR','VARCHARACTER','VARIABLES','VARYING','VCPU','VIEW','VIRTUAL','VISIBLE','WAIT', |
||||
'WARNINGS','WEEK','WEIGHT_STRING','WHEN','WHERE','WHILE','WINDOW','WITH','WITHOUT', |
||||
'WORK','WRAPPER','WRITE','X509','XA','XID','XML','XOR','YEAR','YEAR_MONTH','ZEROFILL' |
||||
]; |
||||
$parts = preg_split('/\s+/', $string); |
||||
foreach($parts as $part) { |
||||
$lc = mb_strtoupper($part); |
||||
if (in_array($lc, $words)) return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private static function try_to_clean_sql(string $string): string { |
||||
$bad_items = [ |
||||
'.user', '.db','.columns', '.tables', 'load_file', 'table_', |
||||
'_schema', 'connection_id', 'binary_checksum', 'cpu_busy', |
||||
'user_id', 'rownum', 'rawtohex', 'lnnvl', 'pg_client_encoding', |
||||
'get_current_ts_config', 'quote_literal', 'current_database', |
||||
'sqlite_version', 'last_insert_rowid', 'last_insert_rowid', |
||||
'group_concat', 'system_user' |
||||
|
||||
]; |
||||
return str_replace($bad_items, "", $string); |
||||
} |
||||
|
||||
public static function get_safer_sql_text(string $string): array { |
||||
$string = str_replace(chr(0), '', $string); // Remove NULL Bytes |
||||
if (self::found_sql_injection_cmds($string)) { |
||||
/* |
||||
* At this Point: Never allow Quotes, @$*=()%` |
||||
* Limit allowed chars now, to safe ones! |
||||
*/ |
||||
$cleaner = self::try_to_clean_sql($string); |
||||
$safer = preg_replace('/[^a-zA-Z0-9.\s]/', "", $cleaner); |
||||
|
||||
if (self::found_sql_keyword($safer)) { |
||||
return ["text"=>$safer, "status"=>SQL_SAFETY_FLAG::dangerious]; |
||||
} |
||||
return ["text"=>$safer, "status"=>SQL_SAFETY_FLAG::filtered]; |
||||
} |
||||
return ["text"=>$string, "status"=>SQL_SAFETY_FLAG::good]; |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue