Files
BE_IBL/application/core/MY_Input.php
2026-04-15 15:16:12 +07:00

293 lines
8.4 KiB
PHP

<?php
defined('BASEPATH') or exit('No direct script access allowed');
/**
* Enhanced Input Class
*
* Extends CodeIgniter's native Input class to provide additional XSS and SQL injection protection.
*
* @package CodeIgniter
* @subpackage Libraries
* @category Input
*/
class MY_Input extends CI_Input
{
/**
* @var CI_Controller CodeIgniter instance
*/
protected $CI;
/**
* @var array Configuration for input sanitization
*/
private $config = [
'encoding' => 'UTF-8',
'html_entities_flags' => ENT_QUOTES,
];
/**
* @var array SQL injection patterns to detect
*/
private $sqlInjectionPatterns = [
// Previous patterns
'/\b(union\s+all|union\s+select|insert\s+into|drop\s+table|exec\s+xp|exec\s+sp)\b/i',
'/\b(or\s+\d+=\d+|or\s+\'.*\'=\'.*\'|or\s+[\'"].*[\'"]=[\'"].*[\'"])\b/i',
'/(\-\-\s*$|\/\*|\*\/|#\s*$)/',
'/\b(concat|char|cast|convert|decode|encode|eval|exec)\s*\(/i',
'/\b(xp_cmdshell|xp_regread|sp_password|sp_executesql)\b/i',
// Enhanced time-based injection patterns
'/\b(?:and|or|if)\s*\(?[\s\w]*(?:sleep|delay|benchmark|wait)\s*[\(\s]*\d+[^\w\s]*\)?/i',
// Enhanced conditional patterns
'/\b(?:and|or|if)\s*\([^)]*(?:sleep|delay|benchmark|wait)[^)]*\)/i',
// Case-insensitive function detection (including spaces and parentheses)
'/\b(?:s[\s]*l[\s]*e[\s]*e[\s]*p|d[\s]*e[\s]*l[\s]*a[\s]*y|b[\s]*e[\s]*n[\s]*c[\s]*h[\s]*m[\s]*a[\s]*r[\s]*k)\s*\(/i',
// Additional patterns for IF statements
'/\b(?:if)\s*\(\s*(?:sleep|delay|benchmark|wait)\s*\(\s*\d+\s*\)\s*,\s*\d+\s*,\s*\d+\s*\)/i',
// Pattern specifically for your case
'/\band\s+if\s*\(\s*sleep\s*\(\s*\d+\s*\)\s*,\s*\d+\s*,\s*\d+\s*\)/i',
// Previous patterns continue
'/\b(0x[0-9a-f]+)\b/i',
'/(\s+and\s+\d+=\d+|\s+and\s+\'.*\'=\'.*\'|\s+and\s+[\'"].*[\'"]=[\'"].*[\'"])/i'
];
/**
* @var array Suspicious characters that might indicate SQL injection
*/
private $suspiciousChars = [
';', // Statement terminator
'\'', // Single quote
'"', // Double quote
'`', // Backtick
'\\', // Backslash
'\x00', // Null byte
'\x1a', // Ctrl+Z
];
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
}
/**
* Get sanitized GET data
*
* @param string|null $index Index for retrieving the GET data
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
public function get($index = null, $xss_clean = true)
{
return $this->sanitizeInput(
parent::get($index, $xss_clean)
);
}
/**
* Get sanitized POST data
*
* @param string|null $index Index for retrieving the POST data
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
public function post($index = null, $xss_clean = true)
{
return $this->sanitizeInput(
parent::post($index, $xss_clean)
);
}
/**
* Get raw input stream
*
* @return string
*/
// Harusnya override raw_input_stream tapi takut kalau merubah banyak fungsi yang pakai input->raw_input_stream
// public function getRawInput()
public function raw_input_stream()
{
$input = file_get_contents('php://input');
return $this->sanitizeInput($input);
}
/**
* Sanitize input data
*
* @param mixed $input Input data to sanitize
* @return mixed
*/
protected function sanitizeInput($input)
{
if (is_string($input)) {
return $this->sanitizeString($input);
}
if (is_array($input)) {
return $this->sanitizeArray($input);
}
return $input;
}
/**
* Sanitize an array recursively
*
* @param array $input Array to sanitize
* @return array
*/
protected function sanitizeArray(array $input): array
{
return array_map(
function ($item) {
return $this->sanitizeInput($item);
},
$input
);
}
/**
* Sanitize a string value
*
* @param string $input String to sanitize
* @return string
*/
protected function sanitizeString(string $input): string
{
// Return empty string for null input
if ($input === null) {
return '';
}
$clean = trim($input);
// Check for SQL injection attempts
if ($this->containsSQLInjection($clean)) {
// log_message('warning', 'Potential SQL injection detected: ' . $clean);
return '';
}
// Basic sanitization
$clean = filter_var($clean, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
// Remove null bytes
$clean = str_replace(chr(0), '', $clean);
// Remove control characters
$clean = preg_replace('/[\x00-\x1F\x7F]/u', '', $clean);
// Convert special characters to HTML entities
$clean = htmlspecialchars(
$clean,
$this->config['html_entities_flags'],
$this->config['encoding']
);
return $clean;
}
/**
* Check if a string contains SQL injection attempts
*
* @param string $input String to check
* @return bool
*/
protected function containsSQLInjection(string $input): bool
{
// Normalize input for better detection
$normalizedInput = $this->normalizeInput($input);
// Check original and normalized input against patterns
foreach ($this->sqlInjectionPatterns as $pattern) {
if (preg_match($pattern, $input) || preg_match($pattern, $normalizedInput)) {
return true;
}
}
// Check for suspicious characters
foreach ($this->suspiciousChars as $char) {
if (strpos($input, $char) !== false) {
return true;
}
}
// Additional check specifically for time-based attacks
if ($this->containsTimeBased($normalizedInput)) {
return true;
}
return false;
}
/**
* Normalize input for consistent checking
*
* @param string $input
* @return string
*/
private function normalizeInput(string $input): string
{
// Convert to lowercase
$normalized = strtolower($input);
// Remove extra spaces
$normalized = preg_replace('/\s+/', ' ', $normalized);
// Remove url encoding
$normalized = urldecode($normalized);
// Replace common obfuscation patterns
$replacements = [
'/s[\s]*l[\s]*e[\s]*e[\s]*p/' => 'sleep',
'/i[\s]*f/' => 'if',
'/a[\s]*n[\s]*d/' => 'and',
'/o[\s]*r/' => 'or',
'/b[\s]*e[\s]*n[\s]*c[\s]*h[\s]*m[\s]*a[\s]*r[\s]*k/' => 'benchmark',
'/w[\s]*a[\s]*i[\s]*t[\s]*f[\s]*o[\s]*r/' => 'waitfor'
];
foreach ($replacements as $pattern => $replacement) {
$normalized = preg_replace($pattern, $replacement, $normalized);
}
return $normalized;
}
/**
* Specific check for time-based SQL injection attempts
*
* @param string $input
* @return bool
*/
private function containsTimeBased(string $input): bool
{
$timeBased = [
// Detect IF(SLEEP()) pattern with variations
'/if\s*\(\s*sleep\s*\(\s*\d+\s*\)\s*,\s*\d+\s*,\s*\d+\s*\)/i',
// Detect AND IF(SLEEP()) pattern
'/and\s+if\s*\(\s*sleep\s*\(\s*\d+\s*\)\s*,\s*\d+\s*,\s*\d+\s*\)/i',
// Additional time-based patterns
'/benchmark\s*\(\s*\d+\s*,\s*[^)]+\)/i',
'/waitfor\s+delay\s+\'\d+\:\d+\:\d+\'/i',
'/pg_sleep\s*\(\s*\d+\s*\)/i'
];
foreach ($timeBased as $pattern) {
if (preg_match($pattern, $input)) {
return true;
}
}
return false;
}
}