2007-12-12
A more abstract view of the generalized open() issue
Part of the generalized open() problem is that
such generalized open()s silently cross what I will call 'security
contexts'. Here a security context is both what you can do and where
the data is coming from; read versus write versus run a pipe, local
files versus remote URLs, and so on.
When something is security sensitive (and open() is here), it should
either be explicit about what security context it is operating in,
or it should at least be explicit about the fact that it is crossing
them. In other words, this is one of the places where you deliberately
want to break abstractions, so that there is no possibility of code
accidentally operating in the wrong security context.
As a corollary, namespaces that cross security contexts are a danger sign. Firefox has had problems with because its internal files and resources are partly named using URIs in the chrome:// scheme, so lots of things that accept URLs have to be blocked from using them (and sometimes stuff using chrome:// URLs had to be blocked from using other sorts of URLs). And of course there's the great fun to be had because browsers accept 'javascript:' as a scheme in URLs.
(I suspect that these things sneak into languages partly because the language designers did not see them as crossing security boundaries, and in part I think this may be because security boundaries sometimes only become obvious with painful experience. After all, no one deliberately puts security holes in their language or their library.)
Implicit generalized open()'s are dangerous
A number of languages have open() functions that are generalized by
default; they open more than plain files, sometimes far more. Such
functions are dangerous bear traps lying in wait for the unwary and
insufficiently paranoid, because what the programmer thinks is a simple
file open may be something very different and more dangerous. In
turn, this means that all an attacker needs to do is find some way to supply a 'file name' that your program
will try to open.
There's two core problems: the generality is implicit, not explicit, and
it is in what you use for a common case that neither needs nor wants the
generality. This means that the full potential of what the code can do
is not immediately obvious when you read (or write) 'open(filename)',
and that you will be writing it a lot. Adding to the problem is that
this is a hard mistake to notice. Your code works; it just has extra
'features' that you don't actually want.
(Even if there is a way to be explicit, people are lazy and sooner or later someone is going to use the implicit way when they shouldn't have.)
As you may have gathered, I don't particularly like such open()s; I
feel that they are a prime example of an error-prone interface.