Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions docs/pages/deployment/security-considerations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,59 @@ In addition to securing the internal APIs, it's recommended to limit access to t
This will allow you to control access to the public APIs, do TLS termination and add additional security measures.
Block any path that's not used by the Nuts node.

.. _ssrf-protection:

Outbound HTTP and SSRF protection
*********************************

The Nuts node fetches URLs supplied by other parties, for example DID documents, OAuth metadata and
Discovery Service endpoints. An attacker could register a URL that resolves to an address inside your
network and let the node request it on their behalf. This is known as Server-Side Request Forgery (SSRF).

In strict mode the node refuses outbound HTTP connections to non-public addresses: loopback,
private (RFC 1918), carrier-grade NAT, link-local, unique local, and the other special-purpose ranges
published in the IANA
`IPv4 <https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml>`_ and
`IPv6 <https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml>`_
special-purpose address registries. The check runs at connect time against the resolved IP address, so it
also covers redirects and cannot be bypassed with DNS rebinding. Cloud provider metadata endpoints (such as
``169.254.169.254`` and Azure's ``168.63.129.16``) are always blocked, following the
`OWASP SSRF prevention cheat sheet <https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html>`_.
Redirects that downgrade from HTTPS to HTTP are refused as well.

A blocked request fails with an error like::

strictmode: blocked connection to non-public address 10.0.0.5

Allowing internal ranges
========================

If a legitimate flow targets a private address, for example an internal OpenID4VCI issuer or an OAuth
flow that stays inside your network, then permit that range with ``http.client.allowedinternalcidrs``.
If part of an allowed range must stay unreachable, then deny that part with ``http.client.deniedcidrs``;
denied ranges take precedence:

.. code-block:: yaml

http:
client:
allowedinternalcidrs:
- 10.0.0.0/8
deniedcidrs:
- 10.5.0.0/16

Keep allowed ranges as narrow as possible. Every address in an allowed range becomes reachable for any
URL an external party can make the node fetch.

Blocking additional ranges
==========================

Some networks use publicly routable addresses for internal-only systems. Public addresses are not
covered by the built-in guard, so add such ranges to ``http.client.deniedcidrs`` explicitly.

The built-in blocks for cloud metadata endpoints cannot be lifted with ``allowedinternalcidrs``.
Both options only apply in strict mode; without strict mode the SSRF guard is disabled.

D(D)oS Protection
*****************

Expand Down
1 change: 1 addition & 0 deletions docs/pages/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Unreleased
* #4233: ``request-credential`` API gains an optional ``credential_request_params`` JSON object overlaid on top of the OpenID4VCI Credential Request body sent to the issuer. Lets the wallet talk to issuers that accept additional fields, or to override the credential request entirely.

## Security
* #4420: Harden the strict-mode HTTP client against SSRF. In strict mode the client now refuses at connect time to reach non-public addresses (loopback, private/RFC1918, unique local, link-local and unspecified), checked against the resolved IP so DNS-rebinding cannot bypass it, and refuses to follow a redirect that downgrades from HTTPS to HTTP. Cloud provider metadata endpoints are always blocked, following the OWASP SSRF prevention cheat sheet. Deployments that legitimately reach a private address for an internal flow (such as an internal credential offering or OAuth user flow) can permit specific ranges with ``http.client.allowedinternalcidrs``; publicly routable ranges that are internal-only can additionally be blocked with ``http.client.deniedcidrs``, which takes precedence. See :ref:`Outbound HTTP and SSRF protection <ssrf-protection>` for deployment guidance. Reported by @raysabee, fixed by @stevenvegt in https://github.com/nuts-foundation/nuts-node/pull/4420
* #4421: Stop reflecting fetched HTTP response bodies in API responses. The OAuth2 and OpenID4VCI callback handlers no longer place a remote endpoint's response body or error text into the returned ``error_description``, the did:web resolver no longer returns the fetched document body in its parse error, and the Discovery Service client no longer includes the remote server's error response in errors returned through the discovery APIs. Such content is now logged (truncated) for diagnostics instead. Static context such as the endpoint that failed is retained. By @stevenvegt in https://github.com/nuts-foundation/nuts-node/pull/4421
* #4420: Harden the strict-mode HTTP client against SSRF. In strict mode the client now refuses at connect time to reach non-public addresses (loopback, private/RFC1918, unique local, link-local and unspecified), checked against the resolved IP so DNS-rebinding cannot bypass it, and refuses to follow a redirect that downgrades from HTTPS to HTTP. Cloud provider metadata endpoints are always blocked, following the OWASP SSRF prevention cheat sheet. Deployments that legitimately reach a private address for an internal flow (such as an internal credential offering or OAuth user flow) can permit specific ranges with ``http.client.allowedinternalcidrs``; publicly routable ranges that are internal-only can additionally be blocked with ``http.client.deniedcidrs``, which takes precedence. Reported by @raysabee, fixed by @stevenvegt in https://github.com/nuts-foundation/nuts-node/pull/4420

Expand Down
Loading