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

Password Exposed Laravel Package

jord-jd/password_exposed

Check if a password has been exposed in a data breach using the haveibeenpwned.com API. Provides a simple password_exposed helper and a PasswordExposedChecker class, returning EXPOSED, NOT_EXPOSED, or UNKNOWN, with support for SHA1 hashes and PHP 7.1+.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

This is a thin, framework-agnostic Composer library — one helper function, one PasswordExposedChecker class, and a PasswordStatus enum (EXPOSED / NOT_EXPOSED / UNKNOWN). It has no Laravel service provider, no facade, no config file, and no published migrations. Architecturally that is fine for a Laravel app because the integration surface is a single synchronous call you can wrap in your own service class or validation rule; it is not a package that will auto-wire itself into the framework. Treat it as a library you call, not a package you install and configure.

The real substance is the HaveIBeenPwned Pwned Passwords v2 range API: SHA-1 the password, send the first five hex characters, match the suffix locally. That is roughly 40 lines of code plus caching. The package adds PSR-6 caching (hash-prefix keyed), a PSR-18 HTTP client, and TLS certificate validation via paragonie/certainty. So the evaluation question is honestly "adopt a maintained wrapper vs. write the same 40 lines in-house," not "adopt a security product."

Integration feasibility

Feasible, with two non-obvious friction points that a spike will surface immediately:

  1. Cache binding. The package wants a PSR-6 CacheItemPoolInterface. Laravel's cache is not PSR-6 (its repository is PSR-16-shaped in recent versions). You either bridge via a Symfony PSR-6 adapter or let the package fall back to its own file cache — which writes into a runtime directory and has a documented history of rename() contention and read-only-filesystem issues. In a containerized/ephemeral deploy, that fallback is the likeliest thing to break.
  2. HTTP client. It needs a PSR-18 client; Laravel's Illuminate\Http\Client is not PSR-18. You will end up pulling guzzlehttp/guzzle plus the package's adapter, and that adapter has version-sensitive Guzzle 6/7 behavior (v5.1.1 exists specifically to fix exception semantics there).

Neither is a blocker, but "just composer require it" is not the accurate story. Budget a half-day spike.

Technical risk

  • Third-party availability in the request path. Registration and password-reset become dependent on an external HTTPS call. UNKNOWN is returned on API error/timeout, so you cannot lose data — but you can lose onboarding, and the default Certainty HTTP timeout was historically 5 seconds. A 5s tail on a signup request is a product incident, not just an error log.
  • Dependency churn. The release history is a long trail of constraint relaxations (psr/cache v1/v2/v3, psr/http-message v1/v2, Symfony cache adapter v3 vs v4 conditional on PHP version, Buzz 1.4 for Symfony 8). Each is a maintenance signal, but collectively they mean a composer update on the wider app can move this package under you. Pin it.
  • Bus factor of one. 223 stars, 0 dependents, single maintainer, LGPL-3.0. Small enough to fork or reimplement in an afternoon, so the abandonment risk is bounded — but do not plan a roadmap around upstream responsiveness.
  • License. LGPL-3.0 is usually workable for Composer-linked PHP, but it will trigger a legal review in many enterprises. Get that ruling early rather than at release.
  • Privacy is the strong point. No plaintext password leaves your network; only a 5-character hash prefix does. That argument shortens data-protection review and is the single best reason to prefer this shape over any alternative that posts credentials.

Key questions

  • Does the team accept a synchronous outbound dependency on signup, or does this need to be queued/async?
  • What is the fail-open vs. fail-closed policy on UNKNOWN? (And is that policy the same for registration, reset, and any admin-driven password set?)
  • Is LGPL-3.0 pre-approved, or does it need a legal exception?
  • Can we bind a real PSR-6 pool from our existing cache layer, or are we shipping a file-cache sidecar into a read-only container?
  • Do we hard-block EXPOSED or warn-and-allow? This is a product decision the package forces, not one it makes.

Integration Approach

Stack fit

Good but not native. It sits cleanly alongside PHP 7.1–8.5 and Laravel 8+, and the SHA-1-hash entry point (passwordExposedByHash) lets you keep the plaintext confined to the validation layer. The lack of a Laravel service provider means the idiomatic integration is: a small application-owned service class wrapping PasswordExposedChecker, a PasswordNotBreached validation rule, and a config entry for the warn/block policy. Roughly 100 lines of glue. This is the right place to put the swap point so the vendor library is replaceable.

Migration path

Nothing to migrate. There is no schema, no persisted state, no data model — the only state is a cache keyed on hash prefixes, which is disposable. Rollout is additive: ship the validation rule behind a config flag, enable on registration first, observe the UNKNOWN rate, then enable on password reset.

Compatibility

Watch three specific things: (a) the PSR-6 cache adapter version, which the package resolves conditionally based on PHP version — verify it agrees with whatever symfony/cache your app already locks; (b) the PSR-18/Guzzle pair, which must be compatible with the Guzzle version already in the lockfile; (c) paragonie/certainty, which fetches and writes a certificate bundle and warns loudly on 32-bit systems without the sodium extension. Pin the package to a minor version and treat upgrades as a reviewed change, given the constraint churn in the changelog.

Sequencing

  1. Spike (0.5 day). Composer install in a branch, prove the PSR-6 and PSR-18 bindings resolve against the real lockfile, and measure p50/p95 latency uncached and cached in the target environment.
  2. Policy decision. Write down the EXPOSED and UNKNOWN behavior for each surface. Fail-closed on UNKNOWN means an HIBP outage blocks new signups — make that a signed-off decision, not a default.
  3. Registration only. Ship the rule with a warn-and-allow default, log outcomes, and instrument the UNKNOWN ratio.
  4. Password reset/change. Same rule, lower risk, second exposure.
  5. Retroactive enforcement — separate initiative. There is no batch API. Scanning an existing user table means looping per password, and forcing resets on existing users is a different and much more disruptive product. Do not bundle it.
  6. Position it honestly. This is a cheap, additive control that ships in days. It is not a substitute for MFA, login rate limiting, or credential-stuffing detection — those close the account-takeover risk; this improves password hygiene at the boundary.

Operational Impact

Maintenance

Low code footprint, non-trivial dependency footprint. The package itself is a few hundred lines; the operational surface is Composer constraints, a PSR-6 pool binding, a PSR-18/Guzzle client, and a certificate-bundle writer. Plan on Dependabot-driven PRs landing against it periodically and requiring review, because upstream actively relaxes constraints. Because the wrapper is application-owned, an abandonment scenario costs an afternoon to replace.

Support

Community-only. GitHub issues, no SLA, no named maintainer commitment, no vendor contract. That is acceptable for a non-differentiating utility and unacceptable if anyone tries to represent this as a compliance control. Document internally that the underlying dependency is the free HaveIBeenPwned API — no key, no commercial agreement, no uptime guarantee.

Scaling

Favorable. Cache keys are 5-character hash prefixes; the keyspace is ~1M, so cache hit rate improves as your user base grows, and steady-state outbound traffic to HIBP is small and well within the free API's tolerance. Payloads are a few KB. The scaling constraint is not throughput — it is latency on the request path and the blast radius of the external call during traffic spikes, when timeouts will cluster.

Failure modes

  • HIBP 5xx / timeout → UNKNOWN. Handled gracefully by design, but surfaces your fail-open/fail-closed policy at the worst possible time.
  • Certainty certificate-bundle write failure. Read-only filesystem, non-writable vendor/, or concurrent container startup. The 5.0.1 changelog explicitly patches rename() contention with a file lock — evidence the failure is real.
  • No PSR-6 binding → implicit file cache. Manifests as permission errors or silent cache misses; easy to miss in staging where volume is low.
  • Composer resolution drift. psr/cache, psr/http-message, or symfony/cache version skew breaking install or runtime after an unrelated dependency bump.
  • Response parser edge cases. Blank/whitespace lines in the API response previously threw (fixed v5.0.1) — a reminder the response parsing is thin and should be covered by an application-level smoke test with a mocked client.
  • Vendor abandonment. Bounded, as above.

Ramp-up

Very low. Any Laravel developer can integrate this in under a day. The non-obvious work is the spike (proving cache and HTTP client bindings resolve in your actual lockfile and runtime) and the policy sign-off, not the code. The two artifacts worth producing are the wrapper service class — which is your fork/swap point — and a monitoring dashboard tracking call volume, cache hit rate, and the UNKNOWN ratio, with an alert on the last one so an external outage shows up as a known, explained condition rather than a mystery spike in failed signups.

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.
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode