Unverified Commit 45b856c2 authored by Hong Minhee's avatar Hong Minhee
Browse files

FEP-fe34 origin-based security model

Implements comprehensive origin-based security checks following FEP-fe34
specification to prevent content spoofing attacks and maintain secure
federation practices.

Key changes:

- Add crossOrigin option to property accessors and lookupObject() with
  three modes: "ignore" (default), "throw", and "trust"
- Implement trust tracking system for embedded objects using trust sets
- Add origin validation for object @id vs document URL in lookupObject()
- Add origin validation for property objects vs their parent object
- Update documentation with security model explanations and examples
- Add comprehensive tests for all cross-origin scenarios

This replaces the previous FEP-c7d3 ownership model with the more robust
origin-based approach, ensuring objects and their properties respect
origin boundaries to prevent malicious content spoofing.

See also http://w3id.org/fep/fe34

Fixes https://github.com/fedify-dev/fedify/issues/440



Co-Authored-By: default avatarClaude <noreply@anthropic.com>
parent a7db279c
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -10,6 +10,27 @@ To be released.

### @fedify/fedify

 -  Implemented [FEP-fe34] origin-based security model to protect against
    content spoofing attacks and ensure secure federation practices.  The
    security model enforces same-origin policy for ActivityPub objects and
    their properties, preventing malicious actors from impersonating content
    from other servers.  [[#440]]

     -  Added `crossOrigin` option to Activity Vocabulary property accessors
        (`get*()` methods) with three security levels: `"ignore"` (default,
        logs warning and returns `null`), `"throw"` (throws error), and
        `"trust"` (bypasses checks).
     -  Added `LookupObjectOptions.crossOrigin` option to `lookupObject()`
        function and `Context.lookupObject()` method for controlling
        cross-origin validation.
     -  Embedded objects are now validated against their parent object's origin
        and only trusted when they share the same origin or are explicitly
        marked as trusted.
     -  Property hydration now respects origin-based security, automatically
        performing remote fetches when embedded objects have different origins.
     -  Internal trust tracking system maintains security context throughout
        object lifecycles (construction, cloning, and property access).

 -  Fixed handling of ActivityPub objects containing relative URLs.  The
    Activity Vocabulary classes now automatically resolve relative URLs by
    inferring the base URL from the object's `@id` or document URL, eliminating
@@ -73,6 +94,7 @@ To be released.
    Node.js's `--experimental-require-module` flag and resolves dual package
    hazard issues.  [[#429], [#431]]

[FEP-fe34]: https://w3id.org/fep/fe34
[FEP-5711]: https://w3id.org/fep/5711
[OStatus 1.0 Draft 2]: https://www.w3.org/community/ostatus/wiki/images/9/93/OStatus_1.0_Draft_2.pdf
[RFC 7033 Section 4.4.4.3]: https://datatracker.ietf.org/doc/html/rfc7033#section-4.4.4.3
@@ -88,6 +110,7 @@ To be released.
[#411]: https://github.com/fedify-dev/fedify/issues/411
[#429]: https://github.com/fedify-dev/fedify/issues/429
[#431]: https://github.com/fedify-dev/fedify/pull/431
[#440]: https://github.com/fedify-dev/fedify/issues/440
[#443]: https://github.com/fedify-dev/fedify/pull/443

### @fedify/cli
+2 −2
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ Supported FEPs
 -  [FEP-8b32][]: Object Integrity Proofs
 -  [FEP-521a][]: Representing actor's public keys
 -  [FEP-5feb][]: Search indexing consent for actors
 -  [FEP-c7d3][]: Ownership
 -  [FEP-fe34][]: Origin-based security model
 -  [FEP-c0e0][]: Emoji reactions
 -  [FEP-e232][]: Object Links

@@ -42,7 +42,7 @@ Supported FEPs
[FEP-8b32]: https://w3id.org/fep/8b32
[FEP-521a]: https://w3id.org/fep/521a
[FEP-5feb]: https://w3id.org/fep/5feb
[FEP-c7d3]: https://w3id.org/fep/c7d3
[FEP-fe34]: https://w3id.org/fep/fe34
[FEP-c0e0]: https://w3id.org/fep/c0e0
[FEP-e232]: https://w3id.org/fep/e232

+38 −0
Original line number Diff line number Diff line
@@ -412,6 +412,44 @@ const note = await ctx.lookupObject(
> `DocumentLoader`*](#getting-an-authenticated-documentloader)
> section for details.

> [!CAUTION]
> For security reasons, the `~Context.lookupObject()` method implements
> origin-based validation following [FEP-fe34].  If the fetched JSON-LD
> document contains an `@id` that has a different origin than the requested
> URL, the method will return `null` by default to prevent content spoofing
> attacks.
>
> For example, if you request `https://example.com/notes/123` but the fetched
> document has `@id: "https://malicious.com/notes/456"`, the method will
> refuse to return the object and log a warning instead.
>
> You can control this behavior using the `crossOrigin` option:
>
> ~~~~ typescript twoslash
> import { type Context } from "@fedify/fedify";
> const ctx = null as unknown as Context<void>;
> // ---cut-before---
> // Default behavior: return null for cross-origin objects (recommended)
> const objectDefault = await ctx.lookupObject("https://example.com/notes/123");
>
> // Throw an error when encountering cross-origin objects
> const objectStrict = await ctx.lookupObject(
>   "https://example.com/notes/123",
>   { crossOrigin: "throw" }
> );
>
> // Bypass origin checks (not recommended, potential security risk)
> const objectBypass = await ctx.lookupObject(
>   "https://example.com/notes/123",
>   { crossOrigin: "trust" }
> );
> ~~~~
>
> Only use `crossOrigin: "trust"` if you fully understand the security
> implications and have implemented additional validation measures.

[FEP-fe34]: https://w3id.org/fep/fe34


WebFinger lookups
-----------------
+88 −0
Original line number Diff line number Diff line
@@ -495,6 +495,94 @@ corresponding TypeScript types:
[`CryptoKey`]: https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey


Origin-based security model
---------------------------

*This section is applicable since Fedify 1.9.0.*

Fedify implements an origin-based security model following [FEP-fe34] to protect
against content spoofing attacks and maintain secure federation practices.
This security model ensures that objects and their properties respect origin
boundaries, preventing malicious actors from impersonating content from other
servers.

[FEP-fe34]: https://w3id.org/fep/fe34

### Same-origin policy for properties

When accessing properties of ActivityPub objects, Fedify enforces same-origin
policy rules.  Even if an object appears to be embedded in the JSON-LD
representation, property accessors will automatically perform hydration (remote
fetching) if the embedded object's `@id` has a different origin than its parent
object.

For example, consider this JSON-LD representation:

~~~~ json
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Create",
  "id": "https://example.com/activities/123",
  "actor": "https://example.com/users/alice",
  "object": {
    "type": "Note",
    "id": "https://different-origin.com/notes/456",
    "content": "This is from a different origin"
  }
}
~~~~

In this case, when you access the `object` property of the `Create` activity,
Fedify will not trust the embedded `Note` object because its `@id` has a
different origin (`different-origin.com`) than the parent activity's origin
(`example.com`).  Instead, it will fetch the `Note` object directly from
`https://different-origin.com/notes/456` to verify its authenticity.

### Controlling origin checks

You can control this behavior using the `crossOrigin` option when calling
property accessors:

~~~~ typescript twoslash
import { Create } from "@fedify/fedify";
const create = {} as unknown as Create;
// ---cut-before---
// Default behavior: ignore untrusted embedded objects (recommended)
const objectDefault = await create.getObject();

// Throw an error when encountering cross-origin objects
const objectStrict = await create.getObject({ crossOrigin: "throw" });

// Bypass origin checks (not recommended, potential security risk)
const objectBypass = await create.getObject({ crossOrigin: "trust" });
~~~~

The `crossOrigin` option accepts the following values:

`"ignore"` (default)
:   Ignore untrusted embedded objects and fetch from origin

`"throw"`
:   Throw an error when encountering cross-origin embedded objects

`"trust"`
:   Trust embedded objects regardless of origin (⚠️ security risk)

> [!WARNING]
> Using `crossOrigin: "trust"` can expose your application to security
> vulnerabilities, including content spoofing attacks.  Only use this option
> if you fully understand the security implications and have implemented
> additional validation measures.

### Trust tracking

Internally, Fedify maintains trust information for each property value.  Objects
that are constructed locally, fetched directly from their authoritative source,
or explicitly validated are marked as trusted.  This trust information is used
to determine whether property accessors need to perform additional validation
or fetching.


Extending the vocabulary
------------------------

+9752 −1360

File changed.

Preview size limit exceeded, changes collapsed.

Loading