Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Wn Pwnedpasswords Plugin Laravel Package

winter/wn-pwnedpasswords-plugin

Adds a Pwned Passwords validation rule to Winter CMS, checking passwords against the Have I Been Pwned breach database using k-anonymity so compromised credentials are rejected during registration and password resets.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. Winter CMS is a Laravel-based fork of October CMS; plugins are Composer packages that register a Plugin class and hook the framework boot lifecycle. A password-validation rule is a low-blast-radius extension point: additive, stateless, no schema change, no interception of the auth pipeline. The package's scope — one validation rule delegating to the HIBP Pwned Passwords range API — is deliberately narrow, which is the correct shape for a CMS plugin. The architectural question is not whether it fits, but whether the fit justifies a dependency at all.

The rule is roughly 60 lines of code. SHA-1 the candidate password, send the first five hex characters to api.pwnedpasswords.com/range/{prefix}, scan the returned suffix list for the remainder, compare the count against a threshold. That is the entire mechanism. A TPM should price the dependency against owning that function in-house, because build cost is hours and ownership cost is effectively zero.

Integration feasibility. High on the happy path. Install via Composer, run php artisan winter:up, enable in the backend. Enforcement points are limited and well-known: front-end registration and password reset (typically RainLab.User), backend user creation, and any custom model with a $rules array. Winter's Validator::extend() mechanism lets the rule attach alongside confirmed and min without modifying existing validation flows.

Technical risks.

  • Synchronous third-party call in the write path. Every password set now blocks on a network round trip. p95 of 200–400ms is typical; a degraded HIBP adds that directly to registration and password-reset endpoints.
  • Fail-open vs fail-closed is a policy decision the plugin does not make for you. This is the single most important choice and must not be left to the library default. Failing closed converts an HIBP outage into a total password-change outage and an attacker-controllable DoS on account recovery.
  • No retroactive enforcement is possible. Password storage is one-way; existing users can only be checked at their next password change. Any stakeholder expectation of "audit the user base" is unmet by design, not by omission.
  • Privacy/DPIA surface. k-anonymity means only a 5-character SHA-1 prefix leaves the perimeter, which is genuinely defensible — but it is still per-password-change egress to a third party. Confirm that no full hash, plaintext, or user identifier is logged or attached to the request.
  • Rate limits and burst behaviour. HIBP throttles. Bulk operations — data imports, seeders, CI fixtures, forced resets across a user base — generate bursts the plugin almost certainly does not queue or cache.
  • Maturity. 5 stars, 0 dependents, single maintainer. There is no downstream signal that this code has run in production at scale, and no security review. The low opportunity score (12.8) reinforces that this is niche, not strategic.
  • Version drift. Winter's Laravel baseline has moved; Composer constraints and the PHP floor must be verified against the target install rather than assumed from the README.

Key questions before adoption.

  1. What HTTP client is used — Laravel's Http facade/Guzzle, or raw file_get_contents? This determines testability, timeout control, and retry behaviour.
  2. Are timeouts, retries, and circuit-breaking configurable, and what is the documented default on failure?
  3. Is response caching implemented per prefix, or is every evaluation a live call?
  4. Does the rule accept a breach-count threshold parameter, or is any appearance in the corpus an automatic rejection?
  5. What are the exact Winter CMS and PHP version constraints, and is the rule registered defensively against name collisions with existing custom validators?

Integration Approach

Stack fit. Good, with the caveat that this is Winter-specific glue around a framework-agnostic idea. On Winter CMS with a Laravel-native stack, no translation layer is required. If the organisation is on October CMS upstream, plain Laravel, or a non-PHP platform, this package is the wrong artifact — the check belongs in a shared validation service or a Password::validate()-style rule in the application layer, not in a CMS plugin.

Migration path. There is no data migration; the rule is stateless and reversible by removing the validation string. The real migration is policy: from "accept any string meeting length and complexity" to "accept any string not present in a breach corpus." Sequence it:

  1. Install and observe. Register the rule in log-only mode on password-set endpoints. Emit metrics on evaluation count, hit rate, latency, and error rate. No user-visible change.
  2. Measure the blast radius. Hit rate tells you how many real users would be blocked at next password change. For a typical consumer base this is often 10–30%, which is a UX and support-load decision before it is a technical one.
  3. Enforce on new and changed passwords only. Explicitly scope out existing credentials and communicate that no retroactive check is possible.
  4. Harden. Add prefix-level caching, an explicit request timeout, and a documented fail-open policy with alerting.
  5. Reassess at volume. If password-change volume or an air-gapped/regulated requirement emerges, replace the API call with the downloadable HIBP hash corpus served internally.

Compatibility. Verify against the actual Winter build in use, not the README: Composer constraints, PHP floor, and whether the target install already extends Validator with a rule of the same name. Critically, confirm that RainLab.User (or whichever auth plugin is in place) routes password validation through a path the rule can attach to. If password setting happens in a custom controller that bypasses model validation, the rule silently never fires — the worst failure mode, because it looks like success.

Sequencing relative to other work. This is small and isolated and should neither block nor be blocked by other roadmap items. It does have a hard dependency on two non-engineering tasks that usually dominate the timeline: privacy review sign-off for the third-party egress, and a product decision on rejection threshold and user-facing copy. Start both before engineering, because a two-hour implementation behind a three-week legal review is a scheduling trap.

Operational Impact

Maintenance and ownership. The dependency surface is tiny, which caps both the upside of adopting it and the downside of forking it. Given 5 stars and 0 dependents, the realistic bus-factor plan is: pin the version, vendor the plugin into the repository, and be prepared to take ownership of ~60 lines. Do not assume upstream responsiveness for a security-adjacent fix. The package has no schema, no background jobs, and no admin UI, so routine maintenance is near-zero — the operational cost lives entirely in the external dependency it wraps.

Support load. Expect a spike at enforcement time. Users whose long-standing password is rejected during an unrelated flow (password reset, profile edit) will read it as a bug or a lockout. Pre-write the error message to explain why — found in a breach corpus, not "too weak" — and offer a path forward. Without that, every rejection becomes a ticket.

Scaling characteristics. Volume is bounded by password-set events, not logins or page views, so throughput is genuinely low; this is not a scaling problem in the ordinary sense. The two real concerns are: (a) added latency on a user-facing write path, mitigated by a short timeout and, if needed, a prefix cache; and (b) bursts during bulk resets, imports, or test seeding, which should bypass the rule or be queued rather than hammering HIBP. If the organisation ever needs thousands of evaluations per minute, the API is the wrong substrate and the offline corpus is the answer.

Failure modes and required mitigations.

  • HIBP unavailable or timing out → degrade to fail-open with an alert; never fail-closed. Failing closed converts a third-party incident into an authentication incident.
  • HTTP 429 rate limiting → treat as unavailable, not as rejection. A 429 misread as "password is pwned" blocks every user with a confusing message.
  • Malformed or truncated response → same as unavailable; parse defensively and log the response shape, never the password.
  • Rule never attached / misconfigured → the silent failure. Detect it with a startup assertion or a synthetic check that a known-breached password (e.g. Password123) is actually rejected in a smoke test.
  • Egress blocked by network policy → the rule appears to work (fail-open) while providing zero protection. Alert on evaluation error rate as a first-class metric, not just latency.
  • Log leakage → confirm the plaintext password and full SHA-1 never reach application logs or error trackers, particularly in exception handlers.

Observability. Instrument the rule as a discrete unit: evaluation count, hit rate, p50/p95 latency, error and timeout rate, and fail-open count. The fail-open counter is the one that matters — it silently tells you the gap between the protection you believe you have and the protection you actually have.

Ramp-up. For a Laravel developer, an afternoon including the smoke tests above. The long poles are the privacy review and the product decision on messaging and thresholds. Given the low overall score (16.8) and low opportunity signal (12.8), the honest prioritisation framing is: this is a cheap, credible security-hygiene improvement with a real but modest UX cost — worth doing once, if and only if the fail-open policy and observability are in place before enforcement is switched on.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel