Find Users with Weak Password Hashes — and Build Your Own Data Filters — with the OXID Consistency Check Tool

Export by Filter — a generic CSV export with a tag-based extension point. Introduced in v3.0.0.
Building on the image cleanup and SEO URL features shipped in earlier releases, v3.0.0 adds Export by Filter to the OXID Data Consistency Tool. Export by Filter is a generic way to export any subset of shop data to CSV — and a clean extension point so you (or your integrators) can plug in your own filters without forking the component.
The release also ships the first built-in filter: deprecated-credentials, which surfaces every user whose password hash is not native $2y$ Bcrypt.
Why Export by Filter Matters
Most consistency problems in an eShop database don't have a one-size-fits-all command. The questions teams actually ask look like this:
Every one of those is "find rows matching a rule, export them to CSV, hand them to someone." Until now, each of those checks would have meant either a one-off SQL query against production or a custom console command per question.
Export by Filter turns that pattern into a first-class extension point: write a small class describing what to export, tag it as a filter, and the tool handles command wiring, CSV generation, timestamped filenames, and logging.
A Note on the Export Directory
Required before running in production. By default, app.export_directory_path is export, which puts CSVs at <shop>/source/export/<filename>.csv — inside the web document root. The shop's global source/.htaccess does not deny .csv, so files there are reachable via https://<your-shop>/export/<filename>.csv. The consistency-check CSVs are privacy-minimal, but they still list accounts plus activity dates.
Before running export commands in production, either:
The README shows both options in full.
A Closer Look: the export-by-filter Command
Version 3.0.0 introduces a single new command, oe:consistency_check:export-by-filter, which dispatches to whichever filter you name:
vendor/bin/oe-console oe:consistency_check:export-by-filter --filter-name=<filter>
Output lands in export/<filter-name>-<timestamp>.csv — for example export/deprecated-credentials-2026-06-03_14-30-15.csv (the directory is configurable — see the security note above).
The First Built-in Filter: deprecated-credentials
OXID shops accumulate password hashes from many sources over their lifetime — SHA512 from older shop versions, MD5 from very old or hand-imported users, Bcrypt variants from non-PHP backends, and occasionally custom plugins. From a security standpoint these are not equivalent, and the first step to fixing them is knowing where they are.
deprecated-credentials exports every user whose password hash is not native $2y$ Bcrypt and is not empty:
vendor/bin/oe-console oe:consistency_check:export-by-filter --filter-name=deprecated-credentials
The CSV includes:
credential_status values
$2a$ / $2b$ — externally produced (Java BCrypt, OpenBSD, modern non-PHP libraries, manual imports). Cryptographically equivalent to $2y$. No action needed — informational only.Note: $2a$/$2b$ rows are exported deliberately so the CSV is a complete audit of non-$2y$ hashes; filter them out client-side if you only want actionable rows.
Privacy by Design
The CSV contains no direct PII — no email, no name, no address — only the user identifier plus the activity metadata needed to prioritise each row (account state, creation date, last modification, last order date, and the hash classification). That's deliberate: the file can be shared, archived, or attached to a ticket without leaking PII. To contact the affected users, look up OXUSERNAME from oxuser separately via the user_id column.
Subshop Scope
In CE/PE (single-shop) installations the export is naturally shop-local; the rest of this section describes the Enterprise Edition behaviour, where subshops share the user table. The export covers users from every shop in an Enterprise installation, with no shop-id column. For blMallUsers = true setups this matches the auth model: a single user record authenticates against any shop in the mall, so a weak hash is a mall-installation-wide concern. For blMallUsers = false setups the user → shop assignment is local; partners running single-shop or strictly separated mall configurations may want a per-shop slice — easy to do with a custom filter (see below).
Extending: Build Your Own Filter
This is the core of the release. Every shop has its own definition of "the rows I need to see." Rather than baking those definitions into the tool, 3.0.0 ships a tag-based extension point so any component — yours, an integrator's, or a one-off project package — can register a filter and immediately get a working export-by-filter --filter-name=... invocation.
Step 1: Implement FilterInterface
Create a class in your component that implements OxidEsales\ConsistencyCheck\ExportByFilter\Filter\FilterInterface. The interface defines four methods:
The interface lives at src/ExportByFilter/Filter/FilterInterface.php — read it once and you've seen the whole contract.
Step 2: Register the Filter Service
Add your filter to your component's services.yaml with the oe.consistency_check.export_filter tag:
services:
YourVendor\YourComponent\Filter\InactiveCustomersFilter:
autowire: true
tags: ['oe.consistency_check.export_filter']
The tag is the entire registration — no central registry to edit, no command to subclass.
Step 3: Run It
vendor/bin/oe-console oe:consistency_check:export-by-filter --filter-name=inactive-customers
For a complete worked example, the DeprecatedCredentialsFilter class in src/ExportByFilter/Filter/Credentials/ is intentionally written as a reference implementation — same shape as anything you'd write yourself.
Filter Ideas
A few directions where a custom filter pays for itself almost immediately:
None of these need a new command. They're all "implement FilterInterface, tag the service, done."
Compatibility and Requirements
Installation
To get started, install or update the tool via Composer:
composer require oxid-esales/consistency-check-tool
Repository: github.com/OXID-eSales/consistency-check-tool
Looking Ahead
The OXID Data Consistency Tool continues to evolve. Image cleanup gave us a safe pattern for modifying shop data. SEO URL management generalized that into a check-export-review-delete workflow. Export by Filter takes the next step: instead of shipping one command per question, it gives shops and integrators a stable extension point so the next consistency check can live in a component — not in a fork.
Other features in the tool — image cleanup, SEO URL management — follow the same pattern: dry-run, CSV-based, privacy-minimal.
Feedback
Open an issue if a filter you'd like to see in-box is missing, or contribute one through a PR.
GitHub Repository: github.com/OXID-eSales/consistency-check-tool
