Optimize verification code and returns WP_Errors

This commit is contained in:
Django Doucet 2023-04-21 15:25:39 -06:00
parent 7dbce74a96
commit f396c6da4e
4 changed files with 70 additions and 16 deletions

View file

@ -57,6 +57,9 @@ function init() {
require_once \dirname( __FILE__ ) . '/includes/rest/class-following.php'; require_once \dirname( __FILE__ ) . '/includes/rest/class-following.php';
Rest\Following::init(); Rest\Following::init();
require_once \dirname( __FILE__ ) . '/includes/rest/class-server.php';
\Activitypub\Rest\Server::init();
require_once \dirname( __FILE__ ) . '/includes/rest/class-webfinger.php'; require_once \dirname( __FILE__ ) . '/includes/rest/class-webfinger.php';
Rest\Webfinger::init(); Rest\Webfinger::init();

View file

@ -114,7 +114,7 @@ class Signature {
$headers = $request->get_headers(); $headers = $request->get_headers();
if ( ! $headers ) { if ( ! $headers ) {
return false; return new \WP_Error( 'activitypub_signature', 'Request not signed', array( 'status' => 403 ) );
} }
$actor = isset( json_decode( $request->get_body() )->actor ) ? json_decode( $request->get_body() )->actor : ''; $actor = isset( json_decode( $request->get_body() )->actor ) ? json_decode( $request->get_body() )->actor : '';
@ -127,7 +127,7 @@ class Signature {
} }
if ( ! isset( $signature_block ) || ! $signature_block ) { if ( ! isset( $signature_block ) || ! $signature_block ) {
return false; return new \WP_Error( 'activitypub_signature', 'Incompatible request signature. keyId and signature are required', array( 'status' => 403 ) );
} }
$signed_headers = $signature_block['headers']; $signed_headers = $signature_block['headers'];
@ -137,12 +137,12 @@ class Signature {
$signed_data = self::get_signed_data( $signed_headers, $signature_block, $headers ); $signed_data = self::get_signed_data( $signed_headers, $signature_block, $headers );
if ( ! $signed_data ) { if ( ! $signed_data ) {
return false; return new \WP_Error( 'activitypub_signature', 'Signed request date outside acceptable time window', array( 'status' => 403 ) );
} }
$algorithm = self::get_signature_algorithm( $signature_block ); $algorithm = self::get_signature_algorithm( $signature_block );
if ( ! $algorithm ) { if ( ! $algorithm ) {
return false; return new \WP_Error( 'activitypub_signature', 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)', array( 'status' => 403 ) );
} }
if ( \in_array( 'digest', $signed_headers, true ) && isset( $body ) ) { if ( \in_array( 'digest', $signed_headers, true ) && isset( $body ) ) {
@ -158,13 +158,17 @@ class Signature {
} }
if ( \base64_encode( \hash( $hashalg, $body, true ) ) !== $digest[1] ) { // phpcs:ignore if ( \base64_encode( \hash( $hashalg, $body, true ) ) !== $digest[1] ) { // phpcs:ignore
return false; return new \WP_Error( 'activitypub_signature', 'Invalid Digest header', array( 'status' => 403 ) );
} }
} }
$public_key = \rtrim( \Activitypub\get_publickey_by_actor( $actor, $signature_block['keyId'] ) ); // phpcs:ignore $public_key = \Activitypub\get_publickey_by_actor( $actor, $signature_block['keyId'] ); // phpcs:ignore
if ( \is_wp_error( $public_key ) ) {
return \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0; return $public_key;
} else {
$public_key = \rtrim( $public_key );
}
return \openssl_verify( $signed_data, $signature_block['signature'], $public_key, $algorithm ) > 0 ?? new \WP_Error( 'activitypub_signature', 'Invalid signature', array( 'status' => 403 ) );
} }

View file

@ -134,10 +134,6 @@ class Inbox {
* @return WP_REST_Response * @return WP_REST_Response
*/ */
public static function user_inbox_post( $request ) { public static function user_inbox_post( $request ) {
// SecureMode/Authorized fetch.
if ( ! \Activitypub\Signature::verify_http_signature( $request ) ) {
return new \WP_REST_Response( array(), 403 );
}
$user_id = $request->get_param( 'user_id' ); $user_id = $request->get_param( 'user_id' );
@ -159,10 +155,6 @@ class Inbox {
* @return WP_REST_Response * @return WP_REST_Response
*/ */
public static function shared_inbox_post( $request ) { public static function shared_inbox_post( $request ) {
// SecureMode/Authorized fetch.
if ( ! \Activitypub\Signature::verify_http_signature( $request ) ) {
return new \WP_REST_Response( array(), 403 );
}
$data = $request->get_params(); $data = $request->get_params();
$type = $request->get_param( 'type' ); $type = $request->get_param( 'type' );

View file

@ -0,0 +1,55 @@
<?php
namespace Activitypub\Rest;
use Activitypub\Signature;
/**
* ActivityPub Server REST-Class
*
* @author Django Doucet
*
* @see https://www.w3.org/TR/activitypub/#security-verification
*/
class Server {
/**
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_filter( 'rest_request_before_callbacks', array( '\Activitypub\Rest\Server', 'authorize_activitypub_requests' ), 10, 3 );
}
/**
* Callback function to authorize each api requests
*
* @see \WP_REST_Request
*
* @param $response
* @param $handler
* @param \WP_REST_Request $request
*
* @return mixed|\WP_Error
*/
public static function authorize_activitypub_requests( $response, $handler, $request ) {
$maybe_activitypub = $request->get_route();
if ( str_starts_with( $maybe_activitypub, '/activitypub' ) ) {
if ( 'POST' === $request->get_method() ) {
$verified_request = Signature::verify_http_signature( $request );
if ( \is_wp_error( $verified_request ) ) {
return $verified_request;
}
} else {
// SecureMode/Authorized fetch.
$secure_mode = \get_option( 'activitypub_use_secure_mode', '0' );
if ( $secure_mode ) {
if ( \is_wp_error( $verified_request ) ) {
return $verified_request;
}
}
}
}
}
}