$30 off During Our Annual Pro Sale. View Details »

Security with WordPress

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)

Jon Ang

April 28, 2016
Tweet

More Decks by Jon Ang

Other Decks in Technology

Transcript

  1. Security with WordPress
    WordPress Singapore

    View Slide

  2. Made possible by
    [ 2 ]

    View Slide

  3. Topics Today
    • Talk: Pressing On
    – Jon Ang
    • WordPress Security
    – Jon Ang
    • Community Open Space
    – Everyone
    [ 3 ]

    View Slide

  4. • Misconceptions
    • Understanding Vulnerabilities
    • Best Practices
    [ 4 ]

    View Slide

  5. Misconception:
    WordPress is insecure

    View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. WordPress is 100% secure if you take it offline

    View Slide

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

    View Slide

  11. Choice
    Old
    Version
    Bad
    Hosting
    Password
    Theme /
    Plugin

    View Slide

  12. Types of Vulnerabilities

    View Slide

  13. Types
    • CSRF (Cross-Site Request Forgery)
    • XSS (Cross-Site Scripting)
    • SQLI (SQL Injection)
    • And more
    • Examples provided by George Stephanis
    [ 13 ]

    View Slide

  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





    • and

    [ 14 ]

    View Slide

  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 ]

    View Slide

  16. CSRF
    • Revisiting our previous example:

    + →




    • The rotating nonce cannot be anticipated.

    • 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 ]

    View Slide

  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 ]

    View Slide

  18. XSS

    • But what about …
    • /page?id=X">alert('XSS');<"
    • Becomes …
    • alert('XSS');<"">
    [ 18 ]

    View Slide

  19. XSS
    [ 19 ]

    View Slide

  20. XSS

    • Again, if we try …
    • /page?id=X">alert('XSS');<"
    • This time, it becomes …

    [ 20 ]

    View Slide

  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 ]

    View Slide

  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 ]

    View Slide

  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` = '[email protected]' WHERE `ID` = 1; --"
    • SELECT * FROM `wp_posts` WHERE `slug` = 'x';
    • UPDATE `wp_users` SET `user_email` = '[email protected]' WHERE `ID` = 1;
    • -- LIMIT 1;
    [ 23 ]

    View Slide

  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 ]

    View Slide

  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 ]

    View Slide

  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 ]

    View Slide

  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 ]

    View Slide

  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 ]

    View Slide

  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 ]

    View Slide

  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 ]

    View Slide

  31. Best Practices

    View Slide

  32. Best Practices
    • Keep Upgrading
    • Checking for intent
    • Sanitising
    • Escaping
    • Check for user capabilities
    • Whitelisting
    [ 32 ]

    View Slide

  33. Keep Upgrading

    View Slide

  34. User Woe
    [ 34 ]

    View Slide

  35. Response
    [ 35 ]

    View Slide

  36. Result
    [ 36 ]

    View Slide

  37. [ 37 ]

    View Slide

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

    View Slide

  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 ]

    View Slide

  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 ]

    View Slide

  41. SSL
    • Without https
    [ 41 ]

    View Slide

  42. SSL
    • With https
    [ 42 ]

    View Slide

  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 ]

    View Slide

  44. Reporting Security Exploits

    View Slide

  45. Escaping
    • Plugins
    – Contact the plugin author
    • Especially if it’s a non WordPress.org plugin
    – Contact [email protected]
    • Themes
    – Contact the theme author
    • WordPress Core
    – Contact [email protected]
    [ 45 ]

    View Slide

  46. View Slide