Michael Chourdakis
Michael Chourdakis

Reputation: 11178

PHP detect SQL injection attempt

My code is already safe, using parameters in SQL queries, but, I would like to detect if anyone attempts to inject something into a submit form.

I found Snort, but I would need something that would be at PHP script level, not the whole network.

This is for a site that contains personal information for students and thus, we will warn (or even take action against) anyone that even tries an attack.

Upvotes: 2

Views: 6074

Answers (2)

BillyK.
BillyK.

Reputation: 71

I have created a very basic and simple PHP class for checking / detecting SQL injection attempts.

<?php
/**
 * simpleSQLinjectionDetect Class
 * @link      https://github.com/bs4creations/simpleSQLinjectionDetect 
 * @version   1.1
 */

class simpleSQLinjectionDetect
{   
    protected $_method  = array();
    protected $_suspect = null; 

    public $_options = array(
                            'log'    => true,
                            'unset'  => true,
                            'exit'   => true,
                            'errMsg' => 'Not allowed',
                        );

    public function detect()
    {
        self::setMethod();

        if(!empty($this->_method))
        {
            $result = self::parseQuery();

            if ($result)
            {
                if ($this->_options['log']) {
                    self::logQuery();
                }

                if ($this->_options['unset']){
                    unset($_GET, $_POST);
                }

                if ($this->_options['exit']){
                    exit($this->_options['errMsg']);
                }
            }
        }
    }

    private function setMethod()
    {
        if ($_SERVER['REQUEST_METHOD'] === 'GET') {
            $this->_method = $_GET;
        }

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $this->_method = $_POST;
        }
    }

    private function parseQuery()
    {
        $operators = array(
            'select * ',
            'select ',
            'union all ',
            'union ',
            ' all ',
            ' where ',
            ' and 1 ',
            ' and ',
            ' or ',
            ' 1=1 ',
            ' 2=2 ',
            ' -- ',
        );

        foreach($this->_method as $key => $val)
        {
            $k = urldecode(strtolower($key));
            $v = urldecode(strtolower($val));

            foreach($operators as $operator)
            {
                if (preg_match("/".$operator."/i", $k)) {
                    $this->_suspect = "operator: '".$operator."', key: '".$k."'";
                    return true;
                }
                if (preg_match("/".$operator."/i", $v)) {
                    $this->_suspect = "operator: '".$operator."', val: '".$v."'";
                    return true;
                }
            }
        }
    }

    private function logQuery()
    {
        $data  = date('d-m-Y H:i:s') . ' - ';
        $data .= $_SERVER['REMOTE_ADDR'] . ' - ';
        $data .= 'Suspect: ['.$this->_suspect.'] ';
        $data .= json_encode($_SERVER);
        @file_put_contents('./logs/sql.injection.txt', $data . PHP_EOL, FILE_APPEND);
    }
}

/* then call it in your app...
*********************************************/
$inj = new simpleSQLinjectionDetect();
$inj->detect();

You can check it on github also

This is a very simple and basic class. Any suggestions for improvements / updates are welcome :)

Upvotes: 5

Martin
Martin

Reputation: 22770

This is actually quite a hard topic. BillyK may have a semi-viable approach but it's better to let MySQL do the hard work for you; therefore:

  • 1) Run the user-constructed (ie unsafe) query in a MySQL Transaction.
  • 2) How many results does it give? (Check for both rows returned and rows affected)
  • 3) Record any MySQL error warning logs.
  • 4) Cancel / rollback the Transaction. So that nothing has changed on your database.
  • 5) Re-run the query with the paramaterised variable (ie safe)
  • 6) How many results does it give? (Check for both rows returned and rows affected)
  • 7) Check if (6) gives a different number of results to (2) or if (5) gives any SQL error warnings. You can also use PHP array comparison features to check if the result sets are equal.
  • 8) Any positives come up, such as differences in result counts, result set eqauality or SQL warnings, then record that query string into a save file for human review.

Concept Thoughts:

With a properly implemented system of Prepared Statements it is not possible for SQL injection to occur from user variables as data strings. Therefore, rather like people throwing water balloons at tanks; it's also pretty worthless to try and "detect" these infractions; they in themselves don't show you anything more than someone read some website that offers such methods.

Therefore, as long as you have built your PHP/SQL correctly then any number or any quality of SQL injecton attempts are just water off a ducks back, and you cumulatively waste more processing power and time and effort trying to detect and record them than you would simply to ignore them.

Upvotes: 2

Related Questions