For those of you who have not been following python-dev, I have been working on implementing sandboxing for Python. At the moment I have a design doc in svn (in the bcannon-sandboxing branch and called sandboxing_design_doc.txt) that is an API, high levelish document.
It would be more complete if I could decide on how to implement the details. =) I was originally going with a design that followed the standard permissions-based approach to security. The interpreter would be hacked so that checks as to whether the running interpreter was sandboxed or not would decide as to whether a certain ability was allowed. For instance, hacking file() so that it's constructor was turned off and then change open() so that it was a factory function that checked whether a path was allowed to be accessed by the interpreter by checking the path against a list of "blessed" paths.
But then I was introduced to object-capabilities. In this security model authorization is the key point; if you don't have access to something, then you just can't use it. It makes things much simpler since if you want to disallow something, you either don't allow it, or check the argument for validity before making a call. So open() would no longer be changed to check on a per-interpreter basis, but instead you would set open() in the sandboxed interpreter to a delegate/proxy/whatever-term-you-want that checked the argument was okay, and then pass to the unrestricted open() the call.
And therein lies the subtle, key point permissions vs. authority. I prefer to think of it as security based on who-I-am compared to what-I-have. The open() example is perfect illustration. For who-I-am security, I have to hack open() itself directly and provide an identity check to see who is making the call along with then checking the argument to make sure it is allowed for that identity.
But with object-capabilities, you cut out that identity check. By setting open() itself to just a delegate, I don't have to hack open() directly but just provide a function that can hide a reference to open() and just call it as long as the arguments to open() pass the argument check by the delegate.
But there is the challenge. How can one make sure the true open() does not make it into a sandboxed interpreter? Unintentially letting a reference get past your security model destroys you in object-capabilities. You must have a strong perimeter defence or the whole thing is shot. And Python does not make that easy.
But it isn't like who-I-am security is perfect. If an identity check incorrectly identifies someone your security model is blown as well. The other drawback is that you can't share objects between the security perimeter without the security protections changing. For instance, if I have an object Foo that for me can open files but for you can't then if I want to give you the ability to use object Foo to open the file I can't without you either changing your identity or escalating your abilities.
But with object-capabilities I can just give you the reference to the object and not worry about you getting other rights. Yes, there is the worry of you passing out the object I gave you, so you need to trust the code you are communicating with, but you have to do that with who-I-am security as well anyway. You can't ever blindly trust code without either examining it or trusting who gave it to you regardless of security model.
Anyway, I have been debating these two approaches in my head as of late. I am hoping to put this issue to rest come some time next week. I have already started implementing for who-I-am security, although I think I will probably redesign it to minimize the amount of time I have multiple interpreters running to keep things simpler.