Re: HTMLPurifier and the "Input" class

Shad Laws <shad-xpYdmXCiSuZWk0Htik3J/[email protected]> Sun, 7 Apr 2013 10:04:09 +0200
Newsgroups gmane.comp.web.gallery.devel
Message-ID <CA+z51A4A6coOObfFSuWDdO0VOJ=zu4Qn7zkD0OUK0EH8x4sUeQ@mail.gmail.com>
Hey Bharat,

A thought on Gallery::ready() - it seems like it'd be a lot more
straightforward if it had access to the request.  In addition to the
reasons you give below, it also calls Request::user_agent().

Two other possibilities:
- build the request, call Gallery::ready(), then execute the request
- make Gallery_Controller() that overrides Kohana_Controller() and
adds a before() function that calls Gallery::ready()

Thoughts?

Take care,
Shad

On 6 April 2013 04:57, Bharat Mediratta <[email protected]> wrote:
>
> Before this email I had mocked up an Input API and have managed to plumb a
> line all the way through to successfully rendering a minimal controller
> (WOOT!).  But I went back and changed it over to using Request::$current
> instead.  This causes a problem in that we do some mechanics in
> Gallery::ready() before the request is created.  The main thing I hit is
> that Gallery_Hook_GalleryEvent::gallery_ready() calls Theme::load_themes
> which:
>
> 1) Cares about $_GET["kohana_uri"] if there's no $_SERVER["PATH_INFO"].  We
> need this to work when we're using mod_rewrite (see the mappings at the
> bottom of gallery3/.htaccess)
>
> 2) Cares about $_GET["theme"] which is the mechanism we use to let admins
> preview a theme
>
> The request isn't built until we do it at the very end of index.php, right
> after bootstrap.  Any thoughts on how to play this?  I'd rather not make
> exceptions to our security flow and use $_GET directly...
>
> -Bharat
>
>
>
> On Fri, Apr 5, 2013 at 10:30 AM, Bharat Mediratta <[email protected]>
> wrote:
>>
>>
>> Outstanding research, Shad.  TL;DR: I agree with you.  Notes inline below.
>>
>>
>> On Fri, Apr 5, 2013 at 8:29 AM, 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.
>>
>>
>> URL::query is a somewhat dangerous function because it allows parameter
>> pollution attacks.  Ie, if you go to /index.php?action=logout and we use
>> URL::query to generate any new urls on the page (which is sometimes what
>> things like the paginator do) then you trick the user into clicking a URL
>> which has an extra bad parameter in it AND that url might have a CSRF token
>> in it so it might get accepted by the server.  Just a thought.
>>
>>>
>>>
>>> ---
>>>
>>> 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
>>
>>
>> So this would mean that the superglobals would contain the raw value?
>> Would it be safer for us to duplicate the superglobals into $_RAW_GET for
>> example then clean it into $_GET and just use the 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.
>>
>>
>> I'd prefer to do this processing earlier in the bootstrap so that it's
>> very clear when this is happening and it happens before we load modules
>> (because in K3 when you load modules they can execute code in their init.php
>> file.
>>
>>> - (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.
>>
>>
>> That is probably a good idea.  Or if we just clean the values in $_GET
>> then it's not necessary to override URL (but it still doesn't protect us
>> from pollution attacks).
>>
>>>
>>>
>>> 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 :-)
>>
>>
>> So do K3 and Formo use the Request class or the superglobals?  I guess
>> that dictates what we do as well.  We should stick to their convention as
>> best as possible.
>>
>> Either way - this looks good to me.  Get the code in and let's start using
>> it!
>>
>>>
>>>
>>> ---
>>>
>>> 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 ]
>>>
>>
>

------------------------------------------------------------------------------
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 ]