You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by "Robert S. Thau" <rs...@ai.mit.edu> on 1996/06/12 23:12:22 UTC

Thoughts on authoring support...

I've been playing with the notion of doing authoring support for
authoring tools which have the decency to use an HTTP PUT to upload
their content, and I've been doing a bit of experimenting.  (FWIW,
these include at *least* Netscape Gold and GNNpress --- my play-test
PUT-handler has successfully uploaded documents from both, though
Netscape Gold, at least on Linux, insists of having a copy of whatever
you're editing on the client's local filesystem for no obvious reason,
and doesn't send Content-type with the PUT).

Anyway, what I'm trying to find is some sort of a security policy
which we could be comfortable describing to people and supporting in
our code.  (I'm not saying we ever *will* find such a policy, but it
would be desirable ;-).  This note probably winds up sounding a lot
more formal than I meant it to be, BTW --- when I master the art of
making a tentative technical spec *sound* breezy and tentative, you
all will be the first to know, I'm sure.  At any rate, take these
for preliminary thoughts on something that I'm likely to throw
together sometime reasonably soon, and just tell me why you all think
I shouldn't...

For the rest of this, I'll be presuming that the back-end support is
provided by means of a PUT-Script... so, here are the sort of
guarantees I'd like.  First off, a few restrictions on the PUT-handler
itself, to limit the damage if the rest of the system runs amok:

   1) PUTs must be specifically allowed in a region of the filesystem;
      the PUT-handler must do enough checks to make sure that a request
      to handle a PUT which claims that client's URI maps to something
      like /etc/passwd will fail regardless of the credentials claimed
      for it.

   2) There should be some way of tracking what the PUT-handler has done.

   3) If a PUT overwrites something that was already there, the old
      version should not be destroyed.

   4) People should not be able to upload anything as dangerous
      as a CGI script (if you want to author the things, in my
      experience, you need access to the machine for debugging
      anyway).

You can deal with restrictions 1) and 2) in a body by requiring a log
file with a specific name to exist in the directory containing the
target file, or perhaps one of its superiors, and requiring that file
to be writable by the person on whose behalf the PUT-handler believes
it is performing the PUT.  (To keep that from being totally vacuous,
PUTs on behalf of root are not to be done regardless, of course).

The "... one of its superiors part" is the dicey bit here --- it's an
attempt to get around requiring people to manually put a .HTTP.PUT.LOG
in each and every a subdirectory of their ~/public_html before they
could edit anything in it.  Still, this is dicey.  In particular, it
opens up the possibility of attacks ins which Mallet, trying to
overwrite Alice's stuff, plants a symlink from inside a directory of
his own, containing a .HTTP.PUT.LOG, to Alice's content.  So, at the
very least, you need to be absolutely certain that you're climbing the
*real* filesystem hierarchy to get to the .HTTP.PUT.LOG, and not
something which might have been artificially created with symlinks.
And if that confuses the users too much, it may be just better to
ban it outright, and require the damn things everyplace.  I'm still
not sure.
  
On to requirement (3).  The right answer to this is decent version
control, but not everyone has RCS, and I'm not sure I'd trust it not
to gag on GIFs anyway.  What my current code uses, therefore, is the
sleaziest version-control system in existence, file version numbers
(using the Emacs convention).

Finally, wrt requirement (4), I'm again doing things the lousy way
--- only allow uploads of files with specific, specified extensions
(in a table at the start of the PUT-handler); if the client is good
enough to supply content-type, check that that's consistent with the
content-type the server would be expected to assign.  On the upside,
this also has the salutary effect of keeping people from doing a PUT
to overwrite an old version of something; on the downside, it's
cheezy as all hell.

NB the above are the bare minimum protections that I'd still want to
have from the PUT-handler itself, even if the web server were
completely compromised (e.g., by some sort of buffer overrun bug).
There are more protections that I'd want to have, some listed below,
which rely on the environment variables ordinarily set by the server
having their proper values.  (The base case of this is having the
script be sure that it was, in fact, run by a web server, which has
been the topic of a heck of a lot of discussion here.  I've got a
variant of the previously discussed "write-pids-in-files" approach
which I haven't found any holes in yet, which I'll discuss below;
for now, let's just hope we've got *something*).

So, anyway, what do we want?  More specifically, what do *I* want?
Well...

   5) Unauthenticated PUTs should be flatly disallowed.  

   6) A PUT should only be done on behalf of a particular user if the
      server has somehow authenticated the REMOTE_USER as being a
      legit representative of that user.  (NB we may not want a 1-1
      mapping of web ID's to unix accounts for the same reason we
      don't want people using the same password all over).

(5) is simple, and I hope, self-justifying; mod_auth_anon provides
some kind of escape hatch for anyone foolish enough to *want* to allow
any fool in the world to scribble all over their space.  The
interesting question here is (6) --- how do you tell which
REMOTE_USERs are allowed to PUT where?

One notion is to look in the controlling .HTTP.PUT.LOG for guidance
(have a line at the top which says, in effect, "I authorize
REMOTE_USERs x, y, and z to edit my stuff").  The question then
becomes, what do you have to know about the file itself in order for
assertions of this form to be believable?

The minimum, of course, is that it be owned by the same user whose
file and directory are about to be altered by the PUT, and that the
file and directory containing the .HTTP.PUT.LOG itself not be writable
by anyone else.  I'm thinking about just letting it go at that (modulo
the issues discussed above about associating any using .HTTP.PUT.LOG
to govern access to a directory other than the one that immediately
contains it).

Note that this is actually more than we require of .htaccess files ---
currently anybody who wants to create a .htaccess file which is
publically writable (and either trusts other people on the box not to
screw them, or just doesn't care), can do so.  An argument could be
made that since we allow people to run wide open with .htaccess, we
should do so with PUT control as well, for consistency.  A different
argument could be made to the effect that since the potential damage
from rogue PUTs (if not from data loss, than from public embarassment
over bogus content) is greater, and that controls should be tighter
accordingly.

(I should say a few words about giveaway chowns here.  I'm assuming
that on a system with giveaway chowns, it's perfectly OK for Mallet to
create a hierarchy of files and directories all owned by Alice, and
give remote users of his choice permission to edit the stuff, so long
as this doesn't somehow indirectly give either him or them the power
to edit anything which was actually created by the *real* Alice.  This
will be the case so long as Mallet can't create a .HTTP.PUT.LOG in any
of the real Alice's directories or their superiors.  I feel
comfortable saying that that's the chance you take if you leave a
directory publically writable with guys like Mallet skulking around,
but reasonable people may differ.  In any case, it's worth noting that
if there is a publically writable directory further up the hierarchy,
Mallet can simply replace the stuff he's going after directly, without
going through the PUT handler at all).

In any case, a more critical requirement is this:

   7) The PUT handler should take measures to insure that it is
      processing a direct PUT request on the resource named by
      REDIRECT_URL, whose filename is PATH_INFO_TRANSLATED.  It should
      not respond if invoked directly:

          /cgi-bin/PUT-handler/path/to/victims/stuff

      ... which would potentially bypass the access controls on the
      /path/to/victims/stuff.  For the same reason, it should just
      report an error and croak if it is invoked by an internal
      redirect from a CGI script (which might have been written by
      a rogue user attempting to subvert another user's access
      controls). 

The obvious way to check for these, assuming the PUT-handler is a CGI
script invoked via mod_actions, is to look in the environment,
making sure that REDIRECT_URL exists, and that REDIRECT_SCRIPT_NAME
does not (nor any REDIRECT_REDIRECT stuff).  The question is, whether
there's something tricky that would sneak by these checks.

That assumes, of course, that the server has not been compromised
internally.  In fact, everything after (4) up there has implicitly
assumed:

    8) The PUT handler's environment was put together by a web server
       acting in accord with its own security policy, and the
       environment variables accurately reflect the processing of some
       real web request (including authentication).

...which gets us back to where this whole thing started ages ago.

One of the last ideas floated about this was keeping a log of the pids
of all relevant processes somewhere on the system, and having the
child process consult this pid-log, or pid-tree, or whatever.  Where
this goes south is that a given process can't necessarily learn a
whole lot about its putative parent given only the pid, which makes
the parent pid of extremely limited use as a validator.

So, I decided to look at things from another angle --- starting at the
log files (of all places).  The transfer logs in particular have the
interesting characteristic that the web server processes themselves
can write to them (running as 'www'), but CGI scripts they invoke
can't.  This is because their common ancestor opened them as root,
before they lost privilege.  As long as they keep this descriptor
open, they can write to the logs themselves.  However, when they
exec() off a CGI script, they close it --- and a rogue script can't
get it back.

This suggests that we could set up a "child-pid" file which has the
same characteristics (servers can write it, other processes running
as 'www' can't), and write authenticating information into it which
a PUT-handler, or other paranoid CGI script, could use to authenticate
that it was in fact spawned off by a web server and not some other
process with the right uid.

One choice for this authenticating information is two items: one being
the pid of the child, and the other being a bit which is initially
set, and which is cleared by the server before wait()ing for the child
in question.  The reason for having this bit is to keep Mallet from
defeating the check by firing off a rogue program which would
continually fork until it got the "right" pid, and then run the
PUT-wrapper --- the pid cannot be reused until the server has wait()ed
for the child, and if the valid bit has been cleared first, the attack
must fail.  Ben helped me think up this way of dealing with that
little detail, BTW.

To summarize, here's what a user would see, assuming that I got all
that right (which I probably haven't).  To enable authoring on a
directory tree which they owned, a user would have to create a
.HTTP.PUT.LOG (or maybe .HTTP.PUT.CONTROL) file at the root of the
directory hierarchy they want remote authoring for, saying which
particular http_authenticated users are allowed to PUT stuff.  (It can
even be "anonymous", if mod_auth_anon is installed, it's just gotta be
*something*).

Once they do this, (5)-(8) mean that unless somebody snoops their
password, or the server *itself* is compromised, they can have
reasonable confidence that other people won't go screwing with their
stuff.  (All of this presumes that you believe my checks do achieve
their goals).  Even if the server or their password is compromised,
(1)-(4) offer some assurance that they'll be able to figure out what
the heck happened, and back out to a reasonable state.  (Note that
it's fairly crucial that (1)-(4) give you some basic level of sanity
even without trusting the web server --- a large, messy piece of
software even before you consider the mischief that a third party
module, particularly a maliciously constructed one, could wreak).

Anyway, those are some half-baked thoughts on the matter.  Feel free
to throw brickbats at it all, and I guess I'll come back tomorrow to
see if anything's left.  (I'm also starting to thing about remote
administration by POSTing to something which would write .htaccess
files, but that's even scarier than this, and besides, I've written
enough of this stuff for one day.  Yikes!).

rst