securing wordpress

Post on 11-Apr-2017

399 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Securing WordPress

OWASP Ottawa October 2015 Meetup !

Shawn HooperChief Technology Officer, Actionable Books

@shawnhooper - shawnhooper.ca

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

• WordPress Core Contributor

• GIAC Certified .NET Secure Software Programmer

Hi!

@shawnhooper - shawnhooper.ca

• 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

• 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

WordPress

@shawnhooper - shawnhooper.ca

This market share makes it a big target for hackers!

Code Security

@shawnhooper - shawnhooper.ca

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

!

!

!

on the Open Web Application Security Project

(OWASP) Top Ten List

Injection Attacks

@shawnhooper - shawnhooper.ca

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

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

!

' OR '1'='1' --

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

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

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

'; DROP TABLE wp_users; --

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

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

wp_users; --

SQL Injection Attacks

@shawnhooper - shawnhooper.ca

!

!

!

on the Open Web Application Security Project

(OWASP) Top Ten List

Cross Site Scripting (XSS)

@shawnhooper - shawnhooper.ca

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

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

!

!

!

on the Open Web Application Security Project

(OWASP) Top Ten List

Cross Site Request Forgery

@shawnhooper - shawnhooper.ca

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

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

!

!

!

on the Open Web Application Security Project

(OWASP) Top Ten List

Unvalidated Forwards & Redirects

@shawnhooper - shawnhooper.ca

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

Unvalidated Forwards & Redirects

@shawnhooper - shawnhooper.ca

@shawnhooper - shawnhooper.ca

Scared Yet?

@shawnhooper - shawnhooper.ca

Scared Yet?

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

Sanitization & Validation

@shawnhooper - shawnhooper.ca

Output Validation and Sanitization

@shawnhooper - shawnhooper.ca

Validation

@shawnhooper - shawnhooper.ca

* Are values of the correct type?

* Are values in range?

Validation

@shawnhooper - shawnhooper.ca

Is an input supposed to be an integer?

intval($_POST[‘quantity’])

or

absint($_POST[‘quantity’])

Validation

@shawnhooper - shawnhooper.ca

Is it in range?

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

!

if ( $quantity > 10 ) {

die(‘Quantity Out of Range’);

}

Validation

@shawnhooper - shawnhooper.ca

Should it be an e-mail address?

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

returns false if invalid

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.

Escaping Text

@shawnhooper - shawnhooper.ca

esc_html( $string );

esc_html__( $string, $domain );

ex:

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

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 ); ?>”>

Escaping Text

@shawnhooper - shawnhooper.ca

esc_js( $text );

Escaping a string for echoing in JavaScript.

Escaping URLs

@shawnhooper - shawnhooper.ca

esc_url ($url );

esc_url_raw ( $url );

urlencode ( $string );

urlencode_deep ( $array );

Escaping HTML

@shawnhooper - shawnhooper.ca

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

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

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

Escaping HTML

@shawnhooper - shawnhooper.ca

wp_rel_nofollow( $html )

!

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

Database Sanitization

@shawnhooper - shawnhooper.ca

$wpdb Is Your Friend!

Database Sanitization

@shawnhooper - shawnhooper.ca

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

Database Sanitization

@shawnhooper - shawnhooper.ca

$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

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

Database Sanitization

@shawnhooper - shawnhooper.ca

What about other general queries? !

Statements that include joins? !

$wpdb->query()

Database Sanitization

@shawnhooper - shawnhooper.ca

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

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

Database Sanitization

@shawnhooper - shawnhooper.ca

Database Sanitization

@shawnhooper - shawnhooper.ca

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

Database Sanitization

@shawnhooper - shawnhooper.ca

Valid Placeholders are: !

%s for strings !

%d for integers !

%f for floats

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()

Database Sanitization

@shawnhooper - shawnhooper.ca

$likeValue = ‘value_’;

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

Input Sanitization

@shawnhooper - shawnhooper.ca

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()

Nonces

@shawnhooper - shawnhooper.ca

Nonces

@shawnhooper - shawnhooper.ca

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

Forgery)

Nonces

@shawnhooper - shawnhooper.ca

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

!

!

!

Nonces

@shawnhooper - shawnhooper.ca

Create a Nonce for a URL:

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

>ID );

Nonces

@shawnhooper - shawnhooper.ca

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

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" />

Nonces

@shawnhooper - shawnhooper.ca

Generic Nonce:

!

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

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 );

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' );

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

Nonces

@shawnhooper - shawnhooper.ca

!

To learn more about nonces,

see the WordPress Codex:

!

https://codex.wordpress.org/WordPress_Nonces

Brain Full ?

@shawnhooper - shawnhooper.ca

Good, because we’re almost done.

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

Now you should get this…

@shawnhooper - shawnhooper.ca

XKCD # 327

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:

security@wordpress.org

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.

General WP Security

@shawnhooper - shawnhooper.ca

Backups

@shawnhooper - shawnhooper.ca

• Make Them

• Make Them Regularly

• Test Them

Keep WordPress Updated

@shawnhooper - shawnhooper.ca

• Updates to Core

• Updates to Themes

• Updates to Plugins

Keep WordPress Updated

@shawnhooper - shawnhooper.ca

• Automatic Updates to Core for all minor releases

• Manual Updates via wp-admin dashboard

@shawnhooper - shawnhooper.ca

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.

“Admin” Username

@shawnhooper - shawnhooper.ca

• This was the default in old versions of WordPress.

• Most commonly attacked username when attempting logins.

Enable Two-Factor Authentication

@shawnhooper - shawnhooper.ca

• Clef

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

• Feature Plugin underway for core

Least Privilege

@shawnhooper - shawnhooper.ca

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

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

Disable XML-RPC

@shawnhooper - shawnhooper.ca

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

• Also a great way to DDoS

Remove Version Number

@shawnhooper - shawnhooper.ca

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

remove_action('wp_head', 'wp_generator');

Security Plugins

@shawnhooper - shawnhooper.ca

• WordFence (Plugin)

• iThemes Security (Plugin)

• Sucuri (Plugin)

Limit Login Attempts

@shawnhooper - shawnhooper.ca

• JetPack Brute Force

• iThemes Security

Hosting

@shawnhooper - shawnhooper.ca

• WordPress Managed Hosting

• Manages Updates

• Custom Firewall Configurations

Thank you! Slides: www.shawnhooper.ca

E-Mail: shawn@actionablebooks.com

Twitter: @shawnhooper

WordPress Slack: shooper

@shawnhooper - shawnhooper.ca

top related