[pmwiki-users] security documentation for recipes

Patrick R. Michaud pmichaud at pobox.com
Fri Mar 7 09:23:53 CST 2008

On Fri, Mar 07, 2008 at 03:28:29PM +0100, Peter & Melodye Bowers wrote:
> > > Would it be helpful for other developers (or is it just me?) to have a
> > > simple wrapper as the "official" way to write to a page?  Something like
> > > UpdateAuthPage() or something similar?
> > 
> > Possibly, but there are a couple of issues that need to be resolved
> > first.  For example, presumably a call to UpdateAuthPage() would
> > look like
> > 
> >     UpdateAuthPage($pagename, $page);
> I would have thought
>        UpdateAuthPage($pagename, $auth, $oldpage, $newpage);

If we require the caller to also provide $oldpage, then there
doesn't seem to be much point to having an UpdateAuthPage() function,
because the caller has to use RetrieveAuthPage to get the contents
of $oldpage and $newpage anyway (and can easily check permissions

We could do something like

    UpdateAuthPage($pagename, $newpage, $auth='edit', $oldpage=NULL)

such that the caller can provide $oldpage if they have it, but either
way UpdateAuthPage is still going to want to call RetrieveAuthPage
on its own to check the permissions, so there doesn't seem to be
much point to having $oldpage for the caller.  Or, put another way,
if the caller is already getting the value of $oldpage, then
they ought to be able to call UpdatePage() directly and not need
to use UpdateAuthPage() at all.

> The $auth is probably unnecessary - is
> there any time you would want to require auth other than "write"?  

Er, "edit".  But yes, you're correct, it should probably read

    UpdateAuthPage($pagename, $newpage, $auth = 'edit')

so that $auth automatically gets a value of 'edit' if not
supplied by the caller.  Then the typical call becomes

    $result = UpdateAuthPage($pagename, $newpage);

but someone who wants to use a different authorization can easily do so.

> Basically I'm not thinking UpdateAuthPage() would be used any
> different from UpdatePage() (i.e., still has the $oldpage parameter) other
> than making sure that authorizations were correctly respected - an
> "authorized API", if you will, to the world outside core...

Ah, if that's the case, then a separate function isn't really needed --
it can just be a test added to the existing $EditFunctions array.  For

  function PostRequiresEdit($pagename, &$old, &$new) {
    global $EnablePost;
    if (!@$old['=auth']['edit']) $EnablePost = false;
  array_unshift($EditFunctions, 'PostRequiresEdit');

> > Also, what should UpdateAuthPage() do if the visitor doesn't
> > have authorization -- should it automatically prompt for a form,
> > Abort, return a false value to the caller, or... ?  (We could provide
> > a $prompt argument to UpdateAuthPage() as well.)
> Here I go off on what may be a total rabbit trail...  Is there any reason
> why authorization is done on a page by itself rather than using some kind of
> popup dialog box?  

Basically because it's not the way the web works.   Most sites that
do things with a popup box are using HTTP authentication, where the
browser provides the popup.  PmWiki supports this (in fact, it used to
be the PmWiki default in 1.0), but over time we've run into lots of
situations where HTTP authentication didn't work or wasn't appropriate,
which is why we have the form-based method we use now.
>    if (!CondAuth($pagename, $auth) and !AskAuth($pagename, $auth))
>       return false;

Note that it's not really possible (short of using something like AJAX)
to have a function like AskAuth() that both prompts the visitor for
a password and returns a value to the calling script.  In the standard
web model, whenever you ask the user for something, the response comes
back as a separate HTTP request (i.e., a separate execution of pmwiki.php).

> > In general if you simply want to check authorization and don't care
> > about the page contents, it's better to use CondAuth() instead of
> > RetrieveAuthPage(), just in case we modify the security model in the
> > future.  However, it's not any quicker, since CondAuth() itself uses
> > RetrieveAuthPage() to check the permissions.
> Curious.  I guess there's some technical reason why CondAuth() calls
> RetrieveAuthPage() rather than checking $page['=auth']['edit'] (as below) or
> something similar...?  

All CondAuth() is provided is a pagename and an authorization level to check.
The page being requested might not have been read yet (i.e., the $page
array doesn't exist for the specified pagename).

> It seems from a performance perspective like a check
> of permissions shouldn't require a full read of the file, but I've never
> gotten that deep into the code to see how it happens so I may be completely
> off base here...

For a variety of design reasons, PmWiki stores authorization information
for each page directly into its pagefile.  This is so that each page carries
its security information along with it directly, instead of having to
synchronize authorization details with a separate location or database.
So yes, checking authorization on a page does require reading the page file.
It's not a full read, however -- the page history isn't read unless it's
going to be needed.

> > Another way of checking that doesn't require an extra read beyond
> > the first is to check $page['=auth']['edit'], if non-zero, then
> > the visitor has 'edit' permission on the page.
> Is this a "safe" bet for the future if the security model changes?  Or
> better to use CondAuth()?  I'm guessing the latter?

For now, either approach works and isn't likely to break anytime soon.  
For the very long term, it would be better to use CondAuth().


More information about the pmwiki-users mailing list