diff options
-rw-r--r-- | docs/example.rst | 227 | ||||
-rw-r--r-- | docs/index.rst | 8 | ||||
-rw-r--r-- | docs/narr.rst | 543 | ||||
-rw-r--r-- | docs/proxy.rst | 220 | ||||
-rw-r--r-- | src/zope/security/interfaces.py | 3 |
5 files changed, 508 insertions, 493 deletions
diff --git a/docs/example.rst b/docs/example.rst new file mode 100644 index 0000000..af5f6de --- /dev/null +++ b/docs/example.rst @@ -0,0 +1,227 @@ +========= + Example +========= + +As an example we take a look at constructing a multi-agent distributed system, +and then adding a security layer using the Zope security model onto it. + +Scenario +======== + +Our agent simulation consists of autonomous agents that live in various agent +homes/sandboxes and perform actions that access services available at their +current home. Agents carry around authentication tokens which signify their +level of access within any given home. Additionally agents attempt to migrate +from home to home randomly. + +The agent simulation was constructed separately from any security aspects. +Now we want to define and integrate a security model into the simulation. The +full code for the simulation and the security model is available separately; +we present only relevant code snippets here for illustration as we go through +the implementation process. + +For the agent simulation we want to add a security model such that we group +agents into two authentication groups, "norse legends", including the +principals thor, odin, and loki, and "greek men", including prometheus, +archimedes, and thucydides. + +We associate permissions with access to services and homes. We differentiate +the homes such that certain authentication groups only have access to services +or the home itself based on the local settings of the home in which they +reside. + +We define the homes/sandboxes + +- origin - all agents start here, and have access to all + services here. + +- valhalla - only agents in the authentication group 'norse + legend' can reside here. + +- jail - all agents can come here, but only 'norse legend's + can leave or access services. + + +Process +======= + +Loosely we define a process for implementing this security model + +- mapping permissions onto actions + +- mapping authentication tokens onto permissions + +- implementing checkers and security policies that use our + authentication tokens and permissions. + +- binding checkers to our simulation classes + +- inserting the hooks into the original simulation code to add + proxy wrappers to automatically check security. + +- inserting hooks into the original simulation to register the + agents as the active principal in an interaction. + + +Defining a Permission Model +=========================== + +We define the following permissions:: + + NotAllowed = 'Not Allowed' + Public = Checker.CheckerPublic + TransportAgent = 'Transport Agent' + AccessServices = 'Access Services' + AccessAgents = 'Access Agents' + AccessTimeService = 'Access Time Services' + AccessAgentService = 'Access Agent Service' + AccessHomeService = 'Access Home Service' + +and create a dictionary database mapping homes to authentication groups which +are linked to associated permissions. + + +Defining and Binding Checkers +============================= + +:class:`Checkers <zope.security.checker.Checker>` are the foundational +unit for the security framework. They define what attributes can be +accessed or set on a given instance. They can be used implicitly via +Proxy objects, to guard all attribute access automatically or +explicitly to check a given access for an operation. + +Checker construction expects two functions or dictionaries, one is +used to map attribute names to permissions for attribute access and +another to do the same for setting attributes. + +We use the following checker factory function:: + + def PermissionMapChecker(permissions_map={}, + setattr_permission_func=NoSetAttr): + res = {} + for k,v in permissions_map.items(): + for iv in v: + res[iv]=k + return checker.Checker(res.get, setattr_permission_func) + + time_service_checker = PermissionMapChecker( + # permission : [methods] + {'AccessTimeService':['getTime']} + ) + +with the NoSetAttr function defined as a lambda which always return the +permission ``NotAllowed``. + +To bind the checkers to the simulation classes we :func:`register +<zope.security.checker.defineChecker>` our checkers with the security +model's global checker registry:: + + import sandbox_simulation + from zope.security.checker import defineChecker + defineChecker(sandbox_simulation.TimeService, time_service_checker) + + +Defining a Security Policy +========================== + +We implement our security policy such that it checks the current agent's +authentication token against the given permission in the home of the object +being accessed. (We extend a simple policy provided by the framework +that will track participations for us):: + + from zope.security.simplepolicies import ParanoidSecurityPolicy + + @provider(ISecurityPolicy) + @implementer(IInteraction) + class SimulationSecurityPolicy(ParanoidSecurityPolicy): + + def checkPermission(self, permission, object): + + home = object.getHome() + db = getattr(SimulationSecurityDatabase, home.getId(), None) + + if db is None: + return False + + allowed = db.get('any', ()) + if permission in allowed or ALL in allowed: + return True + + if not self.participations: + return False + for participation in self.participations: + token = participation.principal.getAuthenticationToken() + allowed = db.get(token, ()) + if permission not in allowed: + return False + + return True + +Since an interaction can have more than one principal, we check that *all* of +them are given the necessary permission. This is not really necessary since +we only create interactions with a single active principal. + +There is some additional code present to allow for shortcuts in defining the +permission database when defining permissions for all auth groups and all +permissions. + + +Integration +=========== + +At this point we have implemented our security model, and we need to integrate +it with our simulation model. We do so in three separate steps. + +First we make it such that agents only access homes that are wrapped in a +security proxy. By doing this all access to homes and services (proxies have +proxied return values for their methods) is implicitly guarded by our security +policy. + +The second step is that we want to associate the active agent with the +security context so the security policy will know which agent's authentication +token to validate against. + +The third step is to set our security policy as the default policy for the +Zope security framework. It is possible to create custom security policies at +a finer grained than global, but such is left as an exercise for the reader. + + +Interaction Access +================== + +The :mod:`*default* implementation <zope.security.management>` of the +interaction management interfaces defines interactions on a per thread +basis with a function for an accessor. This model is not appropriate +for all systems, as it restricts one to a single active interaction +per thread at any given moment. Reimplementing the interaction access +methods though is easily doable and is noted here for completeness. + + +Perspectives +============ + +It's important to keep in mind that there is a lot more that is possible using +the security framework than what's been presented here. All of the +interactions are interface based, such that if you need to re-implement the +semantics to suite your application a new implementation of the interface will +be sufficient. Additional possibilities range from restricted interpreters +and dynamic loading of untrusted code to non Zope web application security +systems. Insert imagination here ;-). + + +Zope Perspective +================ + +A Zope3 programmer will never commonly need to interact with the low level +security framework. Zope3 defines a second security package over top the low +level framework and authentication sources and checkers are handled via zcml +registration. Still those developing Zope3 will hopefully find this useful as +an introduction into the underpinnings of the security framework. + + +Authors +======= + +- Kapil Thangavelu <hazmat at objectrealms.net> +- Guido Wesdorp <guido at infrae.com> +- Marius Gedminas <marius at pov.lt> diff --git a/docs/index.rst b/docs/index.rst index ccd5b84..eb3cbf5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,6 +1,6 @@ -==================================== - ``zope.security`` Documentation -==================================== +============================= + zope.security Documentation +============================= Narrative Documentation ======================= @@ -9,6 +9,8 @@ Narrative Documentation :maxdepth: 2 narr + example + proxy hacking API Reference diff --git a/docs/narr.rst b/docs/narr.rst index 4f19fe4..d746fc3 100644 --- a/docs/narr.rst +++ b/docs/narr.rst @@ -1,8 +1,8 @@ -Overview -======== +=========================== + Overview and Introduction +=========================== -Introduction ------------- +.. currentmodule:: zope.security.interfaces The Security framework provides a generic mechanism to implement security policies on Python objects. This introduction provides a tutorial of the @@ -10,22 +10,34 @@ framework explaining concepts, design, and going through sample usage from the perspective of a Python programmer using the framework outside of Zope. Definitions ------------ +=========== Principal -~~~~~~~~~ +--------- -A generalization of a concept of a user. +A generalization of a concept of a :class:`user <IPrincipal>`. Further +specializations include :class:`groups of users <IGroup>` and +principals that :class:`know what groups they belong to +<IGroupAwarePrincipal>`. All of these principals may interact with the system. Permission -~~~~~~~~~~ +---------- A kind of access, i.e. permission to READ vs. permission to WRITE. -Fundamentally the whole security framework is organized around checking -permissions on objects. +Fundamentally the whole security framework is organized around +checking permissions on objects. Permissions are represented (and +checked) as strings, with the exception of :data:`a constant +<zope.security.checker.CheckerPublic>` that has the special meaning of +"public", i.e., no checking needs to be done. + +There are :class:`permission objects <IPermission>` that can be +registered as zope.component utilities for validation, introspection, +and producing :func:`lists of available permissions +<zope.security.permission.PermissionVocabulary>` to help users assign +them to objects. Purpose -------- +======= The security framework's primary purpose is to guard and check access to Python objects. It does this by providing mechanisms for explicit and @@ -44,10 +56,10 @@ it can be substituted with one which doesn't care about principals or interactions at all. Framework Components --------------------- +==================== Low Level Components -~~~~~~~~~~~~~~~~~~~~ +-------------------- These components provide the infrastructure for guarding attribute access and providing hooks into the higher level security framework. @@ -55,9 +67,10 @@ providing hooks into the higher level security framework. Checkers ~~~~~~~~ -A checker is associated with an object kind, and provides the hooks that map -attribute checks onto permissions deferring to the security manager (which in -turn defers to the policy) to perform the check. +A :class:`checker <IChecker>` is associated +with an object kind, and provides the hooks that map attribute checks +onto permissions deferring to the security manager (which in turn +defers to the policy) to perform the check. Additionally, checkers provide for creating proxies of objects associated with the checker. @@ -68,491 +81,41 @@ grant access based on attribute names. Proxies ~~~~~~~ -Wrappers around Python objects that implicitly guard access to their wrapped -contents by delegating to their associated checker. Proxies are also viral in -nature, in that values returned by proxies are also proxied. +:class:`Wrappers around Python objects <zope.security.proxy.Proxy>` +that implicitly guard access to their wrapped contents by delegating +to their associated checker. Proxies are also viral in nature, in that +values returned by proxies are also proxied. High Level Components ---------------------- +===================== Security Management -~~~~~~~~~~~~~~~~~~~ - -Provides accessors for setting up interactions and the global security policy. +------------------- -Interaction -~~~~~~~~~~~ +Provides accessors for :class:`setting up interactions +<IInteractionManagement>` and the :class:`global security policy +<ISecurityManagement>`. -Stores transient information on the list of participations. - -Participation -~~~~~~~~~~~~~ +:class:`Interaction <zope.security.interfaces.IInteraction>` +------------------------------------------------------------ -Stores information about a principal participating in the interaction. +An :class:`interaction <IInteraction>` represents zero or more +principals manipulating or viewing (interacting with) the system. -Security Policy -~~~~~~~~~~~~~~~ - -Provides a single method that accepts the object, the permission, and the -interaction of the access being checked and is used to implement the +Interactions also provide :func:`a single method +<IInteraction.checkPermission>` that accepts the object and the +permission of the access being checked and is used to implement the application logic for the security framework. -Narrative (agent sandbox) -------------------------- - -As an example we take a look at constructing a multi-agent distributed system, -and then adding a security layer using the Zope security model onto it. - -Scenario -~~~~~~~~ - -Our agent simulation consists of autonomous agents that live in various agent -homes/sandboxes and perform actions that access services available at their -current home. Agents carry around authentication tokens which signify their -level of access within any given home. Additionally agents attempt to migrate -from home to home randomly. - -The agent simulation was constructed separately from any security aspects. -Now we want to define and integrate a security model into the simulation. The -full code for the simulation and the security model is available separately; -we present only relevant code snippets here for illustration as we go through -the implementation process. - -For the agent simulation we want to add a security model such that we group -agents into two authentication groups, "norse legends", including the -principals thor, odin, and loki, and "greek men", including prometheus, -archimedes, and thucydides. - -We associate permissions with access to services and homes. We differentiate -the homes such that certain authentication groups only have access to services -or the home itself based on the local settings of the home in which they -reside. - -We define the homes/sandboxes - - - origin - all agents start here, and have access to all - services here. - - - valhalla - only agents in the authentication group 'norse - legend' can reside here. - - - jail - all agents can come here, but only 'norse legend's - can leave or access services. - - -Process -~~~~~~~ - -Loosely we define a process for implementing this security model - - - mapping permissions onto actions - - - mapping authentication tokens onto permissions - - - implementing checkers and security policies that use our - authentication tokens and permissions. - - - binding checkers to our simulation classes - - - inserting the hooks into the original simulation code to add - proxy wrappers to automatically check security. - - - inserting hooks into the original simulation to register the - agents as the active principal in an interaction. - - -Defining a Permission Model -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We define the following permissions:: - - NotAllowed = 'Not Allowed' - Public = Checker.CheckerPublic - TransportAgent = 'Transport Agent' - AccessServices = 'Access Services' - AccessAgents = 'Access Agents' - AccessTimeService = 'Access Time Services' - AccessAgentService = 'Access Agent Service' - AccessHomeService = 'Access Home Service' - -and create a dictionary database mapping homes to authentication groups which -are linked to associated permissions. - - -Defining and Binding Checkers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Checkers are the foundational unit for the security framework. They define -what attributes can be accessed or set on a given instance. They can be used -implicitly via Proxy objects, to guard all attribute access automatically or -explicitly to check a given access for an operation. - -Checker construction expects two functions or dictionaries, one is used to map -attribute names to permissions for attribute access and another to do the same -for setting attributes. - -We use the following checker factory function:: - - def PermissionMapChecker(permissions_map={}, - setattr_permission_func=NoSetAttr): - res = {} - for k,v in permissions_map.items(): - for iv in v: - res[iv]=k - return checker.Checker(res.get, setattr_permission_func) - - time_service_checker = PermissionMapChecker( - # permission : [methods] - {'AccessTimeService':['getTime']} - ) - -with the NoSetAttr function defined as a lambda which always return the -permission `NotAllowed`. - -To bind the checkers to the simulation classes we register our checkers with -the security model's global checker registry:: - - import sandbox_simulation - from zope.security.checker import defineChecker - defineChecker(sandbox_simulation.TimeService, time_service_checker) - - -Defining a Security Policy -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We implement our security policy such that it checks the current agent's -authentication token against the given permission in the home of the object -being accessed:: - - @implementer(ISecurityPolicy) - class SimulationSecurityPolicy: - - createInteraction = staticmethod(simpleinteraction.createInteraction) - - def checkPermission(self, permission, object, interaction): - - home = object.getHome() - db = getattr(SimulationSecurityDatabase, home.getId(), None) - - if db is None: - return False - - allowed = db.get('any', ()) - if permission in allowed or ALL in allowed: - return True - - if interaction is None: - return False - if not interaction.participations: - return False - for participation in interaction.participations: - token = participation.principal.getAuthenticationToken() - allowed = db.get(token, ()) - if permission not in allowed: - return False - - return True - -There are no specific requirements for the interaction class, so we can just -use `zope.security.simpleinteraction.Interaction`. - -Since an interaction can have more than one principal, we check that *all* of -them are given the necessary permission. This is not really necessary since -we only create interactions with a single active principal. - -There is some additional code present to allow for shortcuts in defining the -permission database when defining permissions for all auth groups and all -permissions. - - -Integration -~~~~~~~~~~~ - -At this point we have implemented our security model, and we need to integrate -it with our simulation model. We do so in three separate steps. - -First we make it such that agents only access homes that are wrapped in a -security proxy. By doing this all access to homes and services (proxies have -proxied return values for their methods) is implicitly guarded by our security -policy. - -The second step is that we want to associate the active agent with the -security context so the security policy will know which agent's authentication -token to validate against. - -The third step is to set our security policy as the default policy for the -Zope security framework. It is possible to create custom security policies at -a finer grained than global, but such is left as an exercise for the reader. - - -Interaction Access -~~~~~~~~~~~~~~~~~~ - -The *default* implementation of the interaction management interfaces defines -interactions on a per thread basis with a function for an accessor. This -model is not appropriate for all systems, as it restricts one to a single -active interaction per thread at any given moment. Reimplementing the -interaction access methods though is easily doable and is noted here for -completeness. - - -Perspectives -~~~~~~~~~~~~ - -It's important to keep in mind that there is a lot more that is possible using -the security framework than what's been presented here. All of the -interactions are interface based, such that if you need to re-implement the -semantics to suite your application a new implementation of the interface will -be sufficient. Additional possibilities range from restricted interpreters -and dynamic loading of untrusted code to non Zope web application security -systems. Insert imagination here ;-). - - -Zope Perspective -~~~~~~~~~~~~~~~~ - -A Zope3 programmer will never commonly need to interact with the low level -security framework. Zope3 defines a second security package over top the low -level framework and authentication sources and checkers are handled via zcml -registration. Still those developing Zope3 will hopefully find this useful as -an introduction into the underpinnings of the security framework. - - -Code -~~~~ - -The complete code for this example is available. - -- sandbox.py - the agent framework - -- sandbox_security.py - the security implementation and binding to the agent - framework. - - -Authors -~~~~~~~ - -- Kapil Thangavelu <hazmat at objectrealms.net> -- Guido Wesdorp <guido at infrae.com> -- Marius Gedminas <marius at pov.lt> - - -Untrusted interpreters -====================== - -Untrusted programs are executed by untrusted interpreters. Untrusted -interpreters make use of security proxies to prevent un-mediated -access to assets. An untrusted interpreter defines an environment for -running untrusted programs. All objects within the environment are -either: - -- "safe" objects created internally by the environment or created in - the course of executing the untrusted program, or - -- "basic" objects - -- security-proxied non-basic objects - -The environment includes proxied functions for accessing objects -outside of the environment. These proxied functions provide the only -way to access information outside the environment. Because these -functions are proxied, as described below, any access to objects -outside the environment is mediated by the target security functions. - -Safe objects are objects whose operations, except for attribute -retrieval, and methods access only information stored within the -objects or passed as arguments. Safe objects contained within the -interpreter environment can contain only information that is already -in the environment or computed directly from information that is -included in the environment. For this reason, safe objects created -within the environment cannot be used to directly access information -outside the environment. - -Safe objects have some attributes that could (very) indirectly be used -to access assets. For this reason, an untrusted interpreter always -proxies the results of attribute accesses on a safe objects. - -Basic objects are safe objects that are used to represent elemental -data values such as strings and numbers. Basic objects require a -lower level of protection than non-basic objects, as will be described -detail in a later section. - -Security proxies mediate all object operations. Any operation -access is checked to see whether a subject is authorized to perform -the operation. All operation results other than basic objects are, in -turn, security proxied. Security proxies will be described in greater -detail in a later section. Any operation on a security proxy that -results in a non-basic object is also security proxied. - -All external resources needed to perform an operation are security -proxied. - -Let's consider the trusted interpreter for evaluating URLs. In -operation 1 of the example, the interpreter uses a proxied method for -getting the system root object. Because the method is proxied, the -result of calling the method and the operation is also proxied. - -The interpreter has a function for traversing objects. This function -is proxied. When traversing an object, the function is passed an -object and a name. In operation 2, the function is passed the result -of operation 1, which is the proxied root object and the name 'A'. We -may traverse an object by invoking an operation on it. For example, -we may use an operation to get a sub-object. Because any operation on a -proxied object returns a proxied object or a basic object, the result -is either a proxied object or a basic object. Traversal may also look -up a component. For example, in operation 1, we might look up a -presentation component named "A" for the root object. In this case, -the external object is not proxied, but, when it is returned from the -traversal function, it is proxied (unless it is a a basic object) -because the traversal function is proxied, and the result of calling a -proxied function is proxied (unless the result is a basic object). -Operation 3 proceeds in the same way. - -When we get to operation 4, we use a function for computing the -default presentation of the result of operation 3. As with traversal, -the result of getting the default presentation is either a proxied -object or a basic object because the function for getting the default -presentation is proxied. - -When we get to the last operation, we have either a proxied object or a -basic object. If the result of operation 4 is a basic object, we -simply convert it to a string and return it as the result page. If -the result of operation 4 is a non-basic object, we invoke a render -operation on it and return the result as a string. - -Note that an untrusted interpreter may or may not provide protection -against excessive resource usage. Different interpreters will provide -different levels of service with respect to limitations on resource -usage. - -If an untrusted interpreter performs an attribute access, the trusted -interpreter must proxy the result unless the result is a basic object. - -In summary, an untrusted interpreter assures that any access to assets -is mediated through security proxies by creating an environment to run -untrusted code and making sure that: - -- The only way to access anything from outside of the environment is - to call functions that are proxied in the environment. - -- Results of any attribute access in the environment are proxied - unless the results are basic objects. - -Security proxies ----------------- - -Security proxies are objects that wrap and mediate access to objects. - -The Python programming language used by Zope defines a set of specific -named low-level operations. In addition to operations, Python objects -can have attributes, used to represent data and methods. Attributes -are accessed using a dot notation. Applications can, and usually do, -define methods to provide extended object behaviors. Methods are -accessed as attributes through the low-level operation named -"__getattribute__". The Python code:: - - a.b() - -invokes 2 operations: - - 1. Use the low-level `__getattribute__` operation with the name "b". - - 2. Use the low-level '__call__' operation on the result of the first - operation. - -For all operations except the `__getattribute__` and -`__setattribute__` operations, security proxies have a permission -value defined by the permission-declaration subsystem. Two special -permission values indicate that access is either forbidden (never -allowed) or public (always allowed). For all other permission values, -the authorization subsystem is used to decide whether the subject has -the permission for the proxied object. If the subject has the -permission, then access to the operation is allowed. Otherwise, access -is denied. - -For getting or setting attributes, a proxy has permissions for getting -and a permission for setting attribute values for a given attribute -name. As described above, these permissions may be one of the two -special permission values indicating forbidden or public access, or -another permission value that must be checked with the authorization -system. - -For all objects, Zope defines the following operations to be always public: - - comparison - "__lt__", "__le__", "__eq__", "__gt__", "__ge__", "__ne__" - - hash - "__hash__" - - boolean value - "__nonzero__" - - class introspection - "__class__" - - interface introspection - "__providedBy__", "__implements__" - - adaptation - "__conform__" - - low-level string representation - "__repr__" - -The result of an operation on a proxied object is a security proxy -unless the result is a basic value. - -Basic objects +Participation ------------- -Basic objects are safe immutable objects that contain only immutable -subobjects. Examples of basic objects include: - -- Strings, - -- Integers (long and normal), - -- Floating-point objects, - -- Date-time objects, - -- Boolean objects (True and False), and +Stores information about a single principal :class:`participating +<zope.security.interfaces.IParticipation>` in the :class:`interaction +<zope.security.interfaces.IInteraction>`. -- The special (nil) object, None. - -Basic objects are safe, so, as described earlier, operations on basic -objects, other than attribute access, use only information contained -within the objects or information passed to them. For this reason, -basic objects cannot be used to access information outside of the -untrusted interpreter environment. - -The decision not to proxy basic objects is largely an optimization. -It allows low-level safe computation to be performed without -unnecessary overhead, - -Note that a basic object could contain sensitive information, but such -a basic object would need to be obtained by making a call on a proxied -object. Therefore, the access to the basic object in the first place -is mediated by the security functions. - -Rationale for mutable safe objects ----------------------------------- - -Some safe objects are not basic. For these objects, we proxy the -objects if they originate from outside of the environment. We do this -for two reasons: - -1. Non-basic objects from outside the environment need to be proxied - to prevent unauthorized access to information. - -2. We need to prevent un-mediated change of information from outside of - the environment. +Security Policy +--------------- -We don't proxy safe objects created within the environment. This is -safe to do because such safe objects can contain and provide access to -information already in the environment. Sometimes the interpreter or -the interpreted program needs to be able to create simple data -containers to hold information computed in the course of the program -execution. Several safe container types are provided for this -purpose. +A :class:`security policy <ISecurityPolicy>` is used to create the +interaction that will ultimately be responsible for security checking. diff --git a/docs/proxy.rst b/docs/proxy.rst new file mode 100644 index 0000000..f5d8166 --- /dev/null +++ b/docs/proxy.rst @@ -0,0 +1,220 @@ +============================================= + Untrusted Interpreters and Security Proxies +============================================= + +Untrusted programs are executed by untrusted interpreters. Untrusted +interpreters make use of security proxies to prevent un-mediated +access to assets. An untrusted interpreter defines an environment for +running untrusted programs. All objects within the environment are +either: + +- "safe" objects created internally by the environment or created in + the course of executing the untrusted program, or + +- "basic" objects + +- security-proxied non-basic objects + +The environment includes proxied functions for accessing objects +outside of the environment. These proxied functions provide the only +way to access information outside the environment. Because these +functions are proxied, as described below, any access to objects +outside the environment is mediated by the target security functions. + +Safe objects are objects whose operations, except for attribute +retrieval, and methods access only information stored within the +objects or passed as arguments. Safe objects contained within the +interpreter environment can contain only information that is already +in the environment or computed directly from information that is +included in the environment. For this reason, safe objects created +within the environment cannot be used to directly access information +outside the environment. + +Safe objects have some attributes that could (very) indirectly be used +to access assets. For this reason, an untrusted interpreter always +proxies the results of attribute accesses on a safe objects. + +Basic objects are safe objects that are used to represent elemental +data values such as strings and numbers. Basic objects require a +lower level of protection than non-basic objects, as will be described +detail in a later section. + +Security proxies mediate all object operations. Any operation +access is checked to see whether a subject is authorized to perform +the operation. All operation results other than basic objects are, in +turn, security proxied. Security proxies will be described in greater +detail in a later section. Any operation on a security proxy that +results in a non-basic object is also security proxied. + +All external resources needed to perform an operation are security +proxied. + +Let's consider the trusted interpreter for evaluating URLs. In +operation 1 of the example, the interpreter uses a proxied method for +getting the system root object. Because the method is proxied, the +result of calling the method and the operation is also proxied. + +The interpreter has a function for traversing objects. This function +is proxied. When traversing an object, the function is passed an +object and a name. In operation 2, the function is passed the result +of operation 1, which is the proxied root object and the name 'A'. We +may traverse an object by invoking an operation on it. For example, +we may use an operation to get a sub-object. Because any operation on a +proxied object returns a proxied object or a basic object, the result +is either a proxied object or a basic object. Traversal may also look +up a component. For example, in operation 1, we might look up a +presentation component named "A" for the root object. In this case, +the external object is not proxied, but, when it is returned from the +traversal function, it is proxied (unless it is a a basic object) +because the traversal function is proxied, and the result of calling a +proxied function is proxied (unless the result is a basic object). +Operation 3 proceeds in the same way. + +When we get to operation 4, we use a function for computing the +default presentation of the result of operation 3. As with traversal, +the result of getting the default presentation is either a proxied +object or a basic object because the function for getting the default +presentation is proxied. + +When we get to the last operation, we have either a proxied object or a +basic object. If the result of operation 4 is a basic object, we +simply convert it to a string and return it as the result page. If +the result of operation 4 is a non-basic object, we invoke a render +operation on it and return the result as a string. + +Note that an untrusted interpreter may or may not provide protection +against excessive resource usage. Different interpreters will provide +different levels of service with respect to limitations on resource +usage. + +If an untrusted interpreter performs an attribute access, the trusted +interpreter must proxy the result unless the result is a basic object. + +In summary, an untrusted interpreter assures that any access to assets +is mediated through security proxies by creating an environment to run +untrusted code and making sure that: + +- The only way to access anything from outside of the environment is + to call functions that are proxied in the environment. + +- Results of any attribute access in the environment are proxied + unless the results are basic objects. + +Security proxies +================ + +Security proxies are objects that wrap and mediate access to objects. + +The Python programming language used by Zope defines a set of specific +named low-level operations. In addition to operations, Python objects +can have attributes, used to represent data and methods. Attributes +are accessed using a dot notation. Applications can, and usually do, +define methods to provide extended object behaviors. Methods are +accessed as attributes through the low-level operation named +"__getattribute__". The Python code:: + + a.b() + +invokes 2 operations: + + 1. Use the low-level ``__getattribute__`` operation with the name "b". + + 2. Use the low-level ``__call__`` operation on the result of the first + operation. + +For all operations except the ``__getattribute__`` and +``__setattribute__`` operations, security proxies have a permission +value defined by the permission-declaration subsystem. Two special +permission values indicate that access is either forbidden (never +allowed) or public (always allowed). For all other permission values, +the authorization subsystem is used to decide whether the subject has +the permission for the proxied object. If the subject has the +permission, then access to the operation is allowed. Otherwise, access +is denied. + +For getting or setting attributes, a proxy has permissions for getting +and a permission for setting attribute values for a given attribute +name. As described above, these permissions may be one of the two +special permission values indicating forbidden or public access, or +another permission value that must be checked with the authorization +system. + +For all objects, Zope defines the following operations to be always public: + + comparison + "__lt__", "__le__", "__eq__", "__gt__", "__ge__", "__ne__" + + hash + "__hash__" + + boolean value + "__nonzero__" + + class introspection + "__class__" + + interface introspection + "__providedBy__", "__implements__" + + adaptation + "__conform__" + + low-level string representation + "__repr__" + +The result of an operation on a proxied object is a security proxy +unless the result is a basic value. + +Basic objects +============= + +Basic objects are safe immutable objects that contain only immutable +subobjects. Examples of basic objects include: + +- Strings, + +- Integers (long and normal), + +- Floating-point objects, + +- Date-time objects, + +- Boolean objects (True and False), and + +- The special (nil) object, None. + +Basic objects are safe, so, as described earlier, operations on basic +objects, other than attribute access, use only information contained +within the objects or information passed to them. For this reason, +basic objects cannot be used to access information outside of the +untrusted interpreter environment. + +The decision not to proxy basic objects is largely an optimization. +It allows low-level safe computation to be performed without +unnecessary overhead, + +Note that a basic object could contain sensitive information, but such +a basic object would need to be obtained by making a call on a proxied +object. Therefore, the access to the basic object in the first place +is mediated by the security functions. + +Rationale for mutable safe objects +================================== + +Some safe objects are not basic. For these objects, we proxy the +objects if they originate from outside of the environment. We do this +for two reasons: + +1. Non-basic objects from outside the environment need to be proxied + to prevent unauthorized access to information. + +2. We need to prevent un-mediated change of information from outside of + the environment. + +We don't proxy safe objects created within the environment. This is +safe to do because such safe objects can contain and provide access to +information already in the environment. Sometimes the interpreter or +the interpreted program needs to be able to create simple data +containers to hold information computed in the course of the program +execution. Several safe container types are provided for this +purpose. diff --git a/src/zope/security/interfaces.py b/src/zope/security/interfaces.py index 86d7bff..6b50a9f 100644 --- a/src/zope/security/interfaces.py +++ b/src/zope/security/interfaces.py @@ -45,6 +45,9 @@ These can be categorized into a few different groups of related objects. - :class:`IMemberAwareGroup` - :class:`IPermission` +Anywhere that an API is documented as accepting a permission, it +means the name of the permission, or the special object +:class:`zope.security.checker.CheckerPublic`. """ from zope.interface import Interface, Attribute, implementer |