質問

I’d like to restrict access to a folder of controllers that are used for admin purposes only. I’ve tried a number of ways and not coming up with a solution. These controllers are behind password protection. But, I’d like to just remove it from view if someone happens to stumble upon the right directory. Can this be done? I’d rather not do it from htaccess. I have access to the apache config files, so I’d like to handle it there.

Does it have anything to do with the way Codeigniter routes? Or, am I just way off?

This what I’m using that doesn’t work

<Directory /var/www/application/controllers/folder/>
  Order deny,allow
  Deny from all
  Allow from xxx.xxx.xxx.xxx
</Directory> 
役に立ちましたか?

解決

Due to the way we re-write urls to work with CI, you'd never match your Apache config because you're actually requesting index.php?{args}. If you want to filter, you have to do it in CI instead. Your options are a core controller or hooks.

A simple way to do it is to create a core controller that your admin/ area scripts extend, and check the IP there.

Something like this:

application/core/MY_Controller.php:

class MY_Controller extends CI_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load->config('permitted_ips');
        // check visitor IP against $config['ips'] array, redirect as needed
    }
}

Then, in your 'sensitive' controllers, extend MY_Controller:

application/controllers/admin/seekrit.php

class Seekrit extends MY_Controller
{
    public function __construct() {
        parent::__construct();
        /* at this point any invalid IP has been redirected */
    }
}

Now, if you're already using a core controller for something else, just check $this->uri->segment() to see if they're in a restricted area before loading the allowed IP configuration file and checking / redirecting / dying or whatever else you need to do.

Also, there's no need to use a constructor in your admin controllers if you don't need one, as the parent will be constructed if one is not defined. Just be sure to call the parent if you define one.

You could also put the white list in a database, Redis, whatever.

Another way to do this would be by using hooks, specifically the pre_controller hook. By the time that hook is entered, all of the security and base classes have run. This would be appropriate if you wanted to protect some or all of your routes in a more granular fashion. There, you could define an array containing routes, such as:

$protected_routes = array(
    'foo' => array(
         'allow_ip' => '1.2.3.4',
         'redirect_if_not' => site_url('goaway')
     )
)

Then, in your hook class (or function) match the first segment (my example is just a function):

$CI = get_instance();
$CI->load-config('my_hook');
$protected_routes = $CI->config->item('protected_routes');
$segment = $CI->uri->segment(1); // foo
if (in_array($segment, $protected_routes)) {

   // grab $protected_routes[$segment] and work with it
}

This has the advantage of not cluttering up your core controller as many people use that as a means of sharing code between methods. However, the hook will run on every request which means adding another two file loads to bootstrap.

I used the hook method on a large RESTful service to protect certain endpoints by requiring additional headers, and enforcing different kinds of rate limiting on others. Note, the code above is just an example of what could go in the hook, not how to set up the hook itself. Read the hooks section of the CI manual, it's extremely easy and straight forward.

Finally, if you really want to do it via .htaccess, you'll have to go by the request itself. The directory application/controllers/foo is never entered, the actual request is /foo/controller/method{args}, which causes CI to instantiate the foo/controller.php class. Remember, once re-written, the server sees index.php?....

To accomplish this, you can re-write based on the request URI pattern, something like this (have not tested, YMMV):

RewriteRule (^|/)foo(/|$) - [F,L]

Which can be used to redirect anyone accessing the virtual path to your protected controllers. This could be preferable as it prevents PHP from needing to handle it, but you lose the granularity of control over what happens when there is a match. Still, you could use something like the above re-write combined with a hook or core implementation if you have more than one sensitive area to protect.

他のヒント

Tim Post's idea above is similar to another method I either saw on this site or somewhere else. It took me awhile to get back to this issue, but at long last it's done.

As TheShiftExchange pointed out in the comments under my original question, .htaccess will not work for a Codeigniter project. Below is what I ultimately ended up with and seems to work well. It probably isn't 100% secure, but I really just wanted to remove these pages from being directly accessed. If someone were to manage to get to the page there is still a user/pass login screen.

New Config File in application/config

switch (ENVIRONMENT) //set in index.php
{
    case 'development':
        $config['admin_ips'] = array('XXX.XXX.XXX.XXX');
        break;
    case 'testing':
        $config['admin_ips'] = array('XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX');
        break;
    case 'production':
        $config['admin_ips'] = array('XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX', 'XXX.XXX.XXX.XXX');
        break;
}

New Controller

class Admin_IP_Controller extends MY_Controller {
    function __construct()
    {
        parent::__construct();
        $this->load->config('admin_ips');
        if (!in_array($this->input->ip_address(), $this->config->item('admin_ips')))
        {
            show_404();
        }
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top