Security with WordPress

1f6b851a12600a18a0766baaad0d55f7?s=47 Jon Ang
April 28, 2016

Security with WordPress

Some standard security stuff to watch out for when developing with WordPress (WordPress itself is safe, developers usually aren't)

1f6b851a12600a18a0766baaad0d55f7?s=128

Jon Ang

April 28, 2016
Tweet

Transcript

  1. Security with WordPress WordPress Singapore

  2. Made possible by [ 2 ]

  3. Topics Today • Talk: Pressing On – Jon Ang •

    WordPress Security – Jon Ang • Community Open Space – Everyone [ 3 ]
  4. • Misconceptions • Understanding Vulnerabilities • Best Practices [ 4

    ]
  5. Misconception: WordPress is insecure

  6. None
  7. None
  8. None
  9. WordPress is 100% secure if you take it offline

  10. WordPress is as secure as your plugins, theme and hosting

    combined
  11. Choice Old Version Bad Hosting Password Theme / Plugin

  12. Types of Vulnerabilities

  13. Types • CSRF (Cross-Site Request Forgery) • XSS (Cross-Site Scripting)

    • SQLI (SQL Injection) • And more • Examples provided by George Stephanis [ 13 ]
  14. CSRF • Cross-Site Request Forgery attacks involve malicious websites attempting

    to forge legitimate requests, often from a different site. • Can your code tell the difference between • <form action="process.php"> <input type="hidden" name="action" value="delete" /> <input type="hidden" name="id" value="42" /> <input type="submit" /> </form> • and • <img src="//target.io/admin/process.php?action=delete&id=42" /> [ 14 ]
  15. CSRF • How do we know the submission is legitimate?

    • We check for something that only we can provide — a nonce. • NONCE: Number used ONCE. • Nonces in WordPress are valid for 12–24 hours, and are generated from: • the current 12 hour tick, • the action name, • the current user’s ID, • their session token. • It also leverages the unique generated strings in wp-config.php for security. [ 15 ]
  16. CSRF • Revisiting our previous example: • <form action="process.php"> +

    → <input type="hidden" name="_nonce" value="JD7ANaoKG" /> <input type="hidden" name="action" value="delete" /> <input type="hidden" name="id" value="42" /> <input type="submit" /> </form> • The rotating nonce cannot be anticipated. • <img src="//target.io/admin/process.php?action=delete&id=42&_nonce=…………?" /> • ALSO: Nonces are for intent, not for authentication! They are for validating that the user intended to do an action. Always also check against user caps. [ 16 ]
  17. XSS • Cross-Site Scripting means — in short — that

    some villain can sneak malicious javascript code into your site, and trick you into running the script. • Anything* you can do, it can do too! • * (nearly) [ 17 ]
  18. XSS • <div id="<?php echo $_GET['id']; ?>"> • But what

    about … • /page?id=X"><script>alert('XSS');</script><" • Becomes … • <div id="X"><script>alert('XSS');</script><""> [ 18 ]
  19. XSS [ 19 ]

  20. XSS • <div id="<?php echo esc_attr( $_GET[‘id’] ); ?>"> •

    Again, if we try … • /page?id=X"><script>alert('XSS');</script><" • This time, it becomes … • <div id="X&quot;&gt;&lt;script&gt;alert(&#039;XSS&# 039;);&lt;/script&gt;&lt;&quot;"> [ 20 ]
  21. XSS • It does a lot more than cause alerts.

    • It can load up admin pages, extract nonces, and spoof form submissions. • It can use the Plugin and Theme Editor in the admin to write nasty PHP code to your server. • It can change your password without your knowledge. • It can create a new Administrator account for itself. • It can install new plugins and themes. • It can inject malware links and embeds into your posts. [ 21 ]
  22. XSS • Cross-Site Scripting isn’t all-powerful. • Anything that you

    need to re-authenticate for, it can’t spoof. #31779-core • Anything the currently logged in user can’t do, XSS can’t either. • This is why folks recommend creating yourself an Author or Editor account for every day use, and not doing everything as Administrator. • Rule of : If a sitting at your can do it, so can XSS. ! [ 22 ]
  23. SQLi • SQL Injection lets attackers shoehorn tricky code into

    existing database calls. • SELECT * FROM `wp_posts` WHERE `slug` = '{$value}' LIMIT 1; • // $value = "foo" SELECT * FROM `wp_posts` WHERE `slug` = 'foo' LIMIT 1; • // $value = "x'; UPDATE `wp_users` SET `user_email` = 'h@ck.er' WHERE `ID` = 1; --" • SELECT * FROM `wp_posts` WHERE `slug` = 'x'; • UPDATE `wp_users` SET `user_email` = 'h@ck.er' WHERE `ID` = 1; • -- LIMIT 1; [ 23 ]
  24. SQLi • So what can SQL Injection do? • DROP

    (delete) all of the tables in your database. • INSERT new users, posts, etc. • DELETE users, posts, meta. • UPDATE (change) user info, passwords, roles. • Getting access to an Administrator user (either by creating a new one or changing the password of an existing) is normally game over. [ 24 ]
  25. SQLi • ! This code is a simplified version of

    real code that was once in use by a popular plugin (that shall remain nameless). It is insecure. Do not use it. • $orderby = 'post_title'; $order = 'ASC'; if ( ! empty( $_GET['orderby'] ) { $orderby = esc_sql( sanitize_text_field( $_GET['orderby'] ) ); } if ( ! empty( $_GET['order'] ) ) { $order = esc_sql( strtoupper( sanitize_text_field( $_GET['order'] ) ) ); } $wpdb->get_col( "SELECT `ID` FROM {$wpdb->posts} ORDER BY {$orderby} {$order} LIMIT 10" ); • We’re fetching posts and letting the user change the sorting. • Fairly harmless? #nopenopenope [ 25 ]
  26. SQLi • But what do the functions do? –sanitize_text_field(): Checks

    for invalid UTF-8, Convert single < characters to entity, strip all tags, remove line breaks, tabs and extra white space, strip octets. • Not really useful (or relevant) to us here. –esc_sql(): Prepares a string for use as an SQL query. This function is a glorified addslashes() that works with arrays. • esc_sql() is also deprecated in favor of using $wpdb->prepare(). It will escape single quotes in values, but as ORDER and ORDERBY aren’t escaped in the query, in this case it’s useless. [ 26 ]
  27. SQLi • Sometimes generic functions aren’t enough. • We can

    fix the prior example by whitelisting values: • $orderby = 'post_title'; $order = 'ASC'; $orderbys = array( 'slug', 'id', 'post_date' ); if ( isset( $_GET['orderby'] && in_array( $_GET['orderby'], $orderbys ) ) { $orderby = $_GET['orderby']; } if ( isset( $_GET['order'] ) && 'DESC' === strtoupper( $_GET['order'] ) ) { $order = 'DESC'; } $wpdb->get_col( "SELECT `ID` FROM {$wpdb->posts} ORDER BY {$orderby} {$order} LIMIT 10" ); [ 27 ]
  28. SQLi • The `$wpdb->prepare()` method will take your data, escape

    it, and then pop them into your query. • $wpdb->prepare( "SELECT * FROM `table` WHERE `str` = %s AND `num` = %d;”, "Sonny's", 3 ); • Becomes… • SELECT * FROM `table` WHERE `str` = 'Sonny\'s' AND `num` = 3; • But you can’t use prepare with order / orderby arguments as they’re not quoted in the query. [ 28 ]
  29. Few ways to avoid this • Developers – There are

    not many use-cases to write your own SQL code – Use WordPress’ Classes, Objects and Queries • Eg. Use WP_Query instead of writing your own SQL line • Users – Ask your developers to use native WordPress functions [ 29 ]
  30. Take-away • Developers – Don’t trust user input • Users

    – Ask developers if they have taken appropriate CSRF, XSS, SQLi preventions – Like asking your local butcher if the meat has been through AVA checks [ 30 ]
  31. Best Practices

  32. Best Practices • Keep Upgrading • Checking for intent •

    Sanitising • Escaping • Check for user capabilities • Whitelisting [ 32 ]
  33. Keep Upgrading

  34. User Woe [ 34 ]

  35. Response [ 35 ]

  36. Result [ 36 ]

  37. [ 37 ]

  38. Love your users, but don’t trust them

  39. Sanitisation • Sanitise Early – Because variables can be passed

    around at any time, sanitise early so you know it’s clean, because other functions that make use of your variable may not have cleaning lines • Understand your content – What content do you want? • Functions – intval() – sanitize_text_field() – https://codex.wordpress.org/Validating_Sanitizing_and_Escaping_User_Data [ 39 ]
  40. Escaping • Escape late – Variable can be used to

    output to different context – Different context requires different code • Understand the context – What is it going to be used for? • Functions – esc_url – esc_html – https://codex.wordpress.org/Validating_Sanitizing_and_Escaping_User_Da ta [ 40 ]
  41. SSL • Without https [ 41 ]

  42. SSL • With https [ 42 ]

  43. SSL • Has a huge advantage – Allows HTTP/2 and

    SPDY – Secured sites are ranked higher by Google – Fast sites are ranked higher too [ 43 ]
  44. Reporting Security Exploits

  45. Escaping • Plugins – Contact the plugin author • Especially

    if it’s a non WordPress.org plugin – Contact plugins@wordpress.org • Themes – Contact the theme author • WordPress Core – Contact security@wordpress.org [ 45 ]
  46. None