[5.8] Correctly escape single quotes in json paths - #28160
Conversation
5167fc6 to
be1896c
Compare
|
Thanks for pointing at this, but please next time send an email to: taylor@laravel.com directly for all security vulnerabilities as described in the readme.md |
|
@jmarcher This vulnerability has been disclosed and discussed privately a while ago. Taylor decided not to fix it, so it's public now: https://murze.be/an-important-security-release-for-laravel-query-builder |
| */ | ||
| protected function wrapJsonPath($value, $delimiter = '->') | ||
| { | ||
| $value = preg_replace("/([\\\\]+)?\\'/", "\\'", $value); |
There was a problem hiding this comment.
With preg_replace("/(\\\\)*'/", ...), the tests still pass. Is there a difference?
There was a problem hiding this comment.
You sure? Because it won't match \', this would cause only the ' to be escaped. Resulting in \\' as the end result, which in turn allows for the attack.
There was a problem hiding this comment.
Don't the tests check this?
There was a problem hiding this comment.
There was a problem hiding this comment.
Same thoughts as staudenmeir, preg_replace("/\\\\*'/", ...) appears to be a strictly equivalent code. But simpler thus less error-prone.
I guess you got a bit confused between PHP and regex escaping ;)
There was a problem hiding this comment.
Describing the code:
- replace: quote preceded by 0 or more backslashes
- with: quote preceded by one backslash
|
Note that you should never allow users to control the columns of your query without a white list. PDO does not allow binding column names as parameters and thus we can't offer much real protection there. |
|
Even with this fix, I DO NOT encourage anyone to allow users to dictate the columns of their query without a white list. |
|
@taylorotwell I agree that these kinds of scenarios should be avoided at all costs. Have you considered adding a general warning to the docs? Doctrine, for example, states the following:
— https://www.doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/security.html The Laravel documentation states the follwing:
— https://laravel.com/docs/master/queries#introduction This statement can be confusing for beginners and even seasoned developers (our query builder package is a good example of that). I think it would be good to add a clear warning about the use of user input as column names. |
|
Since this is considered a security fix, will it be backported to 5.7 and 5.5? |
Looks like 'wrapJsonPath' was only introduced in 5.6 |
|
@GavG The |
* [13.x] Escape single quotes in Postgres JSON path attributes `PostgresGrammar::wrapJsonPathAttributes()` interpolates JSON path attributes into single quoted SQL string literals without escaping single quotes, so a quote in a JSON path terminates the literal early. The equivalent path is already escaped for every other driver by `CompilesJsonPaths::wrapJsonPath()` (#28160), and within this same grammar by `compileJsonContainsKey()`, which uses `str_replace("'", "''", ...)`. `compileJsonUpdateColumn()` delimits path attributes with double quotes but still nests the result inside a `'{...}'` literal, so single quotes are escaped regardless of the configured delimiter. * Update PostgresGrammar.php --------- Co-authored-by: Taylor Otwell <taylor@laravel.com>
There's a potential SQL injection vulnerability with the JSON query syntax. This PR fixes that.
Laravel will parse JSON paths to
json_extractfunctions. Say you've got the following:This will be parsed to:
The actual parsing is done in
\Illuminate\Database\Query\Grammars\Grammar::wrapJsonFieldAndPath()It is however possible to provide a single quote as the "field" value, which will close the
json_extractearly. This gives an attacker the possibility to insert his own malicious SQL code. Take for example this input (indented for clarity):By manually inserting
'afterlang->**", we're able to break out of thejson_extractfunction and inject our malicious code.In this example we're joining on the migrations table, but it's possible to join on anything.
In order for this attack to work, two requirements have to be met:
The solution is to escape all single quotes passed as
$valuein\Illuminate\Database\Query\Grammars\Grammar::wrapJsonPath(). Because attackers could potentially chain multiple backslashes, this PR will take all single quotes, with or without preceding backslashes, and replace it with\'.I decided to use a HEREDOC in the tests, for clarity. If this is not ok for Laravel, I'll be happy to change it.