0

This is a great presentation about Drupal security. They were talking about ways to only allow tags from certain trusted domains.

Any ideas how to do that? I couldn't find a D7 module for that.

2 Answers 2

1

What I've done in this circumstance is to create my own BBCode style filter so as to take out that huge regex issue and have total control over the creation of the img tag, eg, I never allow casual users to enter it in any html filter. Also, it's a lot easier to input for some less than stellar users :)

For example, [img:www.host.com/foo/bar/baz.jpg] can be easily checked for the host, and if you don't want potentially animated gifs, you can disallow them (although yes I do know browsers are looking at the contents of the file now, not the extension, but that's another story), and also, important at least for me, you can set up the css so you don't have a huge image ruining your site formatting, and pretty easily the above turns into

<img src="http://www.host.com/foo/bar/baz.jpg" class="user-submitted-image">

if all is good in the world, or

<img src=".../badhost.png" title="Sorry: images aren't allowed from www.badhost.com" class="user-denied-image">

or even <img src=".../blink-was-bad-enough.png" title="Sorry: gifs aren't allowed here" class="user-denied-image">

if it's not.

Obviously, this doesn't help with any pre-imported content, but moving forward, it might help out.

ADDITION:

I pulled this out of some code and I hope I got all of it:

function YOURMODULE_bbc_inject($text) {

  $types=array('img', 'youtube');

  foreach ($types as $type) {
    $match=array();
    if (preg_match_all('/\[(' . $type . '):([^=\\]]+)\]/i', $text, $match)) {
      $s = $r = array();
      foreach ($match[2] as $before => $after) {
        $s[] = $match[0][$before];
        $r[] = YOURMODULE_bbc($type, $after);
      }
      $text=str_replace($s, $r, $text);
    }
  }

  return $text;
}

and then helpers:

function YOURMODULE_bbc($type, $after) {

  switch ($type) {

    case 'img':
      $foo=explode('/',$after);
      $host=$foo[0];

      if (YOURMODULE_valid_img_host($host)) {
        return '<img src="http://' . check_plain($after) . '" class="user-submitted-image">';
      } else {
        return '[INVALID IMAGE]';
      }

    case 'youtube':
      return '<iframe class="user-submitted-youtube-video" src="http://www.youtube-nocookie.com/embed/' . check_plain($after) . '?wmode=transparent" frameborder="0" allowfullscreen></iframe>';

    default:
      return '[HUH?]';
  }

}

function YOURMODULE_valid_img_host($host) {

  $foo=explode(',', variable_get('YOURMODULE_img_host_blacklist','www.foo.com,foo.com,www.bar.com,bar.com'));

  if (in_array($host, $foo)) {
    return FALSE;
  } else {
    // you can check for valid image type here if desired, otherwise...
    return TRUE;
  }
}

You would use the first function in a filter process, I'd show you how to do it in D6 in a second or two, but I know you are using D7, but it should be straightforward enough to figure that out from some of the other questions and answers on here. Just make sure it runs as late as possible so it doesn't inject some HTML that another filter would disable.

But, if all is installed correctly, you "should" now be able to have a [img:www.foo.com/evil.jpg] denied on your website, plus I threw in the [youtube:35I6Iue_XDE] to show how to extend it to other injectable things.

(It's up to you to add the black list variable into an admin/settings form, but that's also pretty straightforward)

END OF ADDITION :)

Of course, another avenue for this is to not allow images at all in your text and instead have a multiple value image field that can populated and then displayed alongside the text or in a lightbox, etc. And there you could use all sorts of node() and validate() operations to check those fields for sanity/safety before displaying them.

2
  • thanks. You do that with a hook_filter_info() or with a hook_form_alter() when the user submits the form?
    – uwe
    Apr 14, 2012 at 16:04
  • 1
    You let your user put whatever they want in the text field, and then use the filter to modify it on display. That way, if a blacklist changes, for example, it catches it. You rarely want to modify input and then save it. So I would do it in a filter. If you are asking about the last alternative, I like doing that in nodeapi('view') (D6) or for you in D7 its equivalent. That way the validation is done in real time, eg, on display/view.
    – Jimajamma
    Apr 14, 2012 at 16:18
4

If you mean disallowing <img> elements where the src isn't on a whitelist, then you can make a custom input filter.

The first step is to make a hook_filter_info to declare your input filter. You would then make a callback function that does the work.

The callback would do some regex matching, looking for <img> elements and pulling out the src attributes. Then regex match the src against your whitelisted domains. If everything is OK, let the image pass, else replace it with whitespace.

Finally, make a hook_menu which sets up a settings page so you can enter your whitelist via the UI.

Once you have done all of this, you add your filter to the appropriate input formats. When the page gets rendered out, the filter will be applied.

All of this is rather easy. The heavy listing is the <img> regex, but I suspect 15 min of googling will find some good ones.

Not the answer you're looking for? Browse other questions tagged or ask your own question.