Re: HTMLPurifier and the "Input" class

Shad Laws <shad-xpYdmXCiSuZWk0Htik3J/[email protected]> Fri, 5 Apr 2013 18:10:46 +0200
Newsgroups gmane.comp.web.gallery.devel
Message-ID <CA+z51A4n7+0a2tb6si7wEAtivVaV5eLPfPkJ==c8H1HF2a2JKA@mail.gmail.com>
Quick correction:

> - references to these in non-controllers needs to be moved to their
> respective controllers.  I've perused the code, and don't think this
> would be a big deal.

This isn't true.  In controllers, it's:
$request->post()

And in non-controllers, it's:
Request::$current->post()

Take care,
Shad


On 5 April 2013 17:29, Shad Laws <shad-xpYdmXCiSuZWk0Htik3J/[email protected]> wrote:
> Hey everyone,
>
> Some updates:
> - HTMLPurifier - Standalone v4.5.0 is included in gallery.
> - Purifier - I wrote a Purifier class and config file (*strongly*
> based on the 3.0.x purifier module) to load a single instance of it
> and purify things (as Purifier::purify($str)).
> - Input::instance()->ip_address - changed to K3's Request::$client_ip,
> so we no longer need the Input library for this.
> - Input - wrote a draft of it (http://github.com/gallery/gallery3/pull/269)...
>
> ... but as I learn more about K3 and the form-handling module Formo,
> I'm starting to think it's not the right approach.  I'm pretty happy
> with the first three steps, but think that the fourth step (the Input
> class draft) won't play well with the Request class or the Formo.
>
> tl;dr: I don't think we should add an Input class, and think that it'd
> be a better fit in the K3 framework to add code to Gallery::ready() or
> bootstrap and some overrides to the Request class.
>
> ----
>
> K2 approach with global_xss_filtering = true (i.e. how Gallery 3.0.x works)
>
> - PHP fills the GPCS superglobals for us
> - Input::clean() cleans GPCS at init and overwrites the superglobals
> - Input library returns GPS values from these arrays without further processing
> - Input library returns C values after running them through
> cookie::get() to check for signed cookies and remove invalid ones
> - nobody downstream has access to the raw values
>
> Cleaning consists of:
> - GPCS: remove control chars, convert to UTF8 and remove incompatible chars
> - GPC: clean keys (except cookie $Version, $Path, and $Domain),
> standardize newlines, remove magic_quotes if needed, XSS clean
> - S: none
>
> Details on the filters:
> - clean keys: fast, simple, secure regex in MY_Input
> (preg_replace("/[^0-9a-zA-Z:_.-]/", "_", $str)).  This is okay.
> - XSS clean: less fast, less simple, less secure regex in Input.  This
> is problematic.
>
> ---
>
> K3 approach as-shipped
>
> - PHP fills the GPCS superglobals for us
> - Kohana::sanitize() cleans GPC globals at init and overwrites the superglobals
> - Request::factory() stores G as $request->query($_GET)
> - Request::factory() stores P as $request->post($_POST)
> - Request::factory() stores C as $request->cookie($cookies), where
> $cookies is $_COOKIE processed to check signed cookies
> - most things downstream are expected to get their GPC values from
> Request, which is easily accessible from any controller (*)
> - $_SERVER is not touched and always accessed directly
>
> Cleaning consists of:
> - GPC: standardize newlines, remove magic_quotes if needed
> - S: none
>
> Removed from the list:
> - remove control chars, convert to UTF8 and remove incompatible chars.
>  This is handled by UTF8::clean(), which is not called.
> - clean keys
> - XSS clean (obviously)
>
> (*) exception: URL::query() accesses $_GET directly, but nobody else
> seems to use this function.
>
> ---
>
> So, given that, here's my current thought on what we should add at init:
> - GPCS: call UTF8::clean() to process GPCS, overwrite the original values
> - GPC: clean keys (simple, secure regex like before), overwrite the
> original values
> - GPC: clean values using Purifier class, put into Request object but
> not into superglobals
> - S: clear HTTP_HOST to force folks to use SERVER_NAME instead
>
> This can be done by adding:
> - class Gallery_Request extends Kohana_Request, with post(), query(),
> and cookie() that, if given a full array, purify it before calling its
> parent function.  We can make an exception for csrf to run a faster,
> simpler [0-9a-f] regex.
> - extra stanza in Gallery::ready() (or bootstrap) that calls
> UTF8::clean(), does unset($_SERVER("HTTP_HOST")), and cleans keys.
> - (optional) override URL::query() to make it clean $_GET before using
> it.  This isn't strictly needed by Gallery's core, but would be
> security against other devs using it and accidentally introducing XSS
> insecurities.
>
> Then, to access them:
> - GPC cleaned: use Request object from controllers ($request->query(),
> $request->post(), $request->cookie())
> - GPC raw: use $_GET, $_POST, and $_COOKIE directly
> - S: use $_SERVER directly
>
> Which means that:
> - Input::instance()->get($key) becomes $request->query($key)
> - Input::instance()->post($key) becomes $request->post($key)
> - Input::instance()->cookie($key) becomes $request->cookie($key)
> - Input::instance()->server($key) becomes $_SERVER($key)
> - Input::instance()->post($key, $default) becomes
> Arr::get($request->post(), $key, $default)    (and likewise for get,
> cookie, and server using defaults)
> - references to these in non-controllers needs to be moved to their
> respective controllers.  I've perused the code, and don't think this
> would be a big deal.
> - K3 and Formo should use our cleaned values by default :-)
>
> ---
>
> Whew - that writeup took awhile!  Thoughts?
>
> Take care,
> Shad
>
>
>
> On 2 April 2013 18:23, Shad Laws <shad-xpYdmXCiSuZWk0Htik3J/[email protected]> wrote:
> <snipped>
>>>>
>>>> Input
>>>>
>>>> This library is gone with K3.  Instead, they recommend working with $_GET,
>>>> $_POST, and $_SERVER directly.  Handling unset and default values can be
>>>> handled using Arr as:
>>>> Arr::get($_POST, "foo", "default foo");
>>>>
>>>> This part is pretty straightforward.  The only catch is that they need
>>>> cleaning first.
>>>>
>>>> It was recommended to use Security::xss_clean() with K3.0, but that's
>>>> since disappeared.  I'm looking for the best alternative, which shouldn't be
>>>> too hard to find, but think the bigger question is this: *where* do we do
>>>> this so nobody downstream has to think about it?
>>>> - bootstrap?
>>>> - an extension of Route?
>>>> - an extension of Controller?
>>>> - somewhere else?
>>>
>>>
>>> The XSS cleaner in K2 was questionable at best.  We augmented it with our
>>> own tweaks:
>>> https://github.com/gallery/gallery3/blob/master/system/libraries/Input.php#L306
>>>
>>> Even that is pretty crappy and I don't trust it.  We really need to be very
>>> suspicious of any content in the database and treat it very carefully when
>>> we use it.  For K2 we overloaded html::clean to make it safe:
>>> https://github.com/gallery/gallery3/blob/master/modules/gallery/helpers/MY_html.php#L32
>>>
>>> Then we used that (and all the other cleaning methods) everywhere.  My
>>> assumption has always been that with html::clean AND the XSS cleaning code
>>> we don't have to catch every single vector of attack, we've got enough
>>> defense-in-depth to make hacking hard.
>>>
>>> The downside, though is that we are always possibly cleaning away something
>>> that we really care about.  And cleaning is expensive.  :-/
>>>
>>> So options:
>>> 1) Don't bother cleaning at all
>>> 2) Create our own simple API for getting GET/POST/SERVER variables that does
>>> cleaning and handles defaults
>>> 3) Clean everything up front
>>>
>>> I'm in favor of #2 with an API like this:
>>>
>>> Input::GET("foo", "default foo");
>>> Input::GET("foo", "default foo", Input::RAW);  // don't sanitize
>>> Input::POST("foo", "default foo");
>>> Input::SERVER("foo", "default foo");
>>>
>>
>> Me too.  I've been taking a look at how we'd integrate the standalone
>> HTMLPurifier, and it looks like we can be a bit faster if we use a
>> singleton instance() function.  With this, we could *exactly* copy the
>> old API (or at least the parts that Gallery used).  The instance would
>> initialize the class, fire up and configure HTMLPurifier once, and set
>> up an empty cache of purified entries.  Then, we purify them on demand
>> and stick them in the cache so we shouldn't need to do it twice.  I
>> haven't yet figured out if it makes sense to do them one superglobal
>> array at a time or one entry at a time, but certainly it doesn't make
>> sense to do all at once (for example, one might load Input to look for
>> $_POST but never care about $_SERVER).
>>
>> I also like the Input::RAW idea.  It should come with a big
>> disclaimer, and really shouldn't be needed too often since we can
>> accept HTML markup, but it's still nice to have.
>>
>>> This has some advantages:
>>> 1) It's close to what we have now so conversion is easier
>>> 2) We can lazy-clean the input for efficiency
>>> 3) We can use Input::RAW as a findable symbol for all places where we work
>>> with raw data (set the RAW constant to some wacky value so that API users
>>> have to use the constant)
>>> 4) profit?
>>
>> Yes, yes, yes, and hell yes.  :-)
>>
>>
>>> Regarding HTMLPurifier - it added so much weigh to the installed code size
>>> that I was loathe to make it a default module.  Unless it's gotten a lot
>>> smaller, I suspect I'll still feel the same way...
>>
>> The standalone version you found seems to be not *too* bad.  It's <1MB
>> (<300k compressed) and seems like it was designed to play nice with
>> caches.
>>
>> My feeling is this: we should build the Input class and make it use
>> HTMLPurifier.  Then, once we're back in fully-running state, we can
>> run some benchmarks to see how heavy it really is.
>>
>> Thoughts?
>>
>> Take care,
>> Shad
>>
>>
>>>
>>> thoughts?
>>> -Bharat
>>>

------------------------------------------------------------------------------
Minimize network downtime and maximize team effectiveness.
Reduce network management and security costs.Learn how to hire 
the most talented Cisco Certified professionals. Visit the 
Employer Resources Portal
http://www.cisco.com/web/learning/employer_resources/index.html
__[ g a l l e r y - d e v e l ]_________________________

[ list info/archive --> http://gallery.sf.net/lists.php ]
[ gallery info/FAQ/download --> http://gallery.sf.net ]