Home » Php » Does PHP's DateTime class escape date from SQL injection

Does PHP's DateTime class escape date from SQL injection

Posted by: admin July 12, 2020 Leave a comment

Questions:

Usually, I use PDO’s prepared statements, type casting to (int), or PDO::quote() to prevent SQL injection. For this application, I need to modify the date using PHP before adding it to the query. Do I need to take extra steps to prevent SQL injection, or am I safe? Thanks

$date = new DateTime($_GET['suspect_user_provided_date']);
$date->add(new DateInterval('P1D'));
$sql='SELECT * FROM table WHERE date<"'.$date->format('Y-m-d').'"';
How to&Answers:

It doesn’t matter if the DateTime object is safe or not. You should escape the data you are passing to the query and not to rely on the safety of the provided library. If you change the implementation, you will not need to care if the new implementation is safe or not. You should always escape. Otherwise you will try to answer – and to remember – for each function – was it safe for SQL? for HTML? for CSV? for http / mail headers? for… don’t! The line of code that send a query should know nothing about the DateTime implementation and if it’s safe or not

Answer:

It’s a good question: is the PHP date formatter safe from SQL injection?

I guess the starting point is whether the format is hard coded as per your example. The date format string allows raw characters to be included in the formatted date, which could include unsafe characters, so if you’re using a variable for the format string then the answer is definitely No, it isn’t safe.

If you are using a hard coded format, as in the example you’ve given, then it’s a more difficult question, but it boils down to “Can the the output of DateTime::format ever deviate from the desired format?”

The answer to this is Yes it can — it can output false if it fails. This won’t break your SQL, but could give you unexpected results.

In theory, that should be as bad as it gets.

However, you should be thinking defensively. There’s nothing to say that a subtle bug won’t be found in the DateTime class that causes it to output a badly formatted date. Ordinarily, this kind of bug wouldn’t be considered a security issue; it would just be an annoyance. Particularly if it’s difficult to reproduce in normal use. But combined with passing it directly to SQL, it could easily be a security problem.

The lesson is Defensive Programming: Sanitise everything. Even if you are sure it’s safe. Don’t assume that your language or frameword doesn’t contain bugs. Defensive programming means being secure at every level, so that an unexpected bug further up the program or outside of your control can’t leave your code open to attack.

Answer:

The DateTime constructor will parse the argument and try to make it a date.

If it can’t it returns false (actually throws an exception as of PHP 5.3).

Meaning you are safe since SQL injection attempts to “trick” the SQL query string with quotes for instance, while new DateTime returns either a DateTime instance or false (throws exception).
But you could anyway process the error case (from PHP 5.3)

try {
  $date = new DateTime($_GET['suspect_user_provided_date']);
}
catch (Exception $e) {
  echo "Error";
  exit;
}

$date->add(new DateInterval('P1D'));
$sql='SELECT * FROM table WHERE date<"'.$date->format('Y-m-d').'"';

Answer:

The example you’ve posted is safe. But why not using prepared statements? Like this:

     $pdo = new PDO($dsn, $user, $pass, $options = array (
        PDO::ATTR_ERRMODE,  PDO::ERRMODE_EXCEPTION
     ));

     $date = new DateTime($_GET['suspect_user_provided_date']);
     $date->add(new DateInterval('P1D'));

     $sql='
     SELECT
        * 
     FROM table 
     WHERE date < :dt';

     $stmt = $pdo->prepare($sql);
     $params = array (
        'dt' => $date->format('Y-m-d')
     );
     try {
         $res = $pdo->execute($params);
     } catch (PDOException $e) {
         echo $e->getMessage();
     }

Answer:

When it comes to SQL, it’s often better not to think about what would be safe and what’s not; just handle it with prepared statements:

$stmt = $db->prepare('SELECT * FROM table WHERE `date` < :now');
$stmt->execute(array(
    ':now' => $date->format('Y-m-d'),
));

Answer:

Depending on the output format of the date. If you were to change the format and put it to output some text (like the month or day name in the current locale), it may lead to unsafe (or at least failing) queries because it may have some quotes. Or you could put quotes yourself for some reason. So yes, do not doubt and use pdo->quote() or better yet, prepared statements.

// safe
$date = new DateTime($_GET['suspect_user_provided_date']);
$date->add(new DateInterval('P1D'));
$sql='SELECT * FROM table WHERE date<"'.$date->format('Y-m-d').'"';

// failing
$date = new DateTime($_GET['suspect_user_provided_date']);
$date->add(new DateInterval('P1D'));
$sql='SELECT * FROM table WHERE date<"'.$date->format('\O\"\h\a\i \t\o\d\a\y \i\s Y-m-D').'"';

// not failing, whatever format you are using, using pdo::quote
$date = new DateTime($_GET['suspect_user_provided_date']);
$date->add(new DateInterval('P1D'));
$sql='SELECT * FROM table WHERE date<'.$dbh->quote($date->format('\O\"\h\a\i \t\o\d\a\y \i\s Y-m-D'));

// not failing, whatever format you are using, using prepared statments
$stmt = $dbh->prepare('SELECT * FROM table WHERE date < :date');
$date = new DateTime($_GET['suspect_user_provided_date']);
$date->add(new DateInterval('P1D'));
$stmt->bindValue(':date', $date->format('\O\"\h\a\i \t\o\d\a\y \i\s Y-m-D'), PDO::PARAM_STR);
$stmt->execute();