Hi,
as my very private Wiki grew larger, a few people liked to use it as well. So I needed to protect the sites, that are really private. Since some kind of user rights is scheduled for version 1.5, here comes a quickhack:
Wiki Version: 1.3.5 (maybe it works on later versions as well)
Insert the following into Title.php, function userCanRead(), right after the globals have been defined (for me it's line 550):
# inserted by m:o global $wgRequireUser; $siteName = $this->getPrefixedText(); # pagename $requiredUser = $wgRequireUser[$name]; $allowed = true; foreach( $wgRequireUser as $siteRegExp => $requiredUser ) { $check= preg_match("/$siteRegExp/", "$siteName");
if( $check==1 && $wgUser->getName()!=$requiredUser ) { $allowed = false; } } if( ! $allowed ) return false; # /inserted by m:o
Then insert the following in your LocalSettings.php at the end, just before the '?>':
# # inserted by m:o # # set access rights # # usage: # "regular expression that matches the site you want to protect" => "required user" # # examples: # "^Linux" matches all sites beginning with "Linux" # "Linux" matches all sites containing "Linux" # "Linux$" % ending with "Linux" # $wgRequireUser = array ( "^Hauptseite:private$" => "Mo", "^Tbd$" => "Mo", "^Desy" => "Mo", "^Grid" => "Mo", "^Logbook$" => "Mo" );
Maybe someone can comment on possible disadvantages or security holes?
Cheers,
- Moritz
On Thu, 27 Jan 2005 15:58:25 +0100 Moritz Karbach mailinglist@karba.ch wrote:
Hi,
as my very private Wiki grew larger, a few people liked to use it as well. So I needed to protect the sites, that are really private. Since some kind of user rights is scheduled for version 1.5, here comes a quickhack:
Wiki Version: 1.3.5 (maybe it works on later versions as well)
Insert the following into Title.php, function userCanRead(), right after the globals have been defined (for me it's line 550):
# inserted by m:o global $wgRequireUser; $siteName = $this->getPrefixedText(); # pagename $requiredUser = $wgRequireUser[$name]; $allowed = true; foreach( $wgRequireUser as $siteRegExp => $requiredUser ){ $check= preg_match("/$siteRegExp/", "$siteName"); if( $check==1 && $wgUser->getName()!=$requiredUser ) { $allowed = false; } } if( ! $allowed ) return false; # /inserted by m:o
Then insert the following in your LocalSettings.php at the end, just before the '?>':
# # inserted by m:o # # set access rights # # usage: # "regular expression that matches the site you want to protect" => "required user" # # examples: # "^Linux" matches all sites beginning with "Linux" # "Linux" matches all sites containing "Linux" # "Linux$" % ending with "Linux" # $wgRequireUser = array ( "^Hauptseite:private$" => "Mo", "^Tbd$" => "Mo", "^Desy" => "Mo", "^Grid" => "Mo", "^Logbook$" => "Mo" );
Nice! I've been looking for something like this. (Sorry, I haven't studied enough of the Mediawiki code to comment on the security aspect yet.)
Thanks, Jacob
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Moritz Karbach wrote: | as my very private Wiki grew larger, a few people liked to use it as well. So | I needed to protect the sites, that are really private. Since some kind of | user rights is scheduled for version 1.5, here comes a quickhack:
If your sites are really private, you should not use MediaWiki to hold them. It's really, really not designed to hide information.
| Wiki Version: 1.3.5 (maybe it works on later versions as well)
You should upgrade to 1.3.9 immediately, as there are potentially exploitable security holes in 1.3.5.
| Insert the following into Title.php, function userCanRead(), right after the | globals have been defined (for me it's line 550): | | # inserted by m:o | global $wgRequireUser; | $siteName = $this->getPrefixedText(); # pagename | $requiredUser = $wgRequireUser[$name];
Note that the above line doesn't seem to do anything, and produces two PHP notice warnings if error_level is set to E_ALL. (Undefined variable $name, and undefined array index.)
[remainder of code snipped]
| Maybe someone can comment on possible disadvantages or security holes?
An insecure page containing a template inclusion can extract the hidden page's text, like {{:Hauptseite:private}} or {{:Tbd}}.
- -- brion vibber (brion @ pobox.com)
Hi Brion,
If your sites are really private, you should not use MediaWiki to hold them. It's really, really not designed to hide information.
Yes, I'm aware of it. Since the main point of the wiki idea is that there are no private sites :-)
You should upgrade to 1.3.9 immediately, as there are potentially exploitable security holes in 1.3.5.
Thanks.
| $requiredUser = $wgRequireUser[$name];
Note that the above line doesn't seem to do anything
Whops, thats a relict of earlier tests of mine. One can safely delete this line.
An insecure page containing a template inclusion can extract the hidden page's text, like {{:Hauptseite:private}} or {{:Tbd}}.
You are right! I haven't thought about that... :-/
Thanks for your comments,
- Moritz
Moritz,
I also have a similar requirement and have thought of the following scheme: if a page is in the [[Category:Private]] category, it'll only be viewable/editable by registered users (and, of course, the [[Special:Userlogin]] page is protected). This is more flexible than regex matching.
However, I'm not fluent in PHP and didn't have the time to work out the implementation... :-( -- John
Moritz Karbach wrote:
Hi,
as my very private Wiki grew larger, a few people liked to use it as well. So I needed to protect the sites, that are really private. Since some kind of user rights is scheduled for version 1.5, here comes a quickhack:
Wiki Version: 1.3.5 (maybe it works on later versions as well)
Insert the following into Title.php, function userCanRead(), right after the globals have been defined (for me it's line 550):
# inserted by m:o global $wgRequireUser; $siteName = $this->getPrefixedText(); # pagename $requiredUser = $wgRequireUser[$name]; $allowed = true; foreach( $wgRequireUser as $siteRegExp => $requiredUser ) { $check= preg_match("/$siteRegExp/", "$siteName"); if( $check==1 && $wgUser->getName()!=$requiredUser ) { $allowed = false; } } if( ! $allowed ) return false; # /inserted by m:o
Then insert the following in your LocalSettings.php at the end, just before the '?>':
# # inserted by m:o # # set access rights # # usage: # "regular expression that matches the site you want to protect" => "required user" # # examples: # "^Linux" matches all sites beginning with "Linux" # "Linux" matches all sites containing "Linux" # "Linux$" % ending with "Linux" # $wgRequireUser = array ( "^Hauptseite:private$" => "Mo", "^Tbd$" => "Mo", "^Desy" => "Mo", "^Grid" => "Mo", "^Logbook$" => "Mo" );
Maybe someone can comment on possible disadvantages or security holes?
Cheers,
- Moritz
MediaWiki-l mailing list MediaWiki-l@Wikimedia.org http://mail.wikipedia.org/mailman/listinfo/mediawiki-l
Hi John,
I also have a similar requirement and have thought of the following scheme: if a page is in the [[Category:Private]] category, it'll only be viewable/editable by registered users (and, of course, the [[Special:Userlogin]] page is protected). This is more flexible than regex matching.
using categories sounds good! If I have the time, I will have a closer look at it. But I got the feeling that the 'template hack' Brion mentioned will also work on that (if I don't want to make major changes).
- Moritz
[posted and mailed]
Hi Brion,
using categories sounds good! If I have the time, I will have a closer look at it. But I got the feeling that the 'template hack' Brion mentioned will also work on that (if I don't want to make major changes).
I just checked, that categories that are defined in the template appear on a site that includes a template as well.
Do you consider it possible to include a call of the userCanRead() function when the categories are parsed? Can you show me where in the code this would be?
Thanks a lot,
- Moritz
Hi,
I found out how to require specific users for certain page categories. This should be more flexible and somewhat safer than my previous quickhack, since the template security hole doesn't occur any more (pages aquire the categories of a template).
In the meantime I upgraded to Mediawiki 1.4beta5, so this is only tested with this version.
Here are the changes:
----------------------------------------- LocalSettings.php at the end:
# # inserted by m:o # # set access rights # # usage: # - "category" => "required_user" # - 'required_user' can also be a list of users, separeted with blanks # - the first expression witch matches the sitename will be used. If there are other # expressions that match, they will be ignored. # $wgRequireUser = array ( "Privat" => "Mo", "Grid" => "Mo Max Christoph", "H1MC" => "Mo Max Christoph" );
----------------------------------------- includes/Title.php line 843:
# inserted by m:o global $wgRequireUser;
// get categories as array $parentCategories = $this->getParentCategories();
if( !empty($parentCategories) ) //prevents php warning for uncategorized pages { // go through all categories wich have restrictions foreach( $wgRequireUser as $category => $requiredUser ) { // go through all categories to which a page belongs to foreach( $parentCategories as $siteCategory => $wikiSite ) { // is the page in a category that is restricted? if( preg_match("/:".$category."$/", $siteCategory)==1 ) //if( $siteCategory == "Kategorie:".$category ) { // ...then check, whether the user is the right one! $user = $wgUser->getName(); $isUsrAllowed = preg_match("/\b$user\b/", "$requiredUser");
// not the right one? -> go away! if( $isUsrAllowed!=1 ) { return false; } } } } } # /inserted by m:o
Cheers,
- Moritz
On Wed, 2 Feb 2005 12:35:35 +0100, Moritz Karbach mailinglist@karba.ch wrote:
the template security hole doesn't occur any more (pages aquire the categories of a template).
Unforturnately, I've found 2 major problems with this:
Firstly, it allows any user to lock themselves [and just about everyone else] out of any page: either directly, by adding [[Category:Name of private category]] or indirectly, by adding {{:Name of page which is already in private category}}. This hands a rather large license for mayhem to any vandals you encounter.
Secondly, the content can be viewed by using the "preview" function: edit any page, add {{:Name of supposedly private page}}, and click preview. Because the page hasn't been saved, it doesn't belong to the new category as far as getParentCategories() is concerned (because that function gets its info straight out of the database).
It seems to me that your first approach, based on the *title* of the article, is a more sensible one, because I don't see how you could prevent a user from adding something to a category. But I think to be effective, you'll need to add at least:
* a test in the template/inclusion code - even if only to ban private pages from being included full stop (since working out whether two pages are in the same private set is not necessarily simple).
* a test in the page move code - like with adding a category, you don't want people to be able to rename a perfectly ordinary article such that it is now "private", unless they are among those capable of viewing it in its new location (and therefore moving it back). [I suppose you could simply ban all but a few users from renaming pages, but that seems a little draconian...]
So much for the quick hack I guess :( Perhaps it would be best to look into building this round the new user rights system after all, so you don't have to go back and rewrite it all when 1.5 hits.
Hi Rowan,
first of all thanks for analyzing my code :-)
Unforturnately, I've found 2 major problems with this:
This is bad :-/ But you should know, that I'm not expecting this piece of code working without any side effects, since a user management is far more complicated than a few lines :-) I don't think, that I will have any user that I don't know personally in my wiki, so my solution doesn't have to be perfectly secure. But it would be nice, of course :-)
Firstly, it allows any user to lock themselves
That's one thing I can tolerate.
Secondly, the content can be viewed by using the "preview" function:
But this I'd like to fix!
I guess it would be best to include some check after the page has been parsed, but before it gets displayed. If there is a function that gathers the categories (from db and from preview) and passes them to the parser, this would be perfect.
Is there some dataflow chart or something on the web, where the parsing mechanism is explained?
Cheers,
- Moritz
On Wed, 2 Feb 2005 20:42:10 +0100, Moritz Karbach mailinglist@karba.ch wrote:
first of all thanks for analyzing my code :-)
No problem - in fact, sorry for picking it apart to such an extent.
Firstly, it allows any user to lock themselves
That's one thing I can tolerate.
Funny, I figured that would be the worst part. But I guess if you've got total control over who has editing rights anyway (a CMS-style environment, rather than a truly wiki-ish one), this isn't such a big deal after all.
Secondly, the content can be viewed by using the "preview" function:
But this I'd like to fix!
I guess it would be best to include some check after the page has been parsed, but before it gets displayed. If there is a function that gathers the categories (from db and from preview) and passes them to the parser, this would be perfect.
I'm pretty sure it's the other way around - the "parser" has to go through the wikitext, and pick out any category links it finds; these are then plonked in the database for other use if the page is being saved. On preview, they're just added in the little box at the bottom of the page, without the database being updated.
But that doesn't remove the potential for grabbing a list of them - Skin.php has a function called getCategoryLinks(), which grabs the definitive list of categories from $wgOut (the instance of OutputPage.php). If you were to use one of those as your source, I think [although I wouldn't guarantee] you would get the categories currently included on the page, even in preview, irrespective of what's in the database.
It does mean, though, that you'll need to do the check *right at the end* of the parsing - somewhere just before the HTML is actually output, but after the wikitext has been parsed from beginning to end.
Is there some dataflow chart or something on the web, where the parsing mechanism is explained?
Hm, I believe you have just run into "Bug 1" [http://bugzilla.wikipedia.org/show_bug.cgi?id=1]. In other words, no, not that I've ever seen. The codebase, particularly the parsing and associated processing, is somewhat labyrinthine, I'm afraid - all done through a complex tangle of Parser.php (don't be fooled, it's not so much a parser as a bunch of loops and regexes), Skin.php (and its sub-classes) and OutputPage.php.
There is some automatically generated [from source comments] documentation at http://wikipedia.sourceforge.net/docs/html/ - although this seems to be out of date right now. There's also a few fragments on the meta wiki, like http://meta.wikimedia.org/wiki/MediaWiki_code_layout, for very general overviews.
Hi Rowan,
That's one thing I can tolerate.
Funny, I figured that would be the worst part. But I guess if you've got total control over who has editing rights anyway (a CMS-style environment, rather than a truly wiki-ish one), this isn't such a big deal after all.
If it turns out, that I'm using the whole day on removing malicious category links, I'll think about another solution :-)
I'm pretty sure it's the other way around - the "parser" has to go through the wikitext, and pick out any category links it finds; these are then plonked in the database for other use if the page is being saved. On preview, they're just added in the little box at the bottom of the page, without the database being updated.
I guess it works more like 1. grabbing all the wiki text, from DB _and_ from the user's edit form, then 2. pass it to the parser. That's why a check of the access rights in a function that only grabs from DB isn't sufficient...
$wgOut (the instance of OutputPage.php)
Yes, that's the key. The very last action of index.php is to call $wgOut->output(), so I copied my (slightly modified) quickhack code to this function and it seems to work.
So we got three places to modify:
includes/Title.php prevent access for default view, editing, diff includes/OutpuPage.php prevent access in case of preview of templates LocalSettings.php define access rights
------------------------ includes/OutputPage.php, line 370 (function output() at the beginning):
# inserted by m:o global $wgRequireUser;
// get categories as array $parentCategories = $this->getCategoryLinks();
if( !empty($parentCategories) ) //prevents php warning for uncategorized pages { // go through all categories wich have restrictions foreach( $wgRequireUser as $category => $requiredUser ) { // go through all categories to which a page belongs to foreach( $parentCategories as $key => $siteCategoryLink ) { // is the page in a category that is restricted? // strip_tags gets rid of the <a href...> if( $category==strip_tags($siteCategoryLink) ) { // ...then check, whether the user is the right one! $user = $wgUser->getName(); $isUsrAllowed = preg_match("/\b$user\b/", "$requiredUser");
// not the right one? -> go away! if( $isUsrAllowed!=1 ) { // don't show any categories on the 'login required'-page $this->mCategoryLinks = array();
$this->loginToUse(); $this->mBodytext .= '<br><br>Wenn du von einer Bearbeiten-Seite hier gelandet bist, hast du versucht, ein geschuetztes template einzubinden. Benutze den Zurueck-Button in deinem Browser!<br><br>If you ended up here coming from an editing site, you have tried to use a protected template. Please use the back button of your browser!'; } } } } } # /inserted by m:o
------------------------ includes/OutputPage.php, line 623 (function loginToUse()):
//comment the following: $this->returnToMain(); # Flip back to the main page after 10 seconds.
Cheers,
- Moritz
On Thu, 3 Feb 2005 11:04:29 +0100, Moritz Karbach mailinglist@karba.ch wrote:
I'm pretty sure it's the other way around - the "parser" has to go through the wikitext, and pick out any category links it finds; these are then plonked in the database for other use if the page is being saved. On preview, they're just added in the little box at the bottom of the page, without the database being updated.
I guess it works more like 1. grabbing all the wiki text, from DB _and_ from the user's edit form, then 2. pass it to the parser. That's why a check of the access rights in a function that only grabs from DB isn't sufficient...
I'm 90% sure there's no reason for it to grab *anything* from the database on preview - the parser has the [editted] article text to go through, and that contains the definitive list of categories. The page text in the database, and the categorylinks table, will simply be out of date and irrelevant - you could blank the whole page and click preview, and nothing in the database would relate to the text you were previewing. The main purpose of the categorylinks table is for doing *backwards* lookups - as in, what pages are in this category.
But anyway, it looks like you've found a check for previews as well now, so all's well that ends well. If I get round to it, I'll test it out and see if I can spot anything behaving badly, but I'm booted into the wrong OS for my test wikis right now.
By the way, now that the hack involves so many changes, you might want to look into creating a patch - ignore the rest of this paragraph if you already know how and why to do this, I thought I'd add it in case you didn't. If you grabbed the code straight from CVS, you should be able to run something like "cvs diff -U3" to get it to churn one out. Otherwise, you'll need to use diff to compare a modified and unmodified copy of each file. Not only will this make it easier to share with other people, but if you get enough context (-U3 being 3 lines each side of each change) you should be able to reapply the hack after replacing the files with a newer version [unless relevant things have changed, obviously!].
mediawiki-l@lists.wikimedia.org