Security:Scattered Security Checks



This is a proposal for a security model for Gecko. The key idea of this proposal is that security checks are performed immediately before doing a possibly-unsafe operation and that the subject principal for any actions is tracked throughout the codebase. Contrast this with the proposal at Security:Security_Checks_In_Glue.

This proposal does not address Python and other languages, but they are identical to C++ for purposes of this proposal.

Conceptual description

In this model, security checks are performed as needed in the code flow. For example, before setting some internal member of a class, we could check whether the caller is allowed to set it. This model requires keeping track, throughout our code, of who "the caller" is. This is more or less the current model, except we pretty much completely screw it up.

Pros and cons

The main benefit of this model is that at first glance it promises more conservative behavior than the Security:Security_Checks_In_Glue model. The failure cases are mostly cases where permission is denied when it should be granted.

There obvious drawback is that you have to keep track of who "the caller" is (the subject principal) at all times. There are several parts to this. First of all, the subject principal needs to be propagated through various parts of the code. Second, the current subject principal needs to be switched as needed (e.g. when code is no longer acting on behalf of the current subject). Clearly defining when to switch principals is hard; it seems like it would be easy to have errors both of omission (not switching principal when one should; breaks web compat) and commission (switching principal when one should not; causes security bugs).

I question whether the perceived benefit of this model is in fact realized, given the complexity of changing the subject principal at just the right time.

Implementation notes

Implementing this proposal properly would probably involve the following steps:

  1. Decouple the concept of "subject principal" from the JS context stack. Have a separate principal stack.
  2. Push the current JS subject principal on this stack when calling out of JS into C++ (via XPConnect or any C++-implemented JS getter/setter/function).
  3. When posting an event to a thread event queue, save the current subject principal with the event (in the event queue code). When dispatching the event, push that principal on the stack. This ensures that the principal for an action is the same whether the action is taken sync or async. The other option is to make all event consumers do this manually, and that seems really suboptimal.
  4. Handle timers like events.
  5. Same for all other cross-thread communication.
  6. Necko channel callbacks (onStartRequest, onDataAvailable, onStopRequest) would require the necko channel to push its principal (either the owner, or the principal derived from the URI) before making callback. The other option is to have all nsIRequestObserver implementations do this themselves. The goal here is that when parsing a document the subject principal is that document's principal.
  7. Change the handling of chrome JS such that it does NOT affect the principal stack. Calling foo() from bar() from baz() should behave identically whether bar() is implemented in chrome JS or in C++ — in both cases foo() should see the same subject principal as baz().
  8. Implement a way for both C++ and chrome JS to push a different principal (e.g. the system principal) when they are no longer doing work "on behalf" of someone.
  9. Audit our codebase to see where else we need to change the current principal.

Note that the principal stack would probably need to hold nsISupports given that we'd need necko and xpcom support for it. It should live in xpcom.