Ctrl K
ComunidadGuías…

SECURITY.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Security Policy

## Reporting a Vulnerability

**Please do not open a public issue for security vulnerabilities.**

Report security issues privately through GitHub: on the repository's
[Security tab](https://github.com/unmask-sh/unmask/security), choose
**Report a vulnerability**
([direct link](https://github.com/unmask-sh/unmask/security/advisories/new)).
Only you and the maintainers can see the report.

What we will do:

- Acknowledge your report within **48 hours** (best effort).
- Investigate and provide updates as we go.
- Coordinate a fix and a disclosure timeline with you.

If you do not hear back within 7 days, please escalate by opening a
public issue **without disclosing the vulnerability** (just say "I have
a security report awaiting acknowledgement").

## Coordinated Disclosure

We follow a coordinated disclosure process:

- **90-day window** from the initial report to public disclosure.
- We may publish details earlier once a fix has shipped.
- We do not pursue legal action against good-faith researchers who:
  - report responsibly through the channel above,
  - do not access or modify data beyond what is necessary to demonstrate the issue,
  - do not run testing against production deployments of other users.

## Supported Versions

While unmask is pre-release (v0.x), we only provide security fixes for
the **latest released version**. After v1.0 we expect to support the
latest minor release; older releases will be best-effort.

## Threat Model

What unmask defends against, what it does not, and what it relies on --
so that a report, and a deployment, can be weighed against the design
rather than against a guess at it.

**In scope.** Automated clients that fetch a site's pages at a volume or
pace no visitor keeps: scrapers, crawlers that ignore the operator's
wishes, credential-stuffing and form-abuse scripts, and botnets that
spread the same work over many addresses.  The defence is a cost (a
proof-of-work a real browser pays once, a CAPTCHA where the cost must be
higher), a set of signals read from the request (the TLS fingerprint, the
User-Agent against the fingerprint, the network, the rate, the paths a
bot touches and a visitor never does), and the record of what each one
did, so the operator can see and decide.  Every decision is made on the
server; the client's cooperation is never assumed.

**Out of scope.** Volumetric and network-layer denial of service (that
is the provider's job upstream); abuse by an authenticated user of the
protected application; a client that behaves exactly like a browser
because it is one, driven by a person; and attribution of a request to a
person or a device.  A JA4 fingerprint names a TLS stack, not an
individual -- a browser and a bot built on the same stack share it -- so
it is a signal among others, never a verdict on its own.  A proof-of-work
is a cost, not a proof of anything: a client that can afford it gets a
pass, and the budgets on what a pass is used for (per pass and per
address) are what bound the client that pays.

**Trust boundaries.** The daemon and nginx share one secret
(`secret.bv_secret`); every node that verifies passes for a site must
hold it, and a node that leaks it can mint passes for every other.  The
admin's own signatures -- the session cookie, the CSRF tokens -- derive
from a second secret (`secret.admin_secret`) that only the daemon holds
and that no rendered nginx file carries, so a node that leaks `bv_secret`
mints passes but cannot sign an admin session (`unmask doctor` checks
that the rendered files do not carry it).  A
pass cookie is a bearer token bound to the client's address (an IPv6
client by its /64), the site's host and a window; within those it can
be replayed from the same address, which is why what a pass is used for
is counted.  The module trusts the address nginx hands it: behind a load
balancer or a CDN the operator names the trusted ranges
(`nginx.trusted_lb_realip`), and a range named wrongly lets a client
choose its address.  The admin is reachable by whoever reaches its
listener; the operator binds it, lists the hosts it answers to
(`nginx.admin_allowed_hosts`) and, where needed, the addresses.  The
access log the daemon reads arrives over a local socket from nginx; the
daemon counts what nginx logged.

**Assumptions and known limits.**  When the daemon is down, nginx lets
judged requests through rather than refuse every visitor (fail-open, by
design; the module says so in nginx's error log and the admin's
notifications).  Budgets are counted per node: behind a load balancer the
total is that many times the figure.  The module reads the TLS
ClientHello through OpenSSL's message callback, of which an SSL context
holds one: another nginx module that sets the same callback after unmask
replaces it, and JA4-based decisions silently stop applying (the module
logs it, `unmask doctor` reports it).  nginx's QUIC compatibility layer
on OpenSSL older than 3.5 does the same for HTTP/3.  Community Bans are
fetched by default and applied with limits on what one pull can switch
on; submitting is opt-in.  Signatures are HMAC (SHA-1 today, SHA-256
selectable; both verified during the move) cut to 64 bits -- enough for
an online guess to be hopeless, not for an offline one against a leaked
secret, which is the secret's job to prevent.  That signature is what
the nginx module checks on every request; within its binding (the
address or the /64, the host, the window) a pass is a bearer token and
may be presented again, by the same client or by another in the same
/64, up to the reuse budget the operator sets -- a narrower binding
would cut off the visitor whose address moves, which is why the budget,
not the binding, is the limit.

**Rescue by name.**  A request from a crawler the operator chose to let
through is exempt from the challenge.  As shipped, the search engines
and the AI crawlers on that list are recognised by their published
address ranges (`$is_bypass_ip`), never by the User-Agent alone; a
client that merely claims one of their names is treated as any other
client.  A User-Agent pattern the operator adds by hand to the name-only
list is trusted on the name alone, for GET and HEAD, at the name-only
rate -- the honeypot, the ban list, the country and ASN rules and the
custom rules still apply to it -- so an added pattern is a decision to
trust whoever can type that name, and the shipped configuration makes
none.

## Out of Scope

The following are not in scope for this project's security advisories:

- Vulnerabilities in nginx, OpenSSL, MariaDB, or other upstream
  dependencies — please report those to the respective upstream.
- Operator misconfiguration of unmask (e.g. exposing the admin UI to
  the public internet without authentication).
- Issues that require running unmask on an end-of-life OS or with
  rate-limiting disabled.
- Denial-of-service attacks at the network layer (use your provider's
  network-layer DDoS protection upstream).

## Recognition

Reporters are credited in the release notes for the fix, unless they
ask to remain anonymous.