haveibeenpwned

package module
v1.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2026 License: MIT Imports: 16 Imported by: 0

README

haveibeenpwned

CI Docker CodeQL Go Reference Go Report Card

An unofficial Go client for Troy Hunt's Have I Been Pwned API v3, including the Pwned Passwords k-anonymity range API.

Ships in four forms:

  • Go librarygithub.com/marco-montesines/haveibeenpwned
  • CLIhibp (cmd/hibp)
  • HTTP JSON APIhibp serve, published as a container image at ghcr.io/marco-montesines/haveibeenpwned
  • FrankenPHP extension — native PHP functions implemented in Go (frankenphp/)

Documentation

Extended documentation lives in docs/: getting started, use cases, library guide, CLI, HTTP API reference, FrankenPHP extension, deployment & IaC, releases, project status, roadmap, and an FAQ.

Features

Method HIBP endpoint API key required
PwnedPasswordCount range/{first5HashChars} no
GetBreaches breaches no
GetBreachedSite breach/{name} no
GetDataClasses dataclasses no
GetBreachedAccount breachedaccount/{account} yes
GetPastedAccount pasteaccount/{account} yes

An API key for the account endpoints is available at haveibeenpwned.com/API/Key. Password checks use k-anonymity: only the first five characters of the password's SHA-1 hash ever leave your process, and responses are padded.

Library

Install
go get github.com/marco-montesines/haveibeenpwned
Quickstart
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	hibp "github.com/marco-montesines/haveibeenpwned"
)

func main() {
	ctx := context.Background()
	client := hibp.New(os.Getenv("HIBP_API_KEY")) // key may be empty for unauthenticated endpoints

	// Has this password appeared in a breach? (no API key needed)
	count, err := client.PwnedPasswordCount(ctx, "P@ssw0rd")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("password seen %d times in breaches\n", count)

	// All breaches for a domain (no API key needed).
	breaches, err := client.GetBreaches(ctx, "adobe.com")
	if err != nil {
		log.Fatal(err)
	}
	for _, b := range breaches {
		fmt.Printf("%s (%s): %d accounts\n", b.Title, b.BreachDate, b.PwnCount)
	}

	// Breaches for an account (requires API key). nil result = not breached.
	accountBreaches, err := client.GetBreachedAccount(ctx, "info@example.com", "", true, true)
	if err != nil {
		log.Fatal(err)
	}
	if accountBreaches == nil {
		fmt.Println("account not found in any breach")
	}
}

A runnable version lives in examples/quickstart.

Options
client := hibp.New(apiKey,
	hibp.WithHTTPClient(customHTTPClient),
	hibp.WithUserAgent("my-app/1.0"),
	hibp.WithBaseURL(proxyURL),                 // e.g. tests, corporate proxy
	hibp.WithPwnedPasswordsBaseURL(mirrorURL),
)
Error handling

Non-2xx API responses are returned as *hibp.HIBPErrorResponse, which preserves the HTTP status code and the Retry-After value on rate limits:

var apiErr *hibp.HIBPErrorResponse
if errors.As(err, &apiErr) && apiErr.Code == http.StatusTooManyRequests {
	time.Sleep(time.Duration(apiErr.RetryAfter) * time.Second)
}

A 404 on account lookups is not an error — it means "not breached" and yields a nil result with a nil error.

CLI

go install github.com/marco-montesines/haveibeenpwned/cmd/hibp@latest

hibp breaches -domain adobe.com          # all breaches for a domain
hibp breach Adobe                        # a single breach by name
hibp dataclasses                         # all data classes
echo -n 'P@ssw0rd' | hibp password       # check a password (stdin keeps it out of shell history)

export HIBP_API_KEY=...                  # required for account endpoints
hibp account info@example.com
hibp pastes info@example.com

All output is JSON, so it composes with jq.

HTTP API server & Docker

hibp serve exposes the same functionality as a JSON API — useful from PHP, scripts, or any non-Go service:

Route Description
GET /healthz Health check
GET /v1/breaches?domain= All breaches, optional domain filter
GET /v1/breaches/{name} Single breach by name
GET /v1/dataclasses All data classes
GET /v1/breachedaccount/{email} Breaches for an account (?domain=&truncate=&unverified=)
GET /v1/pasteaccount/{email} Pastes for an account
POST /v1/pwnedpassword Body {"password":"..."}{"pwned":true,"count":N}

Run it from the published image:

docker run --rm -p 8080:8080 -e HIBP_API_KEY=... ghcr.io/marco-montesines/haveibeenpwned:latest

curl -s -X POST localhost:8080/v1/pwnedpassword -d '{"password":"P@ssw0rd"}'
# {"count":6421042,"pwned":true}

Or with docker compose (starts the API and the FrankenPHP demo):

HIBP_API_KEY=... docker compose up --build
# API:       http://localhost:8080
# FrankenPHP demo: http://localhost:8081/?password=P@ssw0rd

Images are built for linux/amd64 and linux/arm64 and published to GHCR by .github/workflows/docker.yml only on version tags (v1.2.3:1.2.3, :1.2, :latest) or when the workflow is run manually — never on regular pushes or pull requests.

FrankenPHP: call the library natively from PHP

The frankenphp/ directory contains a FrankenPHP extension written in Go that compiles this library into PHP:

<?php
if (hibp_pwned_password_count($_POST['password']) > 0) {
    throw new RuntimeException('This password appears in known data breaches.');
}

$breaches = json_decode(hibp_breaches('adobe.com'), true);

Build and try it:

docker compose up frankenphp
# open http://localhost:8081/?password=P@ssw0rd&domain=adobe.com

See frankenphp/README.md for details.

Development

go build ./...        # build library, CLI, examples
go test ./... -race   # run the test suite (uses httptest mocks, no network)
go vet ./...

CI runs formatting, vet, build, and race-enabled tests on every push and pull request, plus govulncheck (known vulnerabilities reachable from this code), CodeQL static analysis, and gitleaks secret scanning over the full git history. See SECURITY.md for the security policy.

Rate limits & acceptable use

The authenticated endpoints are rate limited per API key; on 429 the client surfaces the Retry-After value via HIBPErrorResponse.RetryAfter. Please respect the HIBP acceptable use policy and set a descriptive User-Agent (WithUserAgent) identifying your project.

Breach and paste data returned by the API is licensed CC BY 4.0: if your application displays it, you must clearly attribute Have I Been Pwned as the source with a visible link. (The Pwned Passwords range API has no attribution requirement.)

Disclaimer

This is an unofficial client. It is not affiliated with, endorsed by, or sponsored by Have I Been Pwned or Troy Hunt. Breach data is provided by the Have I Been Pwned service.

This software is provided "as is", without warranty of any kind, and is intended for lawful, defensive purposes — such as checking whether your own accounts or passwords appear in known data breaches. You are solely responsible for your use of it, including compliance with the HIBP acceptable use policy and all applicable laws. The authors and contributors accept no liability for any misuse or for any damages arising from the use of this software. See LICENSE for the full terms.

License

MIT.

Documentation

Overview

Package haveibeenpwned is a client for Troy Hunt's Have I Been Pwned API v3 (https://haveibeenpwned.com/API/v3), including the Pwned Passwords k-anonymity range API (https://api.pwnedpasswords.com).

Endpoints that query data for a specific account (GetBreachedAccount, GetPastedAccount) require an API key from https://haveibeenpwned.com/API/Key. All other endpoints, including PwnedPasswordCount, work without a key.

Index

Constants

View Source
const (
	// UserAgent is the default User-Agent header sent with every request.
	// The HIBP API rejects requests without a User-Agent.
	UserAgent = "haveibeenpwned-go/1.0 (+https://github.com/marco-montesines/haveibeenpwned)"
	// Accept is the media type requested from the API.
	Accept = "application/json"
	// Endpoint is the default base URL of the HIBP v3 API.
	Endpoint = "https://haveibeenpwned.com/api/v3/"
	// PwnedPasswordsEndpoint is the default base URL of the Pwned Passwords range API.
	PwnedPasswordsEndpoint = "https://api.pwnedpasswords.com/"
	// Domain matches a syntactically valid ASCII domain name.
	Domain = `^(?:[_\-a-z0-9]+\.)*([\-a-z0-9]+\.)[\-a-z0-9]{2,63}$`
	// DomainUnicode matches a syntactically valid internationalized domain name.
	DomainUnicode = `^(?:[_\-\p{L}\d]+\.)*([\-\p{L}\d]+\.)[\-\p{L}\d]{2,63}$`
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Breach

type Breach struct {
	Name               string      `json:"Name,omitempty"`
	Title              string      `json:"Title,omitempty"`
	Domain             string      `json:"Domain,omitempty"`
	BreachDate         string      `json:"BreachDate,omitempty"`
	AddedDate          time.Time   `json:"AddedDate,omitempty"`
	ModifiedDate       time.Time   `json:"ModifiedDate,omitempty"`
	PwnCount           int         `json:"PwnCount,omitempty"`
	Description        string      `json:"Description,omitempty"`
	LogoPath           string      `json:"LogoPath,omitempty"`
	DataClasses        DataClasses `json:"DataClasses,omitempty"`
	IsVerified         bool        `json:"IsVerified,omitempty"`
	IsFabricated       bool        `json:"IsFabricated,omitempty"`
	IsSensitive        bool        `json:"IsSensitive,omitempty"`
	IsRetired          bool        `json:"IsRetired,omitempty"`
	IsSpamList         bool        `json:"IsSpamList,omitempty"`
	IsMalware          bool        `json:"IsMalware,omitempty"`
	IsSubscriptionFree bool        `json:"IsSubscriptionFree,omitempty"`
	IsStealerLog       bool        `json:"IsStealerLog,omitempty"`
}

Breach describes a single breach in the HIBP corpus. See https://haveibeenpwned.com/API/v3#BreachModel.

type DataClasses

type DataClasses []string

DataClasses is the list of data attributes compromised in a breach.

type HIBPErrorResponse

type HIBPErrorResponse struct {
	Message     string `json:"message"`
	Description string `json:"-"`
	Code        int    `json:"statusCode"`
	RetryAfter  int    `json:"-"`
}

HIBPErrorResponse is the error payload returned by the HIBP API for non-2xx responses. It implements the error interface. RetryAfter carries the Retry-After header value (in seconds) on 429 responses.

func (*HIBPErrorResponse) Error

func (e *HIBPErrorResponse) Error() string

type HaveIBeenPwned

type HaveIBeenPwned struct {
	// contains filtered or unexported fields
}

HaveIBeenPwned is a client for the HIBP v3 and Pwned Passwords APIs. It is safe for concurrent use by multiple goroutines.

func New

func New(accessKey string, opts ...Option) *HaveIBeenPwned

New returns a client for the HIBP v3 API. The accessKey may be empty if only endpoints that do not require authentication are used.

func (*HaveIBeenPwned) GetBreachedAccount

func (o *HaveIBeenPwned) GetBreachedAccount(ctx context.Context, email string, domain string,
	truncateResponse bool, includeUnverified bool) ([]Breach, error)

GetBreachedAccount returns all breaches an account appears in. Requires an API key. A nil slice with a nil error means the account was not found in any breach. See https://haveibeenpwned.com/API/v3#BreachesForAccount.

func (*HaveIBeenPwned) GetBreachedSite

func (o *HaveIBeenPwned) GetBreachedSite(ctx context.Context, site string) (*Breach, error)

GetBreachedSite returns a single breach by its name (the stable "Name" attribute, e.g. "Adobe"). No API key required. See https://haveibeenpwned.com/API/v3#SingleBreach.

func (*HaveIBeenPwned) GetBreaches

func (o *HaveIBeenPwned) GetBreaches(ctx context.Context, domain string) ([]*Breach, error)

GetBreaches returns all breaches in the system, optionally filtered by the domain the breach occurred on. No API key required. See https://haveibeenpwned.com/API/v3#AllBreaches.

func (*HaveIBeenPwned) GetDataClasses

func (o *HaveIBeenPwned) GetDataClasses(ctx context.Context) (*DataClasses, error)

GetDataClasses returns all data classes in the system. No API key required. See https://haveibeenpwned.com/API/v3#AllDataClasses.

func (*HaveIBeenPwned) GetPastedAccount

func (o *HaveIBeenPwned) GetPastedAccount(ctx context.Context, email string) ([]*Paste, error)

GetPastedAccount returns all pastes an account appears in. Requires an API key. A nil slice with a nil error means the account was not found in any paste. See https://haveibeenpwned.com/API/v3#PastesForAccount.

func (*HaveIBeenPwned) PwnedPasswordCount

func (o *HaveIBeenPwned) PwnedPasswordCount(ctx context.Context, password string) (int, error)

PwnedPasswordCount reports how many times a password appears in the Pwned Passwords corpus, using the k-anonymity range API: only the first five characters of the password's SHA-1 hash ever leave the process. A count of zero means the password was not found. No API key required. See https://haveibeenpwned.com/API/v3#PwnedPasswords.

type Option

type Option func(*HaveIBeenPwned)

Option configures a HaveIBeenPwned client.

func WithBaseURL

func WithBaseURL(u *url.URL) Option

WithBaseURL replaces the HIBP v3 API base URL, e.g. for tests or proxies.

func WithHTTPClient

func WithHTTPClient(client *http.Client) Option

WithHTTPClient replaces the default *http.Client.

func WithPwnedPasswordsBaseURL

func WithPwnedPasswordsBaseURL(u *url.URL) Option

WithPwnedPasswordsBaseURL replaces the Pwned Passwords API base URL.

func WithUserAgent

func WithUserAgent(userAgent string) Option

WithUserAgent replaces the default User-Agent header.

type Paste

type Paste struct {
	Source     string    `json:"Source,omitempty"`
	Id         string    `json:"Id,omitempty"`
	Title      string    `json:"Title,omitempty"`
	Date       time.Time `json:"Date,omitempty"`
	EmailCount int       `json:"EmailCount,omitempty"`
}

Paste describes a single paste containing a searched account. See https://haveibeenpwned.com/API/v3#PasteModel.

Directories

Path Synopsis
cmd
hibp command
Command hibp is a command line interface and HTTP API server for Troy Hunt's Have I Been Pwned API v3, built on the github.com/marco-montesines/haveibeenpwned library.
Command hibp is a command line interface and HTTP API server for Troy Hunt's Have I Been Pwned API v3, built on the github.com/marco-montesines/haveibeenpwned library.
examples
quickstart command
A minimal example of the haveibeenpwned library.
A minimal example of the haveibeenpwned library.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL