Wednesday, June 11, 2014

Translating the Site Name in Drupal 7

Allmost all parts of Drupal are easily translatable but a select few are hidden. The Site name and Slogan are a couple of those.
To translate a Drupal 7 Site name you have to activate 'Variable translation' module (which is in the same package as i18n) and the required ones (like 'Variable store').
After you install these modules, go to Configuration -> Multilingual settings -> Variables tab and choose 'Site name' (?q=admin/config/regional/i18n/variable). Then go to Configuration -> System -> Site information (?q=admin/config/system/site-information) and select the language you. Change the 'Site name' field and save.
You'll find the translated site name in 'variable_store' table.

Friday, June 6, 2014

Script ads and their inherent security flaws

Ad networks have built a reputation as spammers and spyware-peddlers constantly busy shuffling the latest hot banners onto your screens. But lurking amongst those banners is a big security hole that can fill your site with bad content, steal data or even use your visitors as bots.
But, how is that possible one might ask. Well, if you're at all familiar with the anatomy of ad scripts you will see that they do a couple of things upon loading. First and foremost they display ads. Ads like in flash, images or html code. Second, they register impressions and clicks for the ads. None of this is dangerous in itself allthough Flash is inherently insecure due to the way Flash is allowed to interact with your browser and the underlying OS.

So, what's the danger in this? Well, let's talk about a common scenario. You run site X. It is a well-known site that gets say 30K unique visitors per day. Most of these visitors don't use AdBlock and all of them except a few IT professionals foilhats don't run NoScript. Your site displays ads from one of the ad networks by inserting their provided script tags in your site.
Your site is secure, you think. Not true! Your site is as secure as the remote scripts you load.

Once a hacker gains access to the ad networks server either by a dns exploit, host spoofing or social engineering he replaces the ad with his own code, making sure the ad still functions while adding another script to the mix. Since you included this in your own code, your site is now open to several exploits. Here are a few possible scenarios.

DOM Hijacking
Once a visitor has loaded the ad scripts and the payload is in effect, the hacker can now display any content in the browser by replacing your DOM elements. It could be subtle manipulations or just vandalism. He is in control of the DOM the script is included in. This is mitigated in part by using iframes.

Cross Site Scripting (XSS)
If you use a web-based content system chances are you will be displaying the ad while logged in to your site. With a little homework, the attacker knows which system you are using and can therefore load any url or post any form that requires login credentials. You are logged in so the payload will be able to access everything you can. This is also mitigated by using iframes.

Distributed Denial Of Service (DDOS)
Since almost all of your visitors display the ad, it's a small feat for the attacker to craft a script that will use javascripts AJAX to enlist your 30K visitors in a botnet. By forcing each visitor to repeatedly load a resource from a target site he will in effect be delivering a huge amount of traffic that will bring the target down.

Consider this simple payload that will overload a server until the client closes the session:

while(somecondition){
   var x = document.createElement('img');
   x.src = 'http://xxx.xxx/img.jpg?' + Math.floor(Math.random() * 10000000);
  document.getElementsByTagName('body')[0].appendChild(x);
}

CPU cycle theft
There are already several available javascript bitcoin miners that could be used in such an exploit. Since there is no real CPU or memory limits on iframes or scripts they could theoretically be quite successful. Especially if they manage to somehow enlist the GPU. Maybe hash calculations could be funneled through WebGL?

I can see several other scenarios where an attacker could utilize your ad network to deliver exploit code.
So the real question is how secure the networks are in reality.

Prevention
DOM/XSS by using iframes. DDOS and CPU theft might not be possible other than by monitoring a client view of your website and ensuring that resource usage and network traffic is normal.
CSP (Content Security Policy) might solve the problems if it gets enough traction with the browser devs http://content-security-policy.com/

Fabric fabfile for Varnish deploy and ban

This is the file I use for deploying new vcls and purging stuff from my caches. I've got load balancing with haproxy set up in front of three caches so deploy or purge needs to be done on all three caches pretty much at the same time. I've set up ssh keys on the hosts so I won't have to stuff the fabfile with passwords and such.

For more on my load balancing proxy set up, see this post.

The fabfile is kept in the same directory as I keep the default.vcl for the hosts.

To purge an url: fab purgeUrl:<urlpattern>
To purge a host:  fab purgeHost:<hostnamepattern>
To deploy default.vcl:  fab deploy

from __future__ import with_statement

import os.path
import time
from fabric.api import *
from fabric.contrib.project import *


"""
Environments
"""

def dev():
        env.hosts = ['host1','host2','host3']
        env.user = 'root'
        env.path = '/etc/varnish'

"Default to 'dev' environment"
dev()


"""
Tasks - Deployment
"""

def deploy():
        require('path', provided_by=[dev])
        with cd(env.path):
                put('default.vcl','default.vcl')
                programname = str(time.time())
                run('varnishadm -Tlocalhost:6082 -S/etc/varnish/secret vcl.load ' + programname + ' /etc/varnish/default.vcl')
                run('varnishadm -Tlocalhost:6082 -S/etc/varnish/secret vcl.use ' + programname)

def purgeHost(myHost):
        require('path', provided_by=[dev])
        with cd(env.path):
                run('varnishadm -Tlocalhost:6082 -S/etc/varnish/secret ban req.http.host == '+ myHost)

def purgeUrl(myUrl):
        require('path', provided_by=[dev])
        with cd(env.path):
                run('varnishadm -Tlocalhost:6082 -S/etc/varnish/secret ban "req.url ~ '+ myUrl +'"')

Bulding a custom token for url aliases (or anything else)

Tokens are a great resource in Drupal when it comes to creating url aliases, replacing text in emails, creating Rules actions or pretty much anything when it comes to automation.

Here's how to create a custom token for nodes that will perform an action on a field from the node and return it as a replacement.

We start out by informing Drupal that we provide a token applicable to nodes. You can replace node with any entity bundle name to provide tokens for them.

/**
 * Implements hook_token_info().
 * Provides Drupal with a list of our tokens to present in the UI 
 */

function example_token_info() {
  $info = array();
  // Define a new token for nodes.
  // "node" can be replaced with other entity names
  $info['tokens']['node']['reverse'] = array(
    'name' => t('Reverse category term name'),
    'description' => t('Outputs a reversed term name.'),
  );
  // Any other tokens follow here
  return $info;
}

We continue by implementing the hook_tokens method to provide methods for performing the token replacements.

/**
 * Implements hook_tokens() .
 * Takes care of the actual replacement of the token
 */
function example_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();

  if ($type == 'node') {
    // Loop through the available tokens.
    foreach ($tokens as $name => $original) {
      // Find our custom tokens by name.
      switch ($name) {
        case 'reverse':
          // Load field from node.
          $fielditems = field_get_items('node',$data['node'],'field_category');
          if ($fielditems[0]['value']) {
            // Load the term.
            $term = taxonomy_term_load($fielditems[0]['value']);            
            // Replace placeholder with reversed term name.
            $replacements[$original] = strrev($term->name);
          }
          else {
            // No category assigned, replace with ''.
            $replacements[$original] = '';
          }
          break;
      }
    }
  }
  // All done.
  return $replacements;

}

That's it. You are now replacing tokens like nobody's business.

Thursday, June 5, 2014

Why Display Suite is a bad idea

And why Code Fields are an even worse idea

You start out with three content types and a couple of view modes and everything is hunky-dory. Then the customer has some specific requirements and you say to your self – "Hey, that's kind of a weird thing to do but what the heck, I'll just do a code field and get it done!"
Of course, this continues throughout the project and you keep adding view modes and code fields to fulfil the reqs because once you've started down the DS + code fields road there's just no stopping it.

A year later, the Customer comes back with some new requirements. The site is going to be relaunched with multiple languages. And you're like – "Fuck yeah! i18n FTW!". Until you realize that all those sweet code fields are all like UND UND UND UND and you really should have written those umpteen gazillion tpls instead.

So, Display Suite? No.
Code Fields? No.

Wednesday, June 4, 2014

Apache Solr Search and FacetAPI translation

To be able to translate the strings "Displaying: 1-10 of 20" and the other parts of the current search blocks you need to install facetapi_i18n. After installing it you need to visit your config for the current search block and hit Save. Then go to your facet configuration and hit Save. Last but not least you need to refresh the strings for translation.

Oh, and btw, the text "Displaying..." will be triple encoded and output escaped html if you don't patch facetapi_i18n with this patch referenced in this issue https://drupal.org/node/1741444

Tuesday, June 3, 2014

Drupal and Ajax, now with language negotiation...

Sometimes I just want to kick myself! I was struggling to set the language for a module that gets called via Ajax and that prints out the contents of a view (with supposedly the correct field language set) but the field language always went for default language...

Well, turns out, sometimes it's Just Not That Hard.

Set up the language negotiation to use prefixes and then just prefix the url with the desired language like /lang/arg

Menu hook from the module:

function example_menu(){
$items = array();
$items['example/ajaxview'] = array(
        'title' => 'Ajax View Loader',
        'page callback' => 'example_loadview',
        'access arguments' => array('access content'),
        'type' => MENU_CALLBACK
);
$items['sv/example/ajaxview'] = array(
        'title' => 'Ajax View Loader',
        'page callback' => 'example_loadview',
        'access arguments' => array('access content'),
        'type' => MENU_CALLBACK
);
$items['en/example/ajaxview'] = array(
        'title' => 'Ajax View Loader',
        'page callback' => 'example_loadview',
        'access arguments' => array('access content'),
        'type' => MENU_CALLBACK
);
$items['de/example/ajaxview'] = array(
        'title' => 'Ajax View Loader',
        'page callback' => 'example_loadview',
        'access arguments' => array('access content'),
        'type' => MENU_CALLBACK
);
return $items;
}

The callback:

function example_loadview($arg){
  // Print the output from a view that takes a contextual argument
  print views_embed_view('myajaxview', 'my_display_id, $arg);
  exit;
}


Calling the function from javascript (Drupal keeps a note of the path prefix used in Drupal.settings.pathPrefix)

var url = "/" + Drupal.settings.pathPrefix + "example/ajaxview/" + arg;
$.get(url, function (data) {
  alert(data); // Or do something with it
});

The language negotiation will do the rest of the heavy lifting.

String just won't translate?

... make sure you're not overwriting it with your fancy-pants javascript functions... :)

Getting the path of a translated node

for those pesky hardcoded links in tpls

global $language;
$translations = translation_path_get_translations("node/42");
/*
Returns the paths of all translations of a node, based on its Drupal path:
array(2) {
        ["de"]=>string(7) "node/42"
        ["en"]=>string(7) "node/43"
}
*/
print l(t('Link Title'), $translations[$language->language]); //l() will return the alias to node/42