Plugins currently often use hard-coded security.
Use ACLs
Plugins could use Acls to protect the data they show. This is already done by some plugins but most times hard coded.
Proposal:
Use a dict resourcename -> acl may be (request.cfg.permission)
- These Acls should default to the current behaviour but could be changed.
- Try to use the rights we already have and avoid new ones.
Examples
- user
write for creation and changes
rather use separate rights for change / creation, after having an interface where one can use that, currently there is no way
- wait until user data is moved into the wiki storage
admin to view list and disable
- wiki
read for backup (SystemAdmin macro)
a backup includes user data, so now we would rather use data now
- write for restore
- config (Webinterface)
- attachments (general, still requires permissions to the page)
- soon just "items" like pages (2.0)
- mail_to_subscribers
This could be especially useful for plugins that show data that is not directly stored in pages/in the wiki itself. Like a DB interface.
Would also solve FeatureRequests/NewUserCreationACL.
Hierarchy of keys
Key names can represent the wiki parts:
wiki - the top item. User with write right for wiki can change anything in the wiki
wiki/pages - the wiki pages. User with write right can create or write any page
wiki/pages/pagename - same for single page an all its sub pages
- this would overlap with acls defined on the page. do we need/want that?
Its good to have a consistent namespace. SecurityPolicy can use the value in the dict, or get the acl object from the page.
- this would overlap with acls defined on the page. do we need/want that?
wiki/users - same for users
wiki/plugin/macro/UserPreferences - rights for this macro
wiki/plugin/action/backup - backup rights. It is handled like any other plugin.
wiki/plugin/action/admin - web admin rights
And so on. The dict would replace acl_rights_before or acl_rights_default or both, with a much more powerful system.
Example - checking rights for UserPreferences macro:
- check acl_before - if we want to keep this
Check wiki/plugin/macro/UserPreferences
Check wiki/plugin/macro
Check wiki/plugin
Check wiki
- return False
Example - checking rights for PageName/SubPage:
- check acl_before - if we want to keep this
Check wiki/pages/PageName/SubPage (check acl on the page)
Check wiki/pages/PageName (check acl on the page)
Check wiki/pages - acl_rights_before or the current system
Check wiki
- return False
It's not clear what right you need to execute a plugin - read? Maybe read for operations that display data, write for operations that change data?
Example - code that need rights in a plugin:
# Before running any code in the plugin: if not request.user.may.read('wiki/plugin/action/name'): raise UserNotAllowed # Before doing something that changes wiki data if not request.user.may.write('wiki/plugin/action/name'): raise UserNotAllowd # rather may.write('wiki/pages') ? # The action may not change pages data, like delete user action, or wiki admin
The caller will handle these errors by sending the current page with an error message.
SecurityPolicy may implement the cascade checking. When you call it with a path, it will check all the components of the path in the correct order. If you call with a path that does not exist, you will get False, which is safe.
Configuration
If the user define acl dict, use it. If not, use the default acl dict.
To change some rights in the acl dict, use:
acl = DefaultConfig.acl acl[key] = new value
Or, use always the default acl, and if the user define his own acl, update the default acl with the user keys.
When a config object is created, each key in the acl dict will be replaced with an ACL object that can answer the may() call.
ACL checking
SecurityPolicy will use this code to check rights:
# resource is an acl path e.g 'wiki/plugin/action/backup' for path in reversePathIterator(resource): acl = request.cfg.acl.get(path) if acl is None: continue may = acl.may() if may is not None: return may return False # default safe answer
reversePathIterator is needed in other places, for example, in code that create bread crumbs:
def reversePathIterator(path): components = path.split('/') length = len(commponents) for i in range(len(components)): path = components[:length - i] path = '/'.join(path) yield path
For pages, we need a way to check the acl on the page instead of the acl dict. We can do this by using a modified dict that returns the pages acl object for keys that starts with wiki/pages/.
Performance
PageList is very expensive currently due to exists and acl check. If ACLs more complicated, it will even get slower.
exists is the most important bottleneck
- acl check is expensive because the acl cache need to do a page.exists check for each page just to get the cached acl from memory
The proposed acl dict is in memory, and contain acl objects. The most expensive thing is the acl matching, which is few dictionary lookups. If we want, we can cache the results of all keys per one request. For example, when you create a page list, and no pages have any, and the acl are defined in wiki/pages, you can cash True or False on the first time the user query wiki/pages, and then the next 1000 queries will just be one dictionary lookup.
We will have to profile such code to see if there are any performance problems.
Usability
Another thing is usability. Quite some user's don't get ACLs right now. They will get more if you make it more complicated.
This will not make acl eaiser to use as the same acl syntax is used. However, admins that can work with this syntax will have more control on the wiki. Maybe it can save the need to write a custom SecurityPolicy for some cases, which of course much more complicated then setting acl rights on an object.
The hierarchy can make your life easier when you can define acl once on on one item in the hierarchy, instead of repeating the same acl on many sub items.
The ChainOfResponsibilityPattern pattern used by the acl tree is used successfully in other systems, for example HyperCard, which was always considered very easy to use system. Also, the tree structure resemble the directory tree of the wiki directory, which should make it easy to grasp.
Today we have 3 levels, before, page, and after, which is rather confusing model compared with wiki, wiki/pages and wiki/pages/pagename.
We should do usability tests to see if people can work with this hierarchy.
Check order
The check order described start the acl check on the last leaf, so you can override the acl of the root of the tree, (called today acl_rights_before), which is not possible today, and its a feature some people requested and complained about.
- This is exactly what is not wanted. acl_rights_before should never be overwritten - this is its purpose.
- People are asking for this. For example, they like to make the wiki secure, and then open only few pages. This approach is common when trying to secure things.
- This is what a default ACL is for. Plus some different ACLs on those few pages.
- But the default acl is ignored if you use acl on the page. A use expect the he can add cal right on the page, and let the other acl rights inherited from the default. You can use the syntax that add the default into the page acl, but its not easy to use and this should be done by the wiki, not by you.
- This is what a default ACL is for. Plus some different ACLs on those few pages.
- People are asking for this. For example, they like to make the wiki secure, and then open only few pages. This approach is common when trying to secure things.
Of course it possible to use acl_rights_before with the tree check order if we like, it not related to the way acl checked on wiki objects.
Pages acl creation
This solution assume that pages do not use the current system of copying all acl lines from acl_before, default and after, and then creating acl object from all the lines, but creating acl object from the page lines, and creating separate acl objects for each dict key. This design make the page acl cache much smaller and faster to create. MoinCaching contains timings for this global acl cache.
I am looking at the Gallery2 parser to develop a web album/interface to photo images already organized into directories on a network disk. This is an Intraweb and I am not too concerned about security, but there are reasons to progress cautiously since I was proposing changes that would allow a wiki user to read images from any directory that the web server process could access.
- Well, the simplest solution to THAT is maybe simply "don't do that".
I want to do that. In my situation, the owner of the web server process has less privledges to the file system than the wiki users. I want the wiki to help organize and display the images that are on the file system and edited Gallery2 to accomplish that goal. My changes are not appropriate for the regular distribution of Gallery2 because of security issues. My request is to have an easy to implement security for the plugins. That is it. I probably shouldn't have mentioned ACL, but it seems like you could at least have a lot of flexibility. -- TimCera 2005-08-13 18:11:18
May be you have experience in securing a programming language and could give us an idea how we could solve those problems as for example are given by http://johannes.sipsolutions.net/Projects/new-moinmoin-latex/security-considerations I think we would need a solution which protects the webserver if they were not set. Do you think this is possible? -- ReimarBauer 2005-08-13 19:07:00
I also was looking at the http://moinmoin.wikiwikiweb.de/FormatterMarket/application_pdf.py and noticed the warning about security.
I suggest that ACL could possibly be extended to control access to the plugins. Maybe the default access would only allow read access to a specific directory. Maybe it could even be on a plugin by plugin basis. I don't have a specific proposal on the ACL format, it might be messy enough to just cause a bunch of problems, but here it is for people to look at.
Since at the current time there is no security whatsoever on the plugins, any way to improve the situation would be better than nothing. I remember a restricted environment in Python that was removed because it didn't work very well. Did anything come along to replace it?
- No. Python has no security concept currently. You are advised to use jails etc.
What is a jail? I was thinking if MoinMoin supplied an API for accessing the file system, and every plugin used the API rather than fp=open("filename", "r") then a security protocol could be enforced. Is that what you mean? -- TimCera 2005-08-13 18:11:18
- A jail is a concept on the BSD operating systems: you can restrict what a process may do very finely grained. To which extent should your proposal help? The plugins could still open the files directly.
I thought of a simpler way to accomplish this. As moinmoin runs a plugin, it looks in the plugin's directory for an ACL of the same name. In other words the ACL is not associated with the page, but installed with the plugin.
/moinmoin_data/engineering/data/plugin/macro/examplemacro.py /moinmoin_data/engineering/data/plugin/macro/examplemacro.acl
The ACL could even limit the use of the plugin to specific pages (for example - you could have plugins that only work with Category* pages). -- TimCera 2005-08-31 16:07:01
At the moment the acls rules are used in the plugins too. I see some problems to destinguish in an additional file which plugin function of one plugin has which rights e.g. AttachFile. For some of the plugins it could go well but I would more prefer a wiki page as it is already used for group definitions instead of a text file in the plugin dir. Something like ExampleMacroUser with a simple list of users are allowed to use ExampleMacro. Something like this could be simple added to the plugin call and if the page does not exist all could use the plugin
The acl rights does not help to make a plugin secure it only restricts the usage and avoid probably some dangerous actions. Secureness must be done in the plugin. Probably this pagename should be changed to AclExtendedToUsePlugins and probably this discussion should be linked to UnifyParsersAndMacros. -- ReimarBauer 2005-08-31 17:57:46
see also FeatureRequests/AclRefactoring