1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-07-12 10:16:18 +02:00

[3.1.1] Implement %URI.SecureMunge and %URI.SecureMungeSecretKey, thanks Chris!

- URIFilter->prepare can return false in order to abort loading of the filter
- Implemented post URI filtering. Set member variable $post to true to set a URIFilter as such.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1772 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang
2008-05-26 16:26:47 +00:00
parent 3c4346cb1e
commit 322288e6c0
17 changed files with 215 additions and 28 deletions

View File

@ -178,6 +178,7 @@ require 'HTMLPurifier/URIFilter/DisableExternal.php';
require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
require 'HTMLPurifier/URIFilter/HostBlacklist.php';
require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
require 'HTMLPurifier/URIFilter/SecureMunge.php';
require 'HTMLPurifier/URIScheme/ftp.php';
require 'HTMLPurifier/URIScheme/http.php';
require 'HTMLPurifier/URIScheme/https.php';

View File

@ -172,6 +172,7 @@ require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';
require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
require_once $__dir . '/HTMLPurifier/URIFilter/SecureMunge.php';
require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';
require_once $__dir . '/HTMLPurifier/URIScheme/http.php';
require_once $__dir . '/HTMLPurifier/URIScheme/https.php';

View File

@ -50,6 +50,10 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
$result = $scheme_obj->validate($uri, $config, $context);
if (!$result) break;
// Post chained filtering
$result = $uri_def->postFilter($uri, $config, $context);
if (!$result) break;
// survived gauntlet
$ok = true;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,33 @@
URI.SecureMunge
TYPE: string/null
VERSION: 3.1.1
DEFAULT: NULL
--DESCRIPTION--
<p>
Like %URI.Munge, this directive munges browsable external resources
into another URI redirection service. %URI.SecureMunge accepts a URI
with a %s located where the original URI should be substituted in,
and %t located where the secure checksum should be provided.
However, this directive affords
an additional level of protection by generating a secure checksum from
the URI as well as a secret key provided by %URI.SecureMungeSecretKey.
Any redirector script can check this key by using:
</p>
<pre>$checksum === sha1($secret_key . ':' . $url)</pre>
<p>
If the output is TRUE, the redirector script should accept the URI.
</p>
<p>
Please note that it would still be possible for an attacker to procure
secure hashes en-mass by abusing your website's Preview feature or the
like, but this service affords an additional level of protection
that should be combined with website blacklisting.
</p>
<p>
<strong>This is a post-filter.</strong> This filter may conflict with other
post-filters that deal with external links.
</p>

View File

@ -0,0 +1,11 @@
URI.SecureMungeSecretKey
TYPE: string/null
VERSION: 3.1.1
DEFAULT: NULL
--DESCRIPTION--
<p>
This is the secret key used in conjunction with %URI.SecureMunge. Your
redirector script needs to know about this key, and no one else should
know about this key. Please see the above
directive for more details.
</p>

View File

@ -5,6 +5,7 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
public $type = 'URI';
protected $filters = array();
protected $postFilters = array();
protected $registeredFilters = array();
/**
@ -34,8 +35,13 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
}
public function addFilter($filter, $config) {
$filter->prepare($config);
$this->filters[$filter->name] = $filter;
$r = $filter->prepare($config);
if ($r === false) return; // null is ok, for backwards compat
if ($filter->post) {
$this->postFilters[$filter->name] = $filter;
} else {
$this->filters[$filter->name] = $filter;
}
}
protected function doSetup($config) {
@ -66,8 +72,16 @@ class HTMLPurifier_URIDefinition extends HTMLPurifier_Definition
}
public function filter(&$uri, $config, $context) {
foreach ($this->filters as $name => $x) {
$result = $this->filters[$name]->filter($uri, $config, $context);
foreach ($this->filters as $name => $f) {
$result = $f->filter($uri, $config, $context);
if (!$result) return false;
}
return true;
}
public function postFilter(&$uri, $config, $context) {
foreach ($this->postFilters as $name => $f) {
$result = $f->filter($uri, $config, $context);
if (!$result) return false;
}
return true;

View File

@ -19,10 +19,15 @@ abstract class HTMLPurifier_URIFilter
*/
public $name;
/**
* True if this filter should be run after scheme validation.
*/
public $post = false;
/**
* Performs initialization for the filter
*/
public function prepare($config) {}
public function prepare($config) {return true;}
/**
* Filter a URI object

View File

@ -6,6 +6,7 @@ class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter
protected $blacklist = array();
public function prepare($config) {
$this->blacklist = $config->get('URI', 'HostBlacklist');
return true;
}
public function filter(&$uri, $config, $context) {
foreach($this->blacklist as $blacklisted_host_fragment) {

View File

@ -11,14 +11,15 @@ class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter
$def = $config->getDefinition('URI');
$this->base = $def->base;
if (is_null($this->base)) {
trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_ERROR);
return;
trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING);
return false;
}
$this->base->fragment = null; // fragment is invalid for base URI
$stack = explode('/', $this->base->path);
array_pop($stack); // discard last segment
$stack = $this->_collapseStack($stack); // do pre-parsing
$this->basePathStack = $stack;
return true;
}
public function filter(&$uri, $config, $context) {
if (is_null($this->base)) return true; // abort early

View File

@ -0,0 +1,32 @@
<?php
class HTMLPurifier_URIFilter_SecureMunge extends HTMLPurifier_URIFilter
{
public $name = 'SecureMunge';
public $post = true;
private $target, $secretKey, $parser;
public function prepare($config) {
$this->target = $config->get('URI', 'SecureMunge');
$this->secretKey = $config->get('URI', 'SecureMungeSecretKey');
$this->parser = new HTMLPurifier_URIParser();
if (!$this->secretKey) {
trigger_error('URI.SecureMunge is being ignored due to lack of value for URI.SecureMungeSecretKey', E_USER_WARNING);
return false;
}
return true;
}
public function filter(&$uri, $config, $context) {
if (!$this->target || !$this->secretKey) return true;
$scheme_obj = $uri->getSchemeObj($config, $context);
if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it
if (is_null($uri->host) || empty($scheme_obj->browsable)) {
return true;
}
$string = $uri->toString();
$checksum = sha1($this->secretKey . ':' . $string);
$new_uri = str_replace('%s', rawurlencode($string), $this->target);
$new_uri = str_replace('%t', $checksum, $new_uri);
$uri = $this->parser->parse($new_uri); // overwrite
return true;
}
}