Javascript para programadores impacientes

JavaScript for impatient programmers — a pesar del título, explica con harto detalle incluso los aspectos más básicos del lenguaje.

Está actualizado hasta ES 2019 por lo que es especialmente útil para refrescar cosas de sintaxis y nuevas funcionalidades que se han ido incorporando al lenguaje.

El libro está prácticamente entero, sólo hay un par de capítulos bonus que son de pago.

Mitigating CVE-2018-6389 WordPress DoS attack with lighttpd

Early in 2018, Barak Tawily published a possible DoS attack for WordPress, that basically works by requesting all possible scripts on the /wp-admin/load-scripts.php, a script that fetches and concatenates javascript files — there’s also a load-styles.php file that does the same for styles.

His vulnerability report was rejected by the WordPress team, on the account that this type of attack should be mitigated at the server or network level… so how do you do that using lighttpd?

Actually it’s pretty easy using mod_evasive, a “very simplistic module to limit connections per IP”, as advertised on the lighttpd docs.

First, you must make sure that mod_evasive it’s enabled on the server.modules block:

server.modules = (
  "mod_access",
  "mod_alias",
  "mod_compress",
  "mod_redirect",
  "mod_rewrite",
  "mod_accesslog",
  "mod_evasive"
)

Then, on the main lighttpd config file you can add the following:

$HTTP["url"] =~ "/wp-admin/load-(styles|scripts).php(.*)" {
  evasive.max-conns-per-ip = 8
}

This will effectively limit the amount of allowed connections to 8 by IP. Of course, you can adjust that value to whatever you need; 8 connections by IP it’s plenty enough for a “normal” editor use.

You can test if it’s working by “attacking” your server with siege or ab or your favourite benchmarking/load testing tool and checking your lighttpd error log, where this should appear:

2018-03-22 21:05:53: (mod_evasive.c.183) 192.168.33.1 turned away. Too many connections.

After testing, you might also want to add this to the lighttpd config:

evasive.silent = "enabled"

… that way, blocked IPs won’t be logged on the errors.log (which, on its own could trigger a DoS by repeatedly writing to the log file)

If you’re using nginx with HTTP/2, there’s an even better way.

Big companies that use PHP

Every now and then there’s still some people who can’t believe PHP can be used for a big, successful project, when actually there are several examples of huge sites using PHP.

Here’s how some of them share their experience.

Facebook

With over 1.49 billion active users, Facebook has been forced into finding creative, out-of-the-box solutions to scaling.

First, they introduced HipHop for PHP on 2010, a transpiler that took PHP code and converted it into a C++ binary.

Even though the project was largely successful, it forced an elaborate deployment process and several incompatibilities with some PHP language features.

On December 2011, they released the HipHop Virtual Machine (HHVM), an open source virtual machine based on Just-In-Time compilation that allowed the greatly improved performance with an easier development and deployment process.

HHVM helped boost the PHP language development introducing lots of new features and a massive performance improvement on PHP7.

Continue reading “Big companies that use PHP”

The challenges of formatting currency data

Localization and internationalization of an app or site it’s a challenge that’s not specially hard on the technical side, but that it can ceirtanly become difficult for social and behavioral aspects, such as our own etnocentrism, lack of standardized standards, or even standards that conflict with actual use or user expectations.

The development team at Etsy identified three attributes that affected currency formatting: the currency, user location and user language. Their post on How Etsy Formats Currency shows how to correctly format currency and some of the practical decisions that are involved in the process.

Using Basic Authentication with the WordPress HTTP API

Basic Authentication it’s often used as a simple security measure or as a temporary authentication method while developing with certain APIs.

While the WordPress HTTP API doesn’t have explicit support for basic authentication, it’s still possible to use it as a header:

$request = wp_remote_post(
  $remote_api_endpoint,
  array(
    'body'    => array( 'foo' => 'bar' ),
    'headers' => array(
      'Authorization' => 'Basic '. base64_encode( $username .':'. $password )
    )
  )
);

Remember that if you’re sending an unencrypted request, all the headers will be sent in plain text, so you should only use it over HTTPS.

Use get_the_terms() instead of wp_get_object_terms()

I was recently debugging the front page of a WordPress site and found a lot of queries to the terms and term relationships database tables.

Digging a little deeper, I found that the culprit were a set of functions that were calling wp_get_object_terms() to get the terms from a set of looped posts… and then I thought… “wait a minute, doesn’t WordPress should be using the object cache for this?”

Well, it turns out that wp_get_object_terms() always queries the database.

If you’re looping over WP_Query results, you should prefer get_the_terms() instead. It’s pretty much the same for most use cases, but it uses the object cache, which by default gets populated with the terms for the posts matching your query — unless you specifically set update_post_term_cache as false when instantiating WP_Query.

The are several differences, though: wp_get_object_terms() can take arrays as the first and second argument, while get_the_terms() can only take the post ID (or object) as first argument (so you can’t get the terms for a bunch of posts on one function call) and a string for taxonomy (so you can’t get the terms for several taxonomies); and you can use a third argument on the former, which the latter doesn’t have.

You could still emulate some of this, and still benefit from using the object cache; for instance, let’s see how you would get the names of the my_custom_tax terms for the current post, ordered by use on a descending way.

// using wp_get_object_terms()
$popular_terms = wp_get_object_terms( $post->ID, 'my_custom_tax', array( 
    'orderby' => 'count',
    'order'   => 'DESC',
    'fields'  => 'names'
) );

// using get_the_terms()
$popular_terms = get_the_terms( $post->ID, 'my_custom_tax' );
// $popular_terms will be ordered alphabetically, so let's order by count
$popular_terms = usort( $popular_terms, function( $a, $b ){
    if ( $a->count < $b->count ) {
        return 1;
    }
    if ( $a->count > $b->count ) {
        return -1;
    }
    return 0;
} );  
// we only need slugs, so...
$popular_terms = wp_list_pluck( $popular_terms, 'name' );

Even if it’s somewhat troublesome, it’s probably worth the effort if you’re trying to maximize for performance.

Unified search with Elasticsearch and WordPress

During the last months of 2012, and as a part of AyerViernes, we worked on one of those projects that is as challenging as delightful to take part in, developing a unified search system for a network of over 200 WordPress sites (both single-install and multisite).

We developed a real-time sync plugin integrating the WordPress sites with an Elasticsearch instance with different content types (mappings) that give us plenty of room to index and search in hundreds of thousands documents generated by the university staff.

You can read the complete post (in spanish) on Medium: Desarrollo de sistema de búsqueda transversal: +200 sitios a un clic de distancia

Using Envoy to automate repetitive tasks

Envoy is a task runner originally developed for Laravel, but that you can also use on any other kind of project.

It’s a very easy way to define tasks with Blade syntax and simple terminal commands, which you can run on remote servers via SSH (including parallel execution) or locally.

Thanks to its simplicity, it’s great to quickly automate repetitive tasks. For instance, this is something I use for importing a replica of the production DB of a site:

@servers(['production' => 'foobar.com', 'local' => 'localhost'])

@macro('sync-db')
    dump-production-db
    get-production-db
    import-production-db
@endmacro

@task('dump-production-db', ['on' => 'production'])
    echo 'Creating production DB dump';
    cd ddbb
    mysqldump --no-autocommit --skip-extended-insert --single-transaction --ignore-table=foobar.wp_simple_history_contexts --ignore-table=foobar.wp_simple_history_history foobar_production | gzip > foobar.production.sql.gz
@endtask

@task('get-production-db', ['on' => 'local'])
    echo 'Copying DB dump from production server';
    cd ddbb
    rsync -P foobar:~/ddbb/foobar.production.sql.gz .
@endtask

@task('import-production-db', ['on' => 'local', 'confirm' => true])
    cd ddbb
    gzip -d -f foobar.production.sql.gz
    sed 's/www.foobar.com/www.foobar.lo/g' -i foobar.production.sql
    echo 'Importing production DB replica';
    mysql -v foobar_development < foobar.production.sql
@endtask