(サンドボックス化)

Description

How to Plone guarantees that a site user does not escape his/her given privileges

はじめに

Plone has two modes of traversing and code execution

  • Unrestricted: Python code is executed normally and the code can access the full Zope application server environment. This includes other site instances too.
  • Restricted: Traversing and function calls go through AccessControl / RestrictedPython / zope.security checks. and Every function call and attribute access is evaluated against ClassSecurityInfo and ModuleSecurityInfo.

Restricted execution is enabled:

  • When incoming HTTP requests are traversed (everything you can type to your browser address bar)
  • Page templates: everything you put in tal:content, tal:condition, etc.
  • Script (Python) code is executed (plone_skins layer Python scripts and old style form management)

Restricted execution has some performance penalty.

Restricted execution special cases

Zope traversing mechanism does not expose

  • Functions without docstring (the “”” comment at the beginning of the function)
  • Functions whose name begins with underscore (“_”-character)

Testing RestrictedPython

RestrictedPython code is problematic, because RestrictedPython hardening is done on Abstract Syntax Tree level and effectively means all evaluated code must be available in the source code form. This makes testing RestrictedPython code little difficult.

Below are few useful unit test functions:

# Zope security imports
from AccessControl import getSecurityManager
from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager
from AccessControl.SecurityManager import setSecurityPolicy
from AccessControl import ZopeGuards
from AccessControl.ZopeGuards import guarded_getattr, get_safe_globals, safe_builtins
from AccessControl.ImplPython import ZopeSecurityPolicy
from AccessControl import Unauthorized

# Restricted Python imports
from RestrictedPython import compile_restricted
from RestrictedPython.SafeMapping import SafeMapping

def _execUntrusted(self, debug, function_body, **kwargs):
    """ Sets up a sandboxed Python environment with Zope security in place.

    Calls func() in an sandboxed environment. The security mechanism
    should catch all unauthorized function calls (declared
    with a class SecurityManager).

    Security is effective only inside the function itself -
    The function security declarations themselves are ignored.

    @param func: Function object
    @param args: Parameters delivered to func
    @param kwargs: Parameters delivered to func
    @param debug: If True, break into pdb debugger just before evaluation
    @return: Function return value
    """

    # Create global variable environment for the sandbox
    globals = get_safe_globals()
    globals['__builtins__'] = safe_builtins

    # Zope seems to have some hacks with guaded_getattr.
    # guarded_getattr is used to check the permission when the
    # object is being traversed in the restricted code.
    # E.g. this controls function call permissions.
    from AccessControl.ImplPython import guarded_getattr as guarded_getattr_safe
    globals['_getattr_'] = guarded_getattr_safe
    #globals['getattr'] = guarded_getattr_safe
    #globals['guarded_getattr'] = guarded_getattr_safe


    globals.update(kwargs)

    # Our magic code

    # The following will compile the parsed Python code
    # and applies a special AST mutator
    # which will proxy __getattr__ and function calls
    # through guarded_getattr
    code = compile_restricted(function_body, "<string>", "eval")

    # Here is a good place to break in
    # if you need to do some ugly permission debugging
    if debug:
        pass # go pdb here

    return eval(code, globals)

def execUntrusted(self, func, **kwargs):
    """ Sets up a sandboxed Python environment with Zope security in place. """
    return self._execUntrusted(False, func, **kwargs)

def execUntrustedDebug(self, func, **kwargs):
    """ Sets up a sandboxed Python debug environment with Zope security in place. """
    return self._execUntrusted(True, func, **kwargs)

def assertUnauthorized(self, func, **kwargs):
    """ Check that calling func with currently effective roles will raise Unauthroized error. """
    try:
        self.execUntrusted(func, **kwargs)
    except Unauthorized, e:
        return

    raise AssertionError, 'Unauthorized exception was expected'

def test_xxx(self):
    # Run RestrictedPython in unit test code
    # myCustomUserCreationFunction() is view/Python script/method you must call in the restricted mode
    self.execUntrusted('portal.myCustomUserCreationFunction(username="national_coordinator", email="nationalcoordinator@redinnovation.com")', portal=self.portal)

目次

前のトピックへ

(動的ロール)

次のトピックへ

(テンプレート、CSS と Javascript)

このページ