Follow-up to #2388255: (followup) Limit PDO MySQL to executing single statements if PHP supports it
Problem/Motivation
One of the reasons for the severity of https://www.drupal.org/SA-CORE-2014-005 was the fact the PDO MySQL allows multiple statements to be executed.
This affects all database drivers, not just MySQL
Proposed resolution
Check all SQL strings and throw an exception if a delimiter is present. This should not cause any problems if all user data is interpolated via placeholders.
Remaining tasks
User interface changes
n/a
API changes
better DB security
| Comment | File | Size | Author |
|---|---|---|---|
| #77 | 2489672-60_repost.patch | 13.29 KB | effulgentsia |
| #73 | 2489672-60_repost.patch | 13.29 KB | pwolanin |
| #69 | 2489672-60.patch | 13.29 KB | effulgentsia |
| #60 | increment.txt | 3.77 KB | pwolanin |
| #60 | 2489672-60.patch | 13.29 KB | pwolanin |
Comments
Comment #1
pwolanin commentedquick patch, to see if it breaks.
Comment #5
mpdonadioI am going take a stab at tracking down what is going wrong here.
Comment #6
mpdonadioSome CREATE TABLE commands have have semicolons in the COMMENT. Let's see if a full test run reveals any problems.
Comment #8
mpdonadioWuh? My install script and
php core/scripts/run-tests.sh --url http://localhost/drupal-8.0.x --all --verboseis running just fine locally?
Comment #9
pwolanin commentedOh, yeah - I was thinking about semicolons in table comments at one point.
Maybe schema API can replace them when building the SQL?
I also found the sqlite driver was adding
;to schema creation statements. I think those can just be removed.The difference you see with run-tests working locally is probably that the testbot uses the --sqlite option.
The interdiff here is to #1.
Comment #10
pwolanin commentedTo avoid false positive perhaps we should rtirm() whitespace and ; in all queries?
Like:
rtrim($query, "; \t\n\r\0\x0B")?Comment #12
pwolanin commentedFixing up comment code also, and moved the delimiter check to a more central place that also catches most insert/update calls.
Comment #14
pwolanin commentedSimple unit test fix to account for replacing ; in comments.
Comment #15
Crell commentedLOL. That is such a Drupal bug to have. I'm tempted to be a grammar pedant and ask if there's something other than periods that make sense, but...
This.
And this. Seems redundant that we need to do it in both places, no?
Comment #16
mpdonadio#15 2 and 3: one is for the COMMENT in the schema install (ie, the CREATE TABLE) and the other is for the comment() method on the query class.
Comment #17
pwolanin commented@Crell - period seems the least silly option, but we can make them utf-8 emojis for all I care
[edit] but don't post emoji since they break core!
Comment #18
Crell commentedmpdonadio: Ah, OK. Silly DBTNG. Who wrote this thing?
Then I think it's fine. Thanks, pwolanin!
Comment #19
mpdonadioDo we need to request a manual test run against PostgreSQL?
Comment #20
pwolanin commented@mpdonadio - yes, that would be a good idea
Comment #21
Crell commentedBoth PostgreSQL and SQLite. Is there a doc page somewhere that explains how we do that now? (It's kinda sad that I don't know that, being DB maintainer and all...)
Comment #22
alexpottThis looks weird - should we be testing that the exploit protection works here instead?
Running the
Databasegroup of test. SQLite is fine with this patch... Postgres is not.Before patch
After patch
To run tests with postgres just pass run-tests.sh a valid postgres database connection url.
Isn't this change adding extra code for no reason now we've gone for #2388255: (followup) Limit PDO MySQL to executing single statements if PHP supports it and doesn't Postgres have something similar to protect a user from running mulitple sql statements?
Comment #23
Crell commentedThe PDO single-statement flag was only added in a super-recent version of PHP, so there are still a lot of users that won't have that check. This is an extra check for users that don't have that flag. I cannot speak to Postgres', as I've never used it for more than 10 minutes.
Comment #24
bzrudi71 commentedAdding #2509296: PostgreSQL: Re-think current PDO::ATTR_EMULATE_PREPARES setting as related.
Comment #25
pwolanin commentedI don't think Postgres has any flag to limit to single statements, so the idea here is to protect Postgres and any other SQL back-end from typical SQL injection attacks (basically adding a 2nd statement onto a select)
Comment #26
bzrudi71 commented@pwolanin, PostgreSQL PDO limits to single statements by default, but we explicit allow multiple statements by enabling emulated prepares ;)
Comment #27
pwolanin commentedright - so it's not clear that for a performance we can make postgres or mysql use native prepares (an extra round trip to the server for every query - which is a lot if you DB server is over a network connection with even just 1-2 ms latency).
Ideally we can backport this change to 7 also since we won't ever switch to native prepares there.
Comment #28
pwolanin commenteddo we have a postgres testbot now?
Comment #29
bzrudi71 commented@pwolanin yes, you have to re-upload your patch again to enable PG testing. Unfortunately there are still many random exceptions with PG, all related to some kind of already installed exceptions that we need to fix first before getting meaningfully results ;)
Comment #30
pwolanin commentedre-uplading and setting to NR to see if I can get the other tests to run
Comment #31
pwolanin commentedre-uploading - looks like sqlite errors mostly went away
Comment #32
pwolanin commentedRe-roll against HEAD
Comment #33
pwolanin commentedtrying again for a multi-SQL test run
Comment #34
pwolanin commentedHere's a re-roll for conflicts.
Comment #35
pwolanin commentedHere's a dumb fix for postgres fails during setup.
Needs to be cleaner, but at least we can see if the basic approach is enough.
Comment #38
pwolanin commentedok, talked to dawehner about this and he had a better suggestion of putting a flag in the query options.
Also fixes some tests of query comments where I didn't make the right changes in the last patch.
Comment #39
dawehnerIs there any particular reason why we don't use str_replace?
Comment #40
pwolanin commented@dawehner - in some benchmarks strtr is faster, and I think it's also easier to understand since it's what we used inside, t(), etc.
The pgsql results look like less fails than HEAD, strangely. At the least, not causing the total failure we had earlier.
Comment #41
Crell commentedThe flag makes sense here, I think. Nice work. A minor nit, but otherwise I'm happy with the latest patch. (I don't know what our postgres policy is this week on whether this is RTBCable or not.)
New-style arrays, please.
Thank you! :-)
Comment #42
pwolanin commentedfiasco looked at the patch, but I'll ping him to leave a comment also.
Comment #43
gregglesI picked some nits.
The word "This" feels ambiguous. How about "Semicolons are needed..."
Missing a closing period on the second sentence.
This sentence feels incomplete and disjointed from the previous paragraph.
Comment #44
pwolanin commentedI'm not able to reproduce some of the postgres fails locally using vagrant + PHP 5.6.12 + pgsql 9.3
e.g. these 3 pass for me:
Drupal\aggregator\Tests\AggregatorAdminTest
Drupal\system\Tests\Theme\EntityFilteringThemeTest
Drupal\system\Tests\System\FloodTest
Patch fixes code comments up for nits.
Comment #45
Crell commentedPer #42 this is probably as much of a Postgres review as we're going to get, so let's move on.
Comment #47
bzrudi71 commented@crell since we have a green testbot (since some hours) let's do another retest to make sure everything works as expected before setting to RTBC.
Comment #48
bzrudi71 commentedComment #49
nikolay shapovalov commentedComment #52
nikolay shapovalov commentedRerolled.
Comment #53
nikolay shapovalov commentedComment #54
pwolanin commentedSo, looks like there are new or more postgres fails to fix.
Comment #55
pwolanin commentedre-posting the last patch to check currrent pgsql fails.
Comment #56
pwolanin commentedComment #57
bzrudi71 commentedLooking at the fails I have to wonder. They are about creating the GREATEST and CONCAT functions, but we removed them (because obsolete in PostgreSQL > 9.1) in #2552769: Remove outdated custom functions for greatest() and concat() ;)
Comment #58
bzrudi71 commentedAdding #2561121: Installer fails on postgresql due to uncaught exception as related...
Comment #59
tstoecklerThis seems to revert #2552769: Remove outdated custom functions for greatest() and concat(), probably a merge error or something. Marking needs work for that.
Should we document *why* this is the case or is that obvious? (I'm genuinely asking, I thought about this myself and am not sure.)
Comment #60
pwolanin commentedOk, taking those out, let's see if the tests are better.
Comment #63
pwolanin commentedok, I think those were sporadic fails.
Comment #64
fabianx commentedRTBC, looks great to me!
Comment #65
alexpottFixing title
Comment #68
pwolanin commentedsporadic fail apparently.
Comment #69
effulgentsia commentedI'm having a hard time interpreting the conflicting DrupalCI results in #60, so reuploading that patch to get fresh, uncluttered, results.
Comment #70
effulgentsia commentedIs that SQLite failure expected? I notice that #60 didn't have any SQLite test running?
Comment #71
effulgentsia commentedPer #70.
Comment #72
pwolanin commentedThe failure is
Value 'Basic page' is equal to value 'Article'If it's not something sporadic, then it must be a HEAD regression. It can't be related to this patch, which would cause failures by throwing exceptions.
Let me re-upload the patch to get fresh tests.
Comment #73
pwolanin commentedComment #74
pwolanin commentedThe node test fail from above is already in 8.0.x https://www.drupal.org/pift-ci-job/37937
Comment #76
pwolanin commentedone of the test fails in in 8.0.x: #2573289: Core NodeAdminTest test failing on sqlite due to unspecified node ordering
I don't see the other test fail locally. Putting back to RTBC since I think this was just something with the bot
Comment #77
effulgentsia commentedI committed #2573289: Core NodeAdminTest test failing on sqlite due to unspecified node ordering so let's try this again.
Comment #78
effulgentsia commentedAdding credit to @bzrudi71 and @Crell for multiple reviews and investigations into related issues.
Comment #79
effulgentsia commentedRemoving credit from myself. I only re-uploaded existing patches, didn't contribute anything significant of my own.
Comment #81
effulgentsia commentedI like how this patch hardens Drupal's Database API for users without a database engine/configuration that does it at that layer. Also, this was RTBC for over a week between #68 and #69 without anyone raising a concern with doing this. I also reviewed the patch myself and no concerns jumped out at me. Therefore, committed and pushed to 8.0.x.
I think we could use a change record for module developers who create SQL functions, like the below, so "needs work" for that.
Comment #82
pwolanin commentedadded draft change record
Comment #83
larowlanChange record looks good to me - looking forward to this in core
Comment #84
effulgentsia commentedThanks. I published the CR, so moving this to the 7.x queue.
Comment #87
stefan.r commentedJust bumping this as this would be good to get into 7.x
Comment #90
fabianx commentedComment #93
mlncn commentedRegarding Drupal 8. Warning, this blows up a statement like:
NOTICE: PHP message: Uncaught PHP Exception PDOException: "SQLSTATE[HY093]: Invalid parameter number: no parameters were bound" at /var/www/html/web/core/lib/Drupal/Core/Database/Statement.php
And while that's easy enough to work around by adding placeholders even though the offending query parameters aren't based on user input:
If you're trying to do an UPDATE query with a join i think you're straight out of luck.
Comment #95
quietone commentedThis was committed.
Comment #97
xjmRestoring credits.