Skip to content

'FOR UPDATE' check in is_write_query() #112

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

Open
wants to merge 10 commits into
base: trunk
Choose a base branch
from

Conversation

justinmaurerdotdev
Copy link

When using Wordfence's 2FA implementation together with HyperDB, the SELECT ... FOR UPDATE queries are currently getting interpreted as "not a write query", which causes user authentication to fail on read-only database replicas. My understanding is that SELECT ... FOR UPDATE is a write lock solution, so it requires a write-able instance.

Here's Wordfence's code, for reference, from wordfence/modules/login-security/classes/controller/totp.php:54:

$record = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$table}` WHERE `user_id` = %d FOR UPDATE", $user->ID), ARRAY_A);

I assume Wordfence isn't the only plugin to use write locks like this, so it seems like this "quick and dirty" is_write_query() is due for an upgrade. This fix appears to be working for me in production.

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

@titustangible
Copy link

This causes coupon usage count queries to fail in Woocommerce as well

hj-collab added a commit to hj-collab/HyperDB that referenced this pull request May 31, 2023
@hj-collab hj-collab mentioned this pull request May 31, 2023
@dd32
Copy link
Member

dd32 commented Jun 12, 2023

One downside of this PR is that SELECT * FROM table WHERE field LIKE '%example FOR UPDATE%' will be detected as a write query, which is an issue for WordPress search queries.

EDIT: After commenting, I realise HyperDB does support Transactions, but requires a specific comment to specify the data-set, if that comment is specified, although is_write_query() will return false, it should hit the write-server anyway: https://github.com/Automattic/HyperDB/blob/trunk/db.php#L325-L330
HyperDB doesn't really seem like it supports transactions (which SELECT FOR UPDATE is used within) as although BEGIN and START TRANSACTION are detected as write queries (since they don't look like a read-query) they'd go to the default HyperDB write server or fail as no table is specified as part of them. Once it's in a transaction, all future queries to that table should go to the write server.

Potentially what is needed here instead, is detecting a transaction starting and then treating all future queries until transaction end as a write query, even if it's a SELECT (such as in the case of what happens if you perform a write, and then start reading from the table)

Copy link

@Cadyshack Cadyshack left a comment

Choose a reason for hiding this comment

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

I approve this pull request. It fixes an important issue. Tested this solution in my own project and fixed the problem caused by SELECT FOR UPDATE queries.

Apparently my approval means nothing, but if someone with proper permission could approve this, that would be great. HyperDB 1.9 is unusable without this change in my case, and many others.

Copy link

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
No data about Duplication

See analysis details on SonarCloud

@justinmaurerdotdev
Copy link
Author

Circling back to this. It's still an issue in latest code, so I've updated the branch to make it merge-able again.

JJJ added a commit to stuttter/ludicrousdb that referenced this pull request Jun 18, 2024
This change adds `FOR\s+UPDATE` to the regular expression check that is used to determine if a query is trying to write to a database.

This better supports queries from other plugins (like Woo or Wordfence) that rely on this MySQL feature.

See also: Automattic/HyperDB#112 (comment)

Fixes #127.
Copy link

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.

4 participants