Defensive Coding

What is it, why you need it, and how to start?

Gabor Javorszky - @javorszky - 6th August 2019

Slides: wpbristol.j7y.co

Happy Path

In the context of software or information modeling, a happy path is a default scenario featuring no exceptional or error conditions.

Cloudflare regex writeup

https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/

It’s long, full of technical details, and just amazing at its complexity.

Shifty email bug

One morning ~3700 emails went out that shouldn’t have gone out.

https://jasonmccreary.me/articles/shifty-email-bug/

Common point

All of these situations arose because software was made to do something that the humans writing it had not considered.

Motivation

Undergone PCI compliance training for client.

Had conversations where unknowns were present.

Regular Tuesday when I deal with an API that just goes away.

So where do these come from?

Hooks and Filters

It's what makes WordPress extensible.

Also what makes WordPress very easy to break.

User input

Every single esc_* function in WordPress is there because of this.

Additioanally wp_kses_* and the PHP native strip_tags() functions.

External services

Let's handle payment with a card through an API (Stripe for example)

  • Payment successful
  • Payment declined (insufficient funds)
  • Payment declined (wrong CVV)
  • Payment declined (fraud risk)
  • Payment declined (card type not accepted)
  • Pre-authorization taken
  • API is just not there
  • ... wait WHAT

External services 2

The return of the external services

You post some data, and you get some data back, for example an autocomplete service.

What happens if that service starts returning poisoned payloads?

Social Warfare plugin RCE

PCI: data coming in MUST be sanitized

But also it's just a good practice anyways. Suggestions:

  1. Validate against some template for expected data, chop everything else that's not supposed to be there.
  2. Equally important: make sure that what's expected to be there IS actually there.

Example code from WooCommerce

Removed bits first.

WC_Order_Factory::get_order( $order_id );


$classname = apply_filters(
	'woocommerce_order_class',
	$classname,
	$order_type,
	$order_id
);

return new $classname( $order_id );
					

+


add_filter( 'woocommerce_order_class', function() {
	return 'NoSuchClass';
} );
					

=

Warning: Uncaught Error: Class 'NoSuchClass' not found in ...

rip


$classname = apply_filters( 'woocommerce_order_class', $classname, $order_type, $order_id );

if ( ! class_exists( $classname ) ) {
	return false;
}

return new $classname( $order_id );
					

+


add_filter( 'woocommerce_order_class', function() {
	return 'NoSuchClass';
} );
					

=


// false is returned
					

👍


$classname = apply_filters( 'woocommerce_order_class', $classname, $order_type, $order_id );

if ( ! class_exists( $classname ) ) {
	return false;
}

return new $classname( $order_id );
					

+


add_filter( 'woocommerce_order_class', function() {
	return 'ClassWillThrowExceptionOnInit';
} );
					

+


class ClassWillThrowExceptionOnInit {
	public function __construct() {
		throw new Exception( 'No instance for you' );
	}
}
					

$classname = apply_filters( 'woocommerce_order_class', $classname, $order_type, $order_id );

if ( ! class_exists( $classname ) ) {
	return false;
}

try {
	return new $classname( $order_id );
} catch ( Exception $e ) {
	wc_caught_exception( $e, __FUNCTION__, func_get_args() );
	return false;
}
					

+


add_filter( 'woocommerce_order_class', function() {
	return 'ClassWillThrowExceptionOnInit';
} );

class ClassWillThrowExceptionOnInit {
	public function __construct() {
		throw new Exception( 'No instance for you' );
	}
}
					

$classname = apply_filters( 'woocommerce_order_class', $classname, $order_type, $order_id );

if ( ! class_exists( $classname ) ) {
	return false;
}

try {
	return new $classname( $order_id );
} catch ( Exception $e ) {
	wc_caught_exception( $e, __FUNCTION__, func_get_args() );
	return false;
}
					

+


add_filter( 'woocommerce_order_class', function() {
	return [ 2 ];
} );
					

But why would anyone DO THAT?!

Why would anyone do that?

It doesn't matter.

Can you mathematically PROVE that that won't happen? Nope. So handle it.

What does ”handle it“ mean?

Software fails gracefully, enters a state where it can either restart itself, or tell the user that it failed, why it failed, and what to do to not fail again.

All without preventing further work.

Apollo 11: Mission Out of Control

Practical limits to handling fail paths

Lack of time

Lack of management buy-in

And...

(CapitalOne)

When do you not care?

  • you're totally fine with it failing. Or when you WANT it to fail, on purpose.
  • effort exceeds benefit.
  • you accept the risk, and are okay cleaning up corrupted data instead of preventing data corruption in the first place because it's easier to fix than prevent.

The end

Questions?

@javorszky

Further reading

Pretty much all of this: https://github.com/kdeldycke/awesome-falsehood