Completes the set of queries that matched a freshly encrypted value against a
stored one, which only works while encryption is deterministic. Each keeps its
original comparison and adds an index comparison in the same WHERE, so nothing
changes today.
- passenger sign-in by email, service-staff sign-in, Firebase token lookup
- driver lookup by phone and by national number
- admin ride lookup and ride monitor (both tables)
- nabeh: driver status, user resolution, ride history, complaint submission
transit_org_admins lives in the transit database and has no index column, so
login there falls back to decrypting the small set of active admins and
comparing normalised numbers.
Schema: adds users.email_bidx/phone_bidx and driver.national_bidx with their
indexes.
Verified that every :*_bidx placeholder introduced is actually bound — an
unbound one is a fatal error at request time, not a silent miss.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
These are the paths that must stop depending on deterministic encryption
before storage can move to AES-GCM. Each keeps its original ciphertext
comparison in the same statement, so behaviour is unchanged today and no
account becomes unreachable during the transition.
Lookups:
- auth/login.php — passenger sign-in matched the raw value against the
encrypted column, which only works because encryptData() is CBC with a
fixed IV.
- auth/passenger/register.php and auth/driver/register.php — duplicate
detection. Without the index these would stop detecting existing accounts
under GCM and allow the same phone to register twice.
Writes now populate the index in the same statement as the value:
- both registration paths write phone/email/name indexes with the row;
driver indexes are computed before the encryption pass, since the raw
values are unavailable afterwards.
- passenger profile update and admin driver update refresh the index when
the underlying field changes. For the composite name index the untouched
half is read back from the row.
Adds --audit to the backfill script: recomputes every index from its
encrypted value and reports missing or stale entries. Drift here is silent
by nature — it surfaces only when a real search fails.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hotfix: a comment added to the dashboard SQL contained double quotes inside
the double-quoted PHP string, terminating it and making dashbord.php fail to
parse. Production was returning a parse error for every dashboard request.
Authorisation gaps closed — connect.php only proves a token is valid, it does
not check what the caller is allowed to do:
- Admin/ggg.php decrypts any database field and was authorised solely by an
admin phone number sent in the request body. Anyone who knew a listed
number could decrypt platform data without signing in. It now runs behind
connect.php, requires super_admin, keeps the phone list as a second factor,
and records every use.
- ride/kazan/update.php, kazan/add.php and ride/promo/{add,update,delete}.php
changed live pricing and discount codes with no role check at all, so any
valid token — including a driver's or passenger's — could rewrite the fare
table. All now require super_admin.
Staff/pending.php: adminUser has no `status` column in this deployment, so
the query failed with an opaque "unavailable". It now checks for the column
and reports the actual reason.
Console: Kazan tariff editor for super admins — sends only changed fields,
shows an old → new confirmation before saving, and stays read-only with an
explanatory notice for plain admins.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>