> ## Documentation Index
> Fetch the complete documentation index at: https://docs.finowl.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Block breached passwords at sign up

> Stop users from choosing passwords that already appear in breach lists, without slowing down sign up.

Credential stuffing attacks reuse passwords leaked from other sites. Checking new passwords against breach data is one of the most effective defenses you can add, and it is recommended by [NIST SP 800-63B](https://pages.nist.gov/800-63-4/sp800-63b.html).

## How it fits in your flow

<Steps>
  <Step title="User submits the sign-up form">
    Run your usual checks first, such as minimum length. They are free and instant.
  </Step>

  <Step title="Your server calls Finowl">
    Send the password, exactly as typed, to `POST /v1/breaches/password`.
  </Step>

  <Step title="Decide based on count">
    If `breached` is `true`, ask the user to choose a different password. Otherwise, create the account.
  </Step>
</Steps>

## Example

<CodeGroup>
  ```javascript Node.js (Express) theme={null}
  app.post("/signup", async (req, res) => {
    const { email, password } = req.body;

    if (password.length < 12) {
      return res.status(400).json({ error: "Use at least 12 characters." });
    }

    if (await isBreached(password)) {
      return res.status(400).json({
        error: "This password has appeared in a data breach. Please choose a different one.",
      });
    }

    await createUser(email, password);
    res.status(201).end();
  });

  async function isBreached(password) {
    try {
      const response = await fetch("https://api.finowl.co/v1/breaches/password", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.FINOWL_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ password }),
        signal: AbortSignal.timeout(5_000),
      });
      if (!response.ok) return false; // Fail open: never block sign up on an outage.
      const { breached } = await response.json();
      return breached;
    } catch {
      return false;
    }
  }
  ```

  ```python Python (Flask) theme={null}
  import os
  import requests
  from flask import Flask, request, jsonify

  app = Flask(__name__)
  session = requests.Session()
  session.headers["Authorization"] = f"Bearer {os.environ['FINOWL_API_KEY']}"


  def is_breached(password: str) -> bool:
      try:
          response = session.post(
              "https://api.finowl.co/v1/breaches/password",
              json={"password": password},
              timeout=5,
          )
          if not response.ok:
              return False  # Fail open: never block sign up on an outage.
          return response.json()["breached"]
      except requests.RequestException:
          return False


  @app.post("/signup")
  def signup():
      email = request.json["email"]
      password = request.json["password"]

      if len(password) < 12:
          return jsonify(error="Use at least 12 characters."), 400

      if is_breached(password):
          return jsonify(
              error="This password has appeared in a data breach. Please choose a different one."
          ), 400

      create_user(email, password)
      return "", 201
  ```
</CodeGroup>

## Design decisions

<AccordionGroup>
  <Accordion title="Fail open or fail closed?" icon="code-branch">
    The examples fail open: if Finowl is slow or unavailable, sign up continues. That keeps your conversion rate safe during an outage. If you are protecting high-value accounts, you may prefer to fail closed and ask the user to try again.

    The examples use a short 5 second timeout because a user is waiting. If the timeout fires, the check may still have been charged.
  </Accordion>

  <Accordion title="Should any breach count block the password?" icon="sliders">
    Blocking at `count > 0` is the safest choice and what NIST recommends. Some teams warn for low counts and block only above a threshold, for example `count >= 10`, to reduce friction. Whatever you choose, never tell the user the exact count.
  </Accordion>

  <Accordion title="Check at login and password change too" icon="right-to-bracket">
    Run the same check when users change their password. At login, a breached password is a strong signal to prompt a reset, especially combined with a new device or location.
  </Accordion>

  <Accordion title="Write a helpful error message" icon="message">
    Explain why, without blaming the user: "This password has appeared in a data breach on another site, so attackers may try it. Please choose a different one." Suggest a password manager.
  </Accordion>
</AccordionGroup>

<Warning>
  Make sure the sign-up request body, which contains the password, is never written to your logs, traces or error reports.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.