Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QueryLogger strips some characters from queries #10181

Merged
merged 1 commit into from Feb 9, 2017

Conversation

garas
Copy link
Member

@garas garas commented Feb 7, 2017

When using DebugKit SqlLog, I noticed that when saving hashed user password, log contained
UPDATE users SET password = 'y$uEFBAE...' WHERE id = 123 which is not in valid Bcrypt format.

But after checking database, password was saved correctly as '$2a$10$uEFBAE...'.

I enabled logging to text file - still same result, so not fault of DebugKit.

After inspecting code, problem is that QueryLogger interpolates bind parameters using preg_replace: but preg_replace replacement string uses $1 or \\1 for backreferences.

$2 and $10 in hash refers to invalid backrefences, so replaced with empty strings.

Another possible common string could be "$0.12" which would replace to ":p1.12" because $0 refers to bind parameter matched by the whole pattern.


My commit escapes $ and \ to \$ and \\ to ignore them by preg_replace.

Also I replace ' to double single-quote '' and \ to \\ to be even more valid SQL string. This is why there is \\ to \\\\\\\\ replacement (preg_replace and SQL escape).

@codecov-io
Copy link

Codecov Report

Merging #10181 into master will increase coverage by -0.01%.

@@             Coverage Diff              @@
##             master   #10181      +/-   ##
============================================
- Coverage     94.94%   94.94%   -0.01%     
  Complexity    11613    11613              
============================================
  Files           417      417              
  Lines         28734    28738       +4     
============================================
+ Hits          27281    27284       +3     
- Misses         1453     1454       +1
Impacted Files Coverage Δ Complexity Δ
src/Database/Log/QueryLogger.php 100% <100%> (ø) 8 <ø> (ø)
src/Cache/CacheEngine.php 91.3% <ø> (-2.18%) 19% <ø> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8a4a496...1dec2bf. Read the comment docs.

"'" => "''",
];

$p = strtr($p, $replacements);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of manually replacing specific characters it would be better to instead use preg_quote() when generating the $keys array below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written in preg_quote()

Note that preg_quote() is not meant to be applied to the $replacement string(s) of preg_replace() etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My mistake, I misread the code. You are modifying the $params var not $keys.

@markstory markstory added this to the 3.3.15 milestone Feb 7, 2017

$logger->expects($this->once())->method('_log')->with($query);
$logger->log($query);
$expected = "duration=0 rows=0 SELECT a FROM b where a = '\$2y\$10\$dUAIj' AND b = '\$0.23' AND c = 'a\\\\0b\\\\1c\\\\d' AND d = 'a''b'";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the \ have been doubled up. Shouldn't the value match the parameters?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The final SQL is
SELECT a FROM b where a = '$2y$10$dUAIj' AND b = '$0.23' AND c = 'a\\0b\\1c\\d' AND d = 'a''b'

And the final strings after SQL unescape are c = a\0b\1c\d and d = a'b - c has backslash and d has single-quote and matches 'p3' => 'a\\0b\\1c\\d', 'p4' => "a'b" after PHP unescape.

If it would be \\ instead of \\\\, SQL would get \0 which would result to null-byte after SQL unescape.

This is light version of PDO::quote() - only escapes \ to \\ and ' to ''.
While it is not complete solution for safe SQL escape, but developer needs less modifications before testing SQL elsewhere.

The ' escape to '' is supported by all 4 CakePHP core SQL drivers.

The \ escape to \\ is supported by MySQL and PostgreSQL, but in Sqlite and SQL Server the \ in string literals has no special meaning.


  1. The commit fixes preg_replace part
$replacements = [
    '$' => '\\$',
    '\\' => '\\\\',
];
  1. Then enhances generated SQL string quoting
$replacements = [
    '\\\\' => '\\\\\\\\',
    "'" => "''",
];

Should I:

  • leave only part 1
  • leave part 1, and then escape only ' to '' (MySQL/PostgreSQL developer will need escape \ manually)
  • or stay with current commit - leave part 1, and then escape both ' and \ (Sqlite/SQL Server developer will need revert \\ back to \ manually)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These generated strings are never used as queries though. Wouldn't we want to emulate what the SQL server would be see?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL search for string It's m\n\o is:

  1. SELECT a FROM b WHERE c = 'It's m\n\o' - current QuerryLogger emulated query
  2. SELECT a FROM b WHERE c = 'It''s m\\n\\o' - valid in MySQL/PostgreSQL
  3. SELECT a FROM b WHERE c = 'It''s m\n\o' - valid in Sqlite/SQL Server

My commit changed SQL to 2nd variant because I work with MySQL and I didn't thought about other DBs. But after I checked syntax of string literal in other DBs, now I'm thinking which variant is best.

QueryLogger is not DB specific. Emulated queries are not executed so security is low priority. But sometimes we need test queries externally if CakePHP works not as expected. I want it to be as much as possible evenly helpful for every DB engine developer.

So I ask which variant is the best? Do CakePHP have higher priorities to some DB engine?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I ask which variant is the best? Do CakePHP have higher priorities to some DB engine?

Not really. Given that the current output works for some/most DBs, we'll go with what is present now.

@markstory markstory merged commit 7161157 into cakephp:master Feb 9, 2017
@garas garas deleted the querylogger-fixes branch February 9, 2017 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants