PHPackages                             tuzelko/yii2-pwned-passwords - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. tuzelko/yii2-pwned-passwords

ActiveYii2-extension[Security](/categories/security)

tuzelko/yii2-pwned-passwords
============================

Have I Been Pwned password breach check (k-anonymity API) for Yii2 framework

v1.0.0(1mo ago)090↓61.8%MITPHPPHP &gt;=8.0CI passing

Since Jun 10Pushed 1mo agoCompare

[ Source](https://github.com/TuzelKO/yii2-pwned-passwords)[ Packagist](https://packagist.org/packages/tuzelko/yii2-pwned-passwords)[ RSS](/packages/tuzelko-yii2-pwned-passwords/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (3)Versions (2)Used By (0)

Yii2 Pwned Passwords extension
==============================

[](#yii2-pwned-passwords-extension)

[![Project Status: Active](https://camo.githubusercontent.com/39c688bf243eeb6d3bfc529dcf3cb27443613deb696c8fa9f49bccf1e63e3bef/68747470733a2f2f7777772e7265706f7374617475732e6f72672f6261646765732f6c61746573742f6163746976652e737667)](https://www.repostatus.org/#active)[![Tests](https://github.com/TuzelKO/yii2-pwned-passwords/actions/workflows/tests.yml/badge.svg)](https://github.com/TuzelKO/yii2-pwned-passwords/actions/workflows/tests.yml)[![Latest Version](https://camo.githubusercontent.com/dec4fbe453b1f3d321dc799b37c083ac98e0742f928d71acd0c779dfb2f5953c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f74757a656c6b6f2f796969322d70776e65642d70617373776f726473)](https://packagist.org/packages/tuzelko/yii2-pwned-passwords)[![PHP Version](https://camo.githubusercontent.com/1d8e88a5f7a3bcf3952391eb51b7cdc58dae13163c0faf626640f8018426c5a5/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f74757a656c6b6f2f796969322d70776e65642d70617373776f7264732f706870)](https://packagist.org/packages/tuzelko/yii2-pwned-passwords)[![Total Downloads](https://camo.githubusercontent.com/c55112fa6df82531d198b5ba454b89eb338128ccdcd403e5793b6191ab7c2dc8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f74757a656c6b6f2f796969322d70776e65642d70617373776f726473)](https://packagist.org/packages/tuzelko/yii2-pwned-passwords)[![License](https://camo.githubusercontent.com/4c9ca921be4abac93a50fc4e5cb2315f112748d97b9a36512a837e08fe1dc365/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f54757a656c4b4f2f796969322d70776e65642d70617373776f726473)](https://github.com/TuzelKO/yii2-pwned-passwords/blob/main/LICENSE)

Password breach check for the [Yii2](https://www.yiiframework.com/) framework, backed by the [Have I Been Pwned](https://haveibeenpwned.com/Passwords) Pwned Passwords API.

Checks whether a password appears in known data breaches **without ever sending the password (or its full hash) anywhere** — only the first 5 characters of the SHA-1 hash are transmitted (k-anonymity model), and the match is performed locally.

Features
--------

[](#features)

- **k-anonymity** — only a 5-character hash prefix leaves your application
- **Yii component** — configure once, inject anywhere (DI container friendly)
- **Shared HTTP client** — reuses the `GuzzleHttp\ClientInterface` from your DI container when one is registered; falls back to a default Guzzle client otherwise
- **Response padding** — optional `Add-Padding` mode so even the response size leaks nothing
- **Breach count** — `getHits()` returns how many times the password was seen in breaches
- **Zero configuration** — sensible defaults, override only what you need

Requirements
------------

[](#requirements)

- PHP &gt;= 8.0
- yiisoft/yii2 ~2.0
- guzzlehttp/guzzle ^7.4

Installation
------------

[](#installation)

```
composer require tuzelko/yii2-pwned-passwords
```

Quick start
-----------

[](#quick-start)

```
use tuzelko\yii\pwnedpasswords\PwnedPasswords;

$pwned = new PwnedPasswords();

$pwned->isPwned('password');  // true — seen in millions of breaches
$pwned->getHits('password');  // e.g. 9659365 — number of breach occurrences
```

Or register it as an application component:

```
// config/web.php
'components' => [
    'pwnedPasswords' => [
        'class'   => \tuzelko\yii\pwnedpasswords\PwnedPasswords::class,
        'padding' => true,
    ],
],
```

```
if (Yii::$app->pwnedPasswords->isPwned($form->password)) {
    $form->addError('password', 'This password has been found in a data breach.');
}
```

Usage in a validator
--------------------

[](#usage-in-a-validator)

A typical place for a breach check is a password policy validator on a form:

```
use tuzelko\yii\pwnedpasswords\PwnedPasswords;
use yii\validators\Validator;

class PasswordBreachValidator extends Validator
{
    public function validateAttribute($model, $attribute): void
    {
        try {
            $pwned = Yii::$container->get(PwnedPasswords::class);
            if ($pwned->isPwned($model->$attribute)) {
                $this->addError($model, $attribute, 'This password has been found in a data breach. Please choose a different one.');
            }
        } catch (\Throwable) {
            // Decide your fail-open / fail-closed policy here
            $this->addError($model, $attribute, 'Unable to verify password against breach database. Please try again later.');
        }
    }
}
```

Configuration
-------------

[](#configuration)

PropertyTypeDefaultDescription`apiUrl``string``https://api.pwnedpasswords.com`API base URL (override for a proxy or a self-hosted mirror)`padding``bool``false`Send `Add-Padding: true` so responses are padded with fake zero-hit entries and their size leaks nothing about the requested range`requestOptions``array``[]`Extra [Guzzle request options](https://docs.guzzlephp.org/en/stable/request-options.html) merged into every API call (timeouts, proxy, headers, ...)```
$pwned = new PwnedPasswords([
    'apiUrl'         => 'https://hibp-mirror.internal',
    'padding'        => true,
    'requestOptions' => ['timeout' => 5, 'connect_timeout' => 2],
]);
```

HTTP client resolution
----------------------

[](#http-client-resolution)

On `init()` the component looks for a `GuzzleHttp\ClientInterface` definition in `Yii::$container`:

- **registered** — your application's client is reused (with its base timeouts, middleware, etc.);
- **not registered** — a plain `GuzzleHttp\Client` is created.

```
// config/main.php — share one configured client across the application
'container' => [
    'singletons' => [
        \GuzzleHttp\ClientInterface::class => static fn () => new \GuzzleHttp\Client([
            'timeout'         => 30,
            'connect_timeout' => 10,
        ]),
    ],
],
```

Error handling
--------------

[](#error-handling)

ConditionResultEmpty password`InvalidArgumentException`Transport / HTTP error (4xx, 5xx, timeout)`GuzzleHttp\Exception\GuzzleException`Unexpected non-200 success status`RuntimeException`The component never fails silently — decide at the call site whether a check failure should block the user (fail-closed) or be ignored (fail-open).

How k-anonymity works
---------------------

[](#how-k-anonymity-works)

1. The password is hashed with SHA-1 locally.
2. Only the **first 5 characters** of the hash are sent: `GET /range/5BAA6`.
3. The API returns every known hash suffix in that range (~800–1000 entries) with breach counts.
4. The full hash is matched against the list **locally**.

The API operator never learns the password, its hash, or even whether a match occurred.

Running tests
-------------

[](#running-tests)

```
make test
```

Tests run inside Docker (PHP 8.3) with no local setup required and no real HTTP calls (Guzzle mock handler).

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance90

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

46d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/38176435?v=4)[Eugene Frost](/maintainers/TuzelKO)[@TuzelKO](https://github.com/TuzelKO)

---

Top Contributors

[![TuzelKO](https://avatars.githubusercontent.com/u/38176435?v=4)](https://github.com/TuzelKO "TuzelKO (1 commits)")

---

Tags

securitypasswordyii2extensionbreachhibppwned-passwordsk-anonymity

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/tuzelko-yii2-pwned-passwords/health.svg)

```
[![Health](https://phpackages.com/badges/tuzelko-yii2-pwned-passwords/health.svg)](https://phpackages.com/packages/tuzelko-yii2-pwned-passwords)
```

###  Alternatives

[craftcms/cms

Craft CMS

3.6k3.6M3.2k](/packages/craftcms-cms)[skeeks/cms

SkeekS CMS — control panel and tools based on php framework Yii2

13725.8k63](/packages/skeeks-cms)[eslazarev/wildberries-sdk

Wildberries OpenAPI clients (generated).

293.1k](/packages/eslazarev-wildberries-sdk)[spooner-web/be_secure_pw

You can set password conventions to force secure passwords for BE users.

10466.0k](/packages/spooner-web-be-secure-pw)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
