Security and problems with it in MODx Revolution

This topic is dedicated to questions of protection of MODx Revolution as a whole, as well as connectors and contexts separately (release 2.1.0 Revolution ).

Background: the question arose to create a major resource for the engine of MODx Revolution. Technical problems we have not seen, but decided to pay more attention to issues of protection of the engine.
Honestly, I always thought security in MODx Revolution is very flexible and reliable, but then I got quite a lot of surprises... will Try to disassemble them as much as possible and in detail.

Who loves from the most interesting to read, begin to read with the words "Now let's summarize what we need to work the connector........", as first considered the problem, and the problem.

UDP: in version 2.1.1 fixed. But knowing how >2.1.0 raw yet, I am sure that 99% Revo in the course of it's earlier releases.


First, take into account the most common myth with open-source products: "Easy to download it, dig it and find holes." Won't prove otherwise, just make a trick: alter file structure. Rename the main folder of MODx (assets/, connectors/, manager/), and core/ General move beyond the root directory of the website to through the address bar in General was not to undermine.
The truth is, with this few points:

    There are subtleties of the transfer folder manager/. Don't want to repeat myself, read tut the

  1. get Ready for what will fly any plugins. This is especially true of directresize plugin. In this first, hard-coded assets/, and secondly the way to images in the file system are formed not absolute, but relative, also, in turn, leads to errors.
  2. the
  3. Do not specify in your code the hard way, be sure to use system variables base_path, core_path, etc.

So it's a little bit you will protect against targeted probing the file system (for example from neighbouring accounts on leaky virtual hosting).

Secondly, we decided to make a fair service for privileged users, but not for database admin (that is not so, they must enter the admin panel, there is security settings, everything is hidden, except a new module for them), and on the basis of additional context (that is on the new subdomain, etc.). We have MODx Revolution — a MultiSite platform))).
Here the most interesting begins...

So in order...
First, we create a new context, it creates a few of documents and prescribe all the necessary settings to link to the site a new subdomain, the index file write on this domain, the initialization of the context... Check — working. Now the most important is to deny access to this context to all but those allowed...

Before continuing, a little about the principles of security:

In MODx Revolution all the rule: "If something has rights, then the principle of 'everything not permitted is forbidden'. But if anything to anybody is not assigned, then all all you can."
Make no mistake: If there is a resource group that is assigned to someone accesses it, everyone else who isn't allowed, go to the forest. But if you just created a resource group to group, and have access to them all in a row.
As with contestame: if the context is spelled out access for some Roles, security policies, all the rest are forested. All is logical.

So, we could create a resource group that would assign levels of access, and which would unite our closed documents. But we easy way refused as first, could be the human factor (for example someone created a new resource and forgot to add to the group), and secondly, we wanted in principle to block access to the entire context: does the log in, or go on.

So we decided to close access to the context. Climb, I mean, to edit our new context in order to forbid access to it... And then I'm in a stupor entered... And so, to close access to it? In the context of the access for as a super user, not anyone else. However, all have access to.
Pokopavshis better, everything was easy: it appears on the public contexts generally do not check accesses, accesses are checked only on the context of mgr, because the listeners are all different. This is a separate article, because send is already written: about the accesses to the contexts MODx Rvolution

Well. Create a new handler for our context, which intercepts the accesses to the context. By the way, MODx shows itself here in all its glory. It takes literally 30 minutes (including the development of new technologies and 10 lines of code). If not authorized, display the login page. If authorized, but does not have access to the download panel (Specifically for this, adding a custom customize security policies), displays a message about the lack of access. It turned out just fine: even the super-users, if they are not simultaneously in the correct group and user access to the context with a specified Role, not have access the control panel. Super)))

Everything seems cool, but I got to writing the needed connectors. Create all the rules of the connector, write code, create processor. Execute a query... an Empty response. This is due to the implementation of the file connectors/index.php
Is there such a code:
if (defined('MODX_REQP') && MODX_REQP === false) {
} else if (!$modx- > context- > checkPolicy('load')) {

@session_write_close();
die();
}




That is, you have 2 options:
    the
  1. to Register the connector define('MODX_REQP', false); In this case, it actually turns off checking for the right script to run. Stupid all you can.
  2. the
  3. to Assign rights in this context for the right load. It is more preferable if you want to restrict access to the connector. The problem in this case is only one: if in the first case, in the event of an error, the code execution will be given a response in JSON format, in storm if the answer is not at all.

We went on the second way. But still not beeping. Was poking around a little more. As a result came to the conclusion about the necessity of this line: $_SERVER['HTTP_MODAUTH'] = $modx- > site_id; Actually it is the variable $modx- > site_id (aka global $site_id) and there is a reason for writing this article. But more on that later...

In General we prescribe and its in the connector. Still doesn't work. It turns out we need another variable to register $_REQUEST['ctx'] = 'context_id'; And it is a source of great pain, for if it is not rigidly prescribe, to convey to the ctx you can at least GET in, even in the POST request. This is actually the key of context.

Now let's summarize what we need to work the connector and what is a global problem:
The job requires:
    the
  1. $_REQUEST['ctx'] — the key of the context passed to any method;
  2. the
  3. $site_id — the site key, is registered in the config MODx-and, he's $_SERVER['HTTP_MODAUTH']


So, focus: for example, we use the security policies are configured when you run the connector. Pass it the ctx our context. All of the fish. Who can perform, that can and who can not, that is impossible. And here we are taking, and prescription fanerny ctx=sdfsdfsdfsdfsdfsd. What is currently happening? Initializes the context sdfsdfsdfsdfsdfsd (interesting to know you have one?)))). I have no such context. And I think we're broken? Nifiga!!! MODx does not check for context. Moreover, it is generally nifiga not checks! He bluntly applies the customizations of the context sdfsdfsdfsdfsdfsd and SECURITY POLICY context sdfsdfsdfsdfsdfsd! And since there are no security policies for context sdfsdfsdfsdfsdfsd, (remember the rule mentioned above), if nothing is forbidden, then everything is possible!!! That is, the connector is performed.
Moreover. It turns out that in order to get access to everything and everyone (including admin area, that is, in the context of mgr), it is not necessary to steal a bunch of usernames/passwords and an other crap. The main thing is to steal the ill-fated $site_id (which also fucking global! that is, can be intercepted in any even the most dead piece of code, producendo via $modx or if you have access to config.inc.php).

So guys, protect their sites, who has what is important :-)

By the way, how to do it?
Very simple. In the file connectors/index.php copy the code
$ctx = isset($_REQUEST['ctx']) && !empty($_REQUEST['ctx']) ? $_REQUEST['ctx'] : 'mgr';
$modx- > initialize($ctx);



on
/* initialize the proper context */
$ctx = 'mgr';
$modx- > initialize($ctx);



that is, initialize the hard-coded context mgr. This is an example. Next is the look of the task.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Automatically create Liquibase migrations for PostgreSQL

Vkontakte sync with address book for iPhone. How it was done

What part of the archived web