Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a more explicit signature verification #1078

Merged
merged 19 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Improve Interactions moderation
* Compatibility with Akismet
* Comment type mapping for `Like` and `Announce`
* Signature verification for API endpoints

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion includes/rest/class-actors.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static function register_routes() {
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'get' ),
'permission_callback' => '__return_true',
'permission_callback' => array( 'Activitypub\Rest\Server', 'signature_verification' ),
),
)
);
Expand Down
2 changes: 1 addition & 1 deletion includes/rest/class-followers.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static function register_routes() {
'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'get' ),
'args' => self::request_parameters(),
'permission_callback' => '__return_true',
'permission_callback' => array( 'Activitypub\Rest\Server', 'signature_verification' ),
),
)
);
Expand Down
2 changes: 1 addition & 1 deletion includes/rest/class-following.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static function register_routes() {
'methods' => \WP_REST_Server::READABLE,
'callback' => array( self::class, 'get' ),
'args' => self::request_parameters(),
'permission_callback' => '__return_true',
'permission_callback' => array( 'Activitypub\Rest\Server', 'signature_verification' ),
),
)
);
Expand Down
6 changes: 3 additions & 3 deletions includes/rest/class-inbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static function register_routes() {
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( self::class, 'shared_inbox_post' ),
'args' => self::shared_inbox_post_parameters(),
'permission_callback' => '__return_true',
'permission_callback' => array( 'Activitypub\Rest\Server', 'signature_verification' ),
),
)
);
Expand All @@ -58,13 +58,13 @@ public static function register_routes() {
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( self::class, 'user_inbox_post' ),
'args' => self::user_inbox_post_parameters(),
'permission_callback' => '__return_true',
'permission_callback' => array( 'Activitypub\Rest\Server', 'signature_verification' ),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'user_inbox_get' ),
'args' => self::user_inbox_get_parameters(),
'permission_callback' => '__return_true',
'permission_callback' => array( 'Activitypub\Rest\Server', 'signature_verification' ),
),
)
);
Expand Down
2 changes: 1 addition & 1 deletion includes/rest/class-outbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static function register_routes() {
'methods' => WP_REST_Server::READABLE,
'callback' => array( self::class, 'user_outbox_get' ),
'args' => self::request_parameters(),
'permission_callback' => '__return_true',
'permission_callback' => array( 'Activitypub\Rest\Server', 'signature_verification' ),
),
)
);
Expand Down
46 changes: 15 additions & 31 deletions includes/rest/class-server.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ public static function init() {
* Add sever hooks.
*/
public static function add_hooks() {
\add_filter( 'rest_request_before_callbacks', array( self::class, 'validate_activitypub_requests' ), 9, 3 );
\add_filter( 'rest_request_before_callbacks', array( self::class, 'authorize_activitypub_requests' ), 10, 3 );
\add_filter( 'rest_request_before_callbacks', array( self::class, 'validate_requests' ), 9, 3 );
\add_filter( 'rest_request_parameter_order', array( self::class, 'request_parameter_order' ), 10, 2 );
}

Expand Down Expand Up @@ -74,39 +73,24 @@ public static function application_actor() {
}

/**
* Callback function to authorize each api requests
* Callback function to authorize an api request.
*
* @see WP_REST_Request
* The function is meant to be used as a permission callback for the rest api.
*
* It verifies the signature of POST, PUT, PATCH and DELETE requests and also the GET requests in secure mode.
* You can use the filter 'activitypub_defer_signature_verification' to defer the signature verification.
* The HEAD request is always bypassed.
*
* @see https://www.w3.org/wiki/SocialCG/ActivityPub/Primer/Authentication_Authorization#Authorized_fetch
* @see https://swicg.github.io/activitypub-http-signature/#authorized-fetch
*
* @param WP_REST_Response|\WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
* Usually a WP_REST_Response or WP_Error.
* @param array $handler Route handler used for the request.
* @param \WP_REST_Request $request Request used to generate the response.
* @param \WP_REST_Request $request The request object.
*
* @return mixed|WP_Error The response, error, or modified response.
* @return bool|\WP_Error True if the request is authorized, WP_Error if not.
*/
public static function authorize_activitypub_requests( $response, $handler, $request ) {
public static function signature_verification( $request ) {
pfefferle marked this conversation as resolved.
Show resolved Hide resolved
if ( 'HEAD' === $request->get_method() ) {
return $response;
}

if ( \is_wp_error( $response ) ) {
return $response;
}

$route = $request->get_route();

// Check if it is an activitypub request and exclude webfinger and nodeinfo endpoints.
if (
! \str_starts_with( $route, '/' . ACTIVITYPUB_REST_NAMESPACE ) ||
\str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'webfinger' ) ||
\str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'nodeinfo' ) ||
\str_starts_with( $route, '/' . \trailingslashit( ACTIVITYPUB_REST_NAMESPACE ) . 'application' )
) {
return $response;
return true;
}

/**
Expand All @@ -123,11 +107,11 @@ public static function authorize_activitypub_requests( $response, $handler, $req
$defer = \apply_filters( 'activitypub_defer_signature_verification', false, $request );

if ( $defer ) {
return $response;
return true;
}

if (
// POST-Requests are always signed.
// POST-Requests have to be always signed.
pfefferle marked this conversation as resolved.
Show resolved Hide resolved
'GET' !== $request->get_method() ||
// GET-Requests only require a signature in secure mode.
( 'GET' === $request->get_method() && use_authorized_fetch() )
Expand All @@ -142,7 +126,7 @@ public static function authorize_activitypub_requests( $response, $handler, $req
}
}

return $response;
return true;
}

/**
Expand All @@ -155,7 +139,7 @@ public static function authorize_activitypub_requests( $response, $handler, $req
*
* @return mixed|WP_Error The response, error, or modified response.
*/
public static function validate_activitypub_requests( $response, $handler, $request ) {
public static function validate_requests( $response, $handler, $request ) {
if ( 'HEAD' === $request->get_method() ) {
return $response;
}
Expand Down
1 change: 1 addition & 0 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ For reasons of data protection, it is not possible to see the followers of other
* Improved: Interactions moderation
* Improved: Compatibility with Akismet
* Improved: Comment type mapping for `Like` and `Announce`
* Improved: Signature verification for API endpoints
* Fixed: Empty `url` attributes in the Reply block no longer cause PHP warnings

= 4.4.0 =
Expand Down
Loading