securing wordpress

80
Securing WordPress OWASP Ottawa October 2015 Meetup Shawn Hooper Chief Technology Officer, Actionable Books @shawnhooper - shawnhooper.ca

Upload: shawn-hooper

Post on 11-Apr-2017

398 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Securing WordPress

Securing WordPress

OWASP Ottawa October 2015 Meetup !

Shawn HooperChief Technology Officer, Actionable Books

@shawnhooper - shawnhooper.ca

Page 2: Securing WordPress

• I’m Shawn Hooper, CTO at Actionable Books. Former Freelance Developer

• WordPress Core Contributor

• GIAC Certified .NET Secure Software Programmer

Hi!

@shawnhooper - shawnhooper.ca

Page 3: Securing WordPress

• Open Source Content Management System (CMS)

• PHP, MySQL, jQuery, BackboneJS

• Runs 24.7% of all web sites

• Has a 58.7% share of the CMS space

WordPress

@shawnhooper - shawnhooper.ca

http://w3techs.com/technologies/overview/content_management/all

Page 4: Securing WordPress

• WordPress.com is a hosted WordPress service run by Automattic

• WordPress.org is the downloadable, self-hosted version of WordPress

• A huge ecosystem of themes (2K just in .org repo) and plugins (40K) that take advantage of the hook and filter system

WordPress

@shawnhooper - shawnhooper.ca

Page 5: Securing WordPress

WordPress

@shawnhooper - shawnhooper.ca

This market share makes it a big target for hackers!

Page 6: Securing WordPress

Code Security

@shawnhooper - shawnhooper.ca

Page 7: Securing WordPress

We are going to look at a couple of different types of attacks and how to avoid them:

* SQL Injection

* Cross Site Scripting (XSS)

* Cross Site Request Forgery (CSRF)

* Unvalidated Redirects and Forwards

We’re Under Attack!

@shawnhooper - shawnhooper.ca

Page 8: Securing WordPress

!

!

!

on the Open Web Application Security Project

(OWASP) Top Ten List

Injection Attacks

@shawnhooper - shawnhooper.ca

Page 9: Securing WordPress

SQL injection is a code injection technique, used to attack data-driven applications, in

which malicious SQL statements are inserted into an entry field for execution (e.g. to dump

the database contents to the attacker).

- Wikipedia

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

Page 10: Securing WordPress

Without protecting against injection attacks, what would happen if a login form allowed this:

!

' OR '1'='1' --

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

Page 11: Securing WordPress

SELECT * FROM wp_users WHERE user_pass = '' OR '1'='1' --'

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

Page 12: Securing WordPress

'; DROP TABLE wp_users; --

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

Page 13: Securing WordPress

SELECT * FROM wp_users WHERE user_pass = ''; DROP TABLE

wp_users; --

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

Page 14: Securing WordPress

!

!

!

on the Open Web Application Security Project

(OWASP) Top Ten List

Cross Site Scripting (XSS)

@shawnhooper - shawnhooper.ca

Page 15: Securing WordPress

Cross-site scripting (XSS) is a type of computer security vulnerability typically found in web

applications. XSS enables attackers to inject client-side script into web pages viewed by other users. A

cross-site scripting vulnerability may be used by attackers to bypass access controls such as the

same-origin policy.

- Wikipedia

Cross Site Scripting (XSS)

@shawnhooper - shawnhooper.ca

Page 16: Securing WordPress

Cross Site Scripting can be used to capture a user’s authentication / session cookie and then impersonate them on a trusted website.

!

Reflected (ex, delivered by e-mail) vs. Persistant (ex, return by DB in a forum)

Cross Site Scripting (XSS)

@shawnhooper - shawnhooper.ca

Page 17: Securing WordPress

!

!

!

on the Open Web Application Security Project

(OWASP) Top Ten List

Cross Site Request Forgery

@shawnhooper - shawnhooper.ca

Page 18: Securing WordPress

Cross-site request forgery, also known as a one-click attack or session riding and abbreviated as CSRF

(sometimes pronounced sea-surf) or XSRF, is a type of malicious exploit of a website whereby

unauthorized commands are transmitted from a user that the website trusts.

-Wikipedia

Cross Site Request Forgery

@shawnhooper - shawnhooper.ca

Page 19: Securing WordPress

An example of a simple CSRF attack would be getting you to visit a link that would change your

password to something the attacker knows.

Cross Site Request Forgery

@shawnhooper - shawnhooper.ca

Page 20: Securing WordPress

!

!

!

on the Open Web Application Security Project

(OWASP) Top Ten List

Unvalidated Forwards & Redirects

@shawnhooper - shawnhooper.ca

Page 21: Securing WordPress

Could allow code in your website to forward the user to a malicious (ex: phishing) website.

Unvalidated Forwards & Redirects

@shawnhooper - shawnhooper.ca

Page 22: Securing WordPress

@shawnhooper - shawnhooper.ca

Scared Yet?

Page 23: Securing WordPress

@shawnhooper - shawnhooper.ca

Scared Yet?

Let’s figure out how to stop all this stuff from happening…..

Page 24: Securing WordPress

Sanitization & Validation

@shawnhooper - shawnhooper.ca

Page 25: Securing WordPress

Output Validation and Sanitization

@shawnhooper - shawnhooper.ca

Page 26: Securing WordPress

Validation

@shawnhooper - shawnhooper.ca

* Are values of the correct type?

* Are values in range?

Page 27: Securing WordPress

Validation

@shawnhooper - shawnhooper.ca

Is an input supposed to be an integer?

intval($_POST[‘quantity’])

or

absint($_POST[‘quantity’])

Page 28: Securing WordPress

Validation

@shawnhooper - shawnhooper.ca

Is it in range?

$quantity = absint($_POST[‘quantity’])

!

if ( $quantity > 10 ) {

die(‘Quantity Out of Range’);

}

Page 29: Securing WordPress

Validation

@shawnhooper - shawnhooper.ca

Should it be an e-mail address?

$email = is_email( $_POST[‘email’] );

returns false if invalid

Page 30: Securing WordPress

Sanitization

@shawnhooper - shawnhooper.ca

Should it be an e-mail address?

$email = sanitize_email( $_POST[‘email’] );

removes characters that are not valid

in an e-mail address.

Page 31: Securing WordPress

Escaping Text

@shawnhooper - shawnhooper.ca

esc_html( $string );

esc_html__( $string, $domain );

ex:

Hello <?php echo esc_html( $string ); ?> !

Page 32: Securing WordPress

Escaping Text

@shawnhooper - shawnhooper.ca

esc_attr( $text );

esc_attr__( $text, $domain );

Escaping a string for use in an HTML attribute tag.

<div data-value=“<?php echo esc_attr( $value ); ?>”>

Page 33: Securing WordPress

Escaping Text

@shawnhooper - shawnhooper.ca

esc_js( $text );

Escaping a string for echoing in JavaScript.

Page 34: Securing WordPress

Escaping URLs

@shawnhooper - shawnhooper.ca

esc_url ($url );

esc_url_raw ( $url );

urlencode ( $string );

urlencode_deep ( $array );

Page 35: Securing WordPress

Escaping HTML

@shawnhooper - shawnhooper.ca

wp_kses( $fragment, $allowed_html, $protocols);

array( 'a' => array( 'href' => array(), 'title' => array() ),

'br' => array(), 'em' => array(), 'strong' => array() );

Page 36: Securing WordPress

Escaping HTML

@shawnhooper - shawnhooper.ca

wp_rel_nofollow( $html )

!

Adds rel=“nofollow” to every link in the HTML fragment.

Page 37: Securing WordPress

Database Sanitization

@shawnhooper - shawnhooper.ca

Page 38: Securing WordPress

$wpdb Is Your Friend!

Database Sanitization

@shawnhooper - shawnhooper.ca

Page 39: Securing WordPress

$wpdb->insert( ‘table_name’, array( 'column1' => 'value1', 'column2' => 123 ), array( '%s', '%d' ) );

Database Sanitization

@shawnhooper - shawnhooper.ca

Page 40: Securing WordPress

$wpdb->update( 'table', array( 'column1' => 'value1', // string 'column2' => 'value2' // integer (number) ), array( 'ID' => 1 ), array( '%s', // value1 '%d' // value2 ), array( '%d' ) );

Database Sanitization

@shawnhooper - shawnhooper.ca

Page 41: Securing WordPress

$wpdb->delete( 'table', array( 'ID' => 1 ), array( '%d' ) );

Database Sanitization

@shawnhooper - shawnhooper.ca

Page 42: Securing WordPress

What about other general queries? !

Statements that include joins? !

$wpdb->query()

Database Sanitization

@shawnhooper - shawnhooper.ca

Page 43: Securing WordPress

$wpdb->prepare() to make sure query is safe: !!

$wpdb->prepare(SQL Code with Placeholders, variable 1, variable 2, etc.);

Database Sanitization

@shawnhooper - shawnhooper.ca

Page 44: Securing WordPress

Database Sanitization

@shawnhooper - shawnhooper.ca

$safeSQL = $wpdb->prepare(“SELECT * FROM mytable WHERE col1 = ‘%s’ AND col2 = %d”, $sParam, $iParam); !$wpdb->query($safeSQL);

Page 45: Securing WordPress

Database Sanitization

@shawnhooper - shawnhooper.ca

Valid Placeholders are: !

%s for strings !

%d for integers !

%f for floats

Page 46: Securing WordPress

Database Sanitization

@shawnhooper - shawnhooper.ca

If your query includes a LIKE statement in the WHERE clause, use

esc_like()

to properly escape %, _ and \ characters,

which have special meanings.

Still requires $wpdb->prepare()

Page 47: Securing WordPress

Database Sanitization

@shawnhooper - shawnhooper.ca

$likeValue = ‘value_’;

$safeSQL = $wpdb->prepare(“SELECT * FROM table WHERE col1 LIKE ‘%s’", esc_like($likeValue) . '%' );

Page 48: Securing WordPress

Input Sanitization

@shawnhooper - shawnhooper.ca

Page 49: Securing WordPress

Input Sanitization

@shawnhooper - shawnhooper.ca

There are a pile of functions to do input sanitization:

sanitize_title() sanitize_user() balance_tags() tag_escape() is_email() sanitize_html_class() array_map() sanitize_email() sanitize_file_name() sanitize_term() sanitize_term_field()

sanitize_html_class() sanitize_key() sanitize_mime_type() sanitize_option() sanitize_sql_orderby() sanitize_text_field() sanitize_title_for_query() sanitize_title_with_dashes() sanitize_user() sanitize_meta()

Page 50: Securing WordPress

Nonces

@shawnhooper - shawnhooper.ca

Page 51: Securing WordPress

Nonces

@shawnhooper - shawnhooper.ca

A “number used once” to help protect URLs from malicious use (Cross Site Request

Forgery)

Page 52: Securing WordPress

Nonces

@shawnhooper - shawnhooper.ca

NOTE: In WordPress, a nonce is not a number, and it is not used once.

!

!

!

Page 53: Securing WordPress

Nonces

@shawnhooper - shawnhooper.ca

Create a Nonce for a URL:

$complete_url = wp_nonce_url( $bare_url, 'trash-post_'.$post-

>ID );

Page 54: Securing WordPress

Nonces

@shawnhooper - shawnhooper.ca

Create a Nonce for a Form: wp_nonce_field( 'delete-comment_'.$comment_id );

Page 55: Securing WordPress

Nonces

@shawnhooper - shawnhooper.ca

Generates code like this: <input type="hidden" id="_wpnonce"

name="_wpnonce" value="796c7766b1" />

<input type="hidden" name="_wp_http_referer" value="/wp-admin/edit-comments.php" />

Page 56: Securing WordPress

Nonces

@shawnhooper - shawnhooper.ca

Generic Nonce:

!

$nonce = wp_create_nonce( 'my-action_'.$post->ID );

Page 57: Securing WordPress

Validate Nonces

@shawnhooper - shawnhooper.ca

To verify a nonce that was passed in a URL or a form in an admin screen:

!

check_admin_referer( 'delete-comment_'.$comment_id );

Page 58: Securing WordPress

Validate Nonces

@shawnhooper - shawnhooper.ca

To verify a nonce that was passed in an AJAX request:

(parameter is the action sent via AJAX)

!

check_ajax_referer( 'process-comment' );

Page 59: Securing WordPress

Validate Nonces

@shawnhooper - shawnhooper.ca

To verify a generic nonce:

!

wp_verify_nonce( $_REQUEST['my_nonce'], 'process-comment'.$comment_id );

!

Returns false if the nonce fails

Page 60: Securing WordPress

Nonces

@shawnhooper - shawnhooper.ca

!

To learn more about nonces,

see the WordPress Codex:

!

https://codex.wordpress.org/WordPress_Nonces

Page 61: Securing WordPress

Brain Full ?

@shawnhooper - shawnhooper.ca

Good, because we’re almost done.

Page 62: Securing WordPress

Redirecting

@shawnhooper - shawnhooper.ca

wp_redirect( $url, $status ); exit;

wp_safe_redirect( $url, $status ); exit;

!

$status defaults to 302 (temporary)

safe_redirect only allows redirects to a specified set of hostnames, which can be set using the

allowed_redirect_hosts filter

Page 63: Securing WordPress

Now you should get this…

@shawnhooper - shawnhooper.ca

XKCD # 327

Page 64: Securing WordPress

Responsible Disclosure

@shawnhooper - shawnhooper.ca

If you find what you think may be a security vulnerability in WordPress’ code, be responsible. Send an

e-mail with as much detail to:

[email protected]

Don’t blog about it, Facebook it, put it in Trac, Tweet it, etc. Allow the team time to confirm and fix the bug

before letting all the hackers out there know it exists.

Page 65: Securing WordPress

General WP Security

@shawnhooper - shawnhooper.ca

Page 66: Securing WordPress

Backups

@shawnhooper - shawnhooper.ca

• Make Them

• Make Them Regularly

• Test Them

Page 67: Securing WordPress

Keep WordPress Updated

@shawnhooper - shawnhooper.ca

• Updates to Core

• Updates to Themes

• Updates to Plugins

Page 68: Securing WordPress

Keep WordPress Updated

@shawnhooper - shawnhooper.ca

• Automatic Updates to Core for all minor releases

• Manual Updates via wp-admin dashboard

Page 69: Securing WordPress

@shawnhooper - shawnhooper.ca

Page 70: Securing WordPress

File Permissions

@shawnhooper - shawnhooper.ca

• Make sure files & directories have only the permissions required.

• Allowing files in your uploads folder to be executed leads to ugly phishing attacks.

Page 71: Securing WordPress

“Admin” Username

@shawnhooper - shawnhooper.ca

• This was the default in old versions of WordPress.

• Most commonly attacked username when attempting logins.

Page 72: Securing WordPress

Enable Two-Factor Authentication

@shawnhooper - shawnhooper.ca

• Clef

• https://wordpress.org/plugins/search.php?q=two+factor

• Feature Plugin underway for core

Page 73: Securing WordPress

Least Privilege

@shawnhooper - shawnhooper.ca

• Only grant users the appropriate roles they need in wp-admin.

Page 74: Securing WordPress

Change Keys

@shawnhooper - shawnhooper.ca

define('AUTH_KEY', '[w$u#*IL-lLtigU?Un)DY>DSbE}C -<d*+Z{gzc}Qw~p%o%g+INE3MiLBsT@%fjf');

define('SECURE_AUTH_KEY', '+=fttecyOK0jVI/~Q}f+|QMKo0H:}iV9C*koL@ci#L|ERr7i[J`>VDz{qd@zX2rq');

define('LOGGED_IN_KEY', ';5+<dNW?)zzrm*6zb+7-dB IRY%{D0;P2H|^v5BJYh]E[blAUU-n49Hgw0S@#nR-');

define('NONCE_KEY', 'R^@%&qAN$;t;<OTq$<Sm(447Rio}c<2,ts)+bVq1BE-?$Cw+a_@i7!*<`7?K4ne2');

define('AUTH_SALT', '@`Z-(+4Aq}{Y|*ow!OWSe&UNK4v^)hpi|}v)Xe-j14UN|lombcE}pv7#|/]VeG#U');

define('SECURE_AUTH_SALT', 'y9wF-&[!<PzrU]bII>RL0+OiI)D)]juvkojz$40l<Wbejx|xnvn5P,DI9816X-(]');

define('LOGGED_IN_SALT', 'l5&&8omK=~.},&!1w3VyVqFSF}edd7ldN,Y7cI)]XKq7+GUGQKfxjq<%6;v5|v|r');

define('NONCE_SALT', '?vsQ>D>oYiX_g=FnGHU%Sv-f?DuNCD@%1RGeTAL~|%,n(=+-Wr?~1uzmXlw?QW9N');

wp-config.php

Page 75: Securing WordPress

Disable XML-RPC

@shawnhooper - shawnhooper.ca

• Used for remote blogging, tracebacks, pingbacks, etc.

• Also a great way to DDoS

Page 76: Securing WordPress

Remove Version Number

@shawnhooper - shawnhooper.ca

<meta name="generator" content="WordPress 4.3.1”>

remove_action('wp_head', 'wp_generator');

Page 77: Securing WordPress

Security Plugins

@shawnhooper - shawnhooper.ca

• WordFence (Plugin)

• iThemes Security (Plugin)

• Sucuri (Plugin)

Page 78: Securing WordPress

Limit Login Attempts

@shawnhooper - shawnhooper.ca

• JetPack Brute Force

• iThemes Security

Page 79: Securing WordPress

Hosting

@shawnhooper - shawnhooper.ca

• WordPress Managed Hosting

• Manages Updates

• Custom Firewall Configurations

Page 80: Securing WordPress

Thank you! Slides: www.shawnhooper.ca

E-Mail: [email protected]

Twitter: @shawnhooper

WordPress Slack: shooper

@shawnhooper - shawnhooper.ca