Skip to content

Commit f0a04b9

Browse files
authored
Prevent webhook event processing when no secret is set in the store (#3333)
* Fail the webhook validation when the store has no webhook secret set It was possible to have no webhook secret configured in the store before the 'configure webhook' button was introduced. This change forces merchants to have a secret set in the store. * Add changelog entries * Replace user agent tests with empty webhook secret assertions
1 parent 82b48c2 commit f0a04b9

File tree

5 files changed

+12
-35
lines changed

5 files changed

+12
-35
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* Tweak - Improve UX by using the 3DS verification modal to confirm setup intents for subscription sign-ups, ensuring customers stay on the checkout page.
1919
* Tweak - Display a notice when the Stripe connect URL is not available.
2020
* Fix - Prevent displaying the default admin description on the checkout page when a payment method description is empty.
21+
* Tweak - Don't process webhooks when the webhook secret isn't set in the store.
2122

2223
= 8.5.2 - 2024-07-22 =
2324
* Fix - Fixed errors when using Link to purchase subscription products that could lead to duplicate payment attempts.

includes/class-wc-stripe-webhook-handler.php

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public function validate_request( $request_headers, $request_body ) {
130130
}
131131

132132
if ( empty( $this->secret ) ) {
133-
return $this->validate_request_user_agent( $request_headers );
133+
return WC_Stripe_Webhook_State::VALIDATION_FAILED_EMPTY_SECRET;
134134
}
135135

136136
// Check for a valid signature.
@@ -157,21 +157,6 @@ public function validate_request( $request_headers, $request_body ) {
157157
return WC_Stripe_Webhook_State::VALIDATION_SUCCEEDED;
158158
}
159159

160-
/**
161-
* Verify User Agent of the incoming webhook notification. Used as fallback for the cases when webhook secret is missing.
162-
*
163-
* @since 5.0.0
164-
* @version 5.0.0
165-
* @param array $request_headers The request headers from Stripe.
166-
* @return string The validation result (e.g. self::VALIDATION_SUCCEEDED )
167-
*/
168-
private function validate_request_user_agent( $request_headers ) {
169-
$ua_is_valid = empty( $request_headers['USER-AGENT'] ) || preg_match( '/Stripe/', $request_headers['USER-AGENT'] );
170-
$ua_is_valid = apply_filters( 'wc_stripe_webhook_is_user_agent_valid', $ua_is_valid, $request_headers );
171-
172-
return $ua_is_valid ? WC_Stripe_Webhook_State::VALIDATION_SUCCEEDED : WC_Stripe_Webhook_State::VALIDATION_FAILED_USER_AGENT_INVALID;
173-
}
174-
175160
/**
176161
* Gets the incoming request headers. Some servers are not using
177162
* Apache and "getallheaders()" will not work so we may need to

includes/class-wc-stripe-webhook-state.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class WC_Stripe_Webhook_State {
2424
const VALIDATION_SUCCEEDED = 'validation_succeeded';
2525
const VALIDATION_FAILED_EMPTY_HEADERS = 'empty_headers';
2626
const VALIDATION_FAILED_EMPTY_BODY = 'empty_body';
27+
const VALIDATION_FAILED_EMPTY_SECRET = 'empty_secret';
2728
const VALIDATION_FAILED_USER_AGENT_INVALID = 'user_agent_invalid';
2829
const VALIDATION_FAILED_SIGNATURE_INVALID = 'signature_invalid';
2930
const VALIDATION_FAILED_TIMESTAMP_MISMATCH = 'timestamp_out_of_range';
@@ -143,6 +144,11 @@ public static function get_last_error_reason() {
143144
return( __( 'The webhook was missing expected body', 'woocommerce-gateway-stripe' ) );
144145
}
145146

147+
if ( self::VALIDATION_FAILED_EMPTY_SECRET === $last_error ) {
148+
return( __( 'The webhook secret is not set in the store', 'woocommerce-gateway-stripe' ) );
149+
}
150+
151+
// Legacy failure reason. Removed in 8.6.0.
146152
if ( self::VALIDATION_FAILED_USER_AGENT_INVALID == $last_error ) {
147153
return( __( 'The webhook received did not come from Stripe', 'woocommerce-gateway-stripe' ) );
148154
}

readme.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,6 @@ If you get stuck, you can ask for help in the Plugin Forum.
146146
* Tweak - Improve UX by using the 3DS verification modal to confirm setup intents for subscription sign-ups, ensuring customers stay on the checkout page.
147147
* Tweak - Display a notice when the Stripe connect URL is not available.
148148
* Fix - Prevent displaying the default admin description on the checkout page when a payment method description is empty.
149+
* Tweak - Don't process webhooks when the webhook secret isn't set in the store.
149150

150151
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-stripe/trunk/changelog.txt).

tests/phpunit/test-wc-stripe-webhook-state.php

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -227,21 +227,6 @@ public function test_get_error_reason_empty_body() {
227227
$this->assertMatchesRegularExpression( '/missing expected body/', WC_Stripe_Webhook_State::get_last_error_reason() );
228228
}
229229

230-
// Test custom user agent validator
231-
public function test_get_error_custom_user_agent_validator() {
232-
$this->cleanup_webhook_secret();
233-
add_filter(
234-
'wc_stripe_webhook_is_user_agent_valid',
235-
function() {
236-
return false;
237-
}
238-
);
239-
240-
$this->set_valid_request_data();
241-
$this->process_webhook();
242-
$this->assertMatchesRegularExpression( '/did not come from Stripe/', WC_Stripe_Webhook_State::get_last_error_reason() );
243-
}
244-
245230
// Test user agent validation ignored
246231
public function test_skip_user_agent_validation() {
247232
// Run test without cleaning up webhook secret.
@@ -257,14 +242,13 @@ function() {
257242
$this->assertEquals( 'No error', WC_Stripe_Webhook_State::get_last_error_reason() );
258243
}
259244

260-
// Test failure reason: invalid user agent.
261-
public function test_get_error_reason_invalid_user_agent() {
245+
// Test failure reason: empty secret.
246+
public function test_get_error_reason_empty_secret() {
262247
$this->cleanup_webhook_secret();
263248

264249
$this->set_valid_request_data();
265-
$this->request_headers['USER-AGENT'] = 'Other';
266250
$this->process_webhook();
267-
$this->assertMatchesRegularExpression( '/did not come from Stripe/', WC_Stripe_Webhook_State::get_last_error_reason() );
251+
$this->assertEquals( 'The webhook secret is not set in the store', WC_Stripe_Webhook_State::get_last_error_reason() );
268252
}
269253

270254
// Test failure reason: invalid signature.

0 commit comments

Comments
 (0)