# RUSTSEC-2026-0199: Panic in `bcrypt::verify` on non-ASCII hash input

> Attackers can crash Rust applications by supplying non-ASCII bcrypt hashes, causing a panic in the verification logic.

- **Severity:** Not scored
- **Exploited in the wild:** Not known
- **Login needed:** Not needed
- **Attackable:** Over the internet
- **Victim action needed:** Not needed
- **Fix available:** Yes
- **Published:** 20 Jun 2026

## What is RUSTSEC-2026-0199?

RUSTSEC-2026-0199 is a denial of service vulnerability in bcrypt, published on 20 Jun 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.

### Official description

`bcrypt::verify(password, hash)` and `HashParts::from_str(hash)` panic in
`str::slice_error_fail` when given a 60-byte `&str` containing a multi-byte
UTF-8 character at certain byte positions.

## Impact

Any Rust code that calls `bcrypt::verify` (or `HashParts::from_str`) with an
attacker-controlled hash string will panic. The `bcrypt` crate is
`#![forbid(unsafe_code)]`, so this is limited to a denial-of-service and
cannot lead to memory corruption.

Realistic attack contexts include:

- Rust authentication services reading hashes from a database that was
  previously compromised via, e.g., SQL injection. The attacker can then
  crash the service on every login attempt against the tampered account.
- CLI tools accepting hashes from stdin or command-line arguments.
- Password managers or vault services loading hashes from untrusted
  configuration sources.

## Root cause

`split_hash` performed five `&str` slicing operations on the input hash:
`&hash[1..3]`, `&hash[4..6]`, `&hash[7..]`, `&salt_and_hash[..22]`, and
`&salt_and_hash[22..]`. None of these were char-boundary-checked. Any input
where a multi-byte UTF-8 character spanned one of those byte positions
caused a panic.

This is a regression of the fix originally shipped in 2021 for issue #62
(commit `0833509`). The regression was introduced in the parser rewrite in
PR #95 (commit `e9a8394`, released as 0.19.0).

The pre-existing regression test `does_no_error_on_char_boundary_splitting`
was not removed, but was silently rendered ineffective by the new
`bytes[0] != b'$'` guard, which rejected its input earlier and prevented
it from reaching the buggy slices — leaving CI green through the regression.

## Fix

`split_hash` now rejects any hash string containing non-ASCII bytes up
front. A valid bcrypt hash is always exactly 60 ASCII bytes, so this
closes the entire class of byte-boundary panics rather than guarding each
slice individually.

The fix was merged in PR #103 and released as `bcrypt 0.19.2` on 2026-06-20.

## Downstream impact

`pyca/bcrypt` (which depended on `bcrypt 0.19.1`) is **not** affected. Its
Python-side wrapper performs its own byte-level salt parsing before
invoking `bcrypt::hash_with_salt`, and never reaches the buggy code path
in `split_hash` or `verify`.

## Which versions are affected by RUSTSEC-2026-0199?

1 open source package ship an affected version, including bcrypt.


| Package | Ecosystem | Affected | Fixed in |
| --- | --- | --- | --- |
| `bcrypt` | crates.io | From 0.19.0 | 0.19.2 |

## How do I fix RUSTSEC-2026-0199?

Upgrade bcrypt to 0.19.2 or later.

## Is RUSTSEC-2026-0199 being exploited?

There is no known exploitation of RUSTSEC-2026-0199 in the wild and no public exploit code.

## Fix prompt for AI coding agents

```text
Check this codebase for RUSTSEC-2026-0199 and fix it if it is affected.

What it is: RUSTSEC-2026-0199 is a denial of service vulnerability in bcrypt, published on 20 Jun 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.
Exploitation: There is no known exploitation of RUSTSEC-2026-0199 in the wild and no public exploit code.

Affected packages and fixed versions:
- bcrypt (crates.io): affected from 0.19.0; fixed in 0.19.2

Task:
1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.
2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.
3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.
4. Run the tests and summarise what changed and why.
5. If nothing in the project is affected, say so and change nothing.

Reference: https://insights.sbomify.com/RUSTSEC-2026-0199/ (Markdown: https://insights.sbomify.com/RUSTSEC-2026-0199/index.md)
```

## Sources

- OSV: https://osv.dev/vulnerability/RUSTSEC-2026-0199

---
From sbomify Insights: https://insights.sbomify.com/RUSTSEC-2026-0199/
Summaries are generated and may contain mistakes; the official description above is the record.
