Question

CakePHP has a requireSecure function in the SecurityComponent. I'm using this to force SSL when passing sensitive information such as a credit card number.

Questions:

  • Is there a requireNonSecure function?
  • If there's no requireNonSecure function is it possible to extend/add a function to CakePHP's core file without modifying the original file?

I want a requireNonSecure function because some of my pages have embedded videos that can only be played on our domain name. When using SSL the video hosting service does not recognize our domain name and cannot play the videos.

This is some of the code in the beforeFilter of a controller:

function beforeFilter() {
    parent::beforeFilter();

    $this->Security->validatePost = false; // disable CSRF protection
    $this->Security->blackHoleCallback = 'forceSSL';
    $this->Security->requireSecure('pay', 'index');

    $this->Auth->allow('index');
}

This is the callback in app_controller.php

function forceSSL() {
    $redirect = '';
    if (!empty($this->params['url']['redirect'])) {
        $redirect = '?redirect=' . $this->params['url']['redirect'];
    }

    $this->redirect('https://' . rtrim(env('SERVER_NAME'), '/') . $this->here . $redirect);
}
Was it helpful?

Solution

A solution would be to append a function to beforeFilter like this:

In a controller:

function beforeFilter() {
    parent::beforeFilter();

    // Require non secure (http) for video action
    $this->requireNonSecure('video');

    // ... other code here

}

In app_controller.php:

function requireNonSecure() {
    $requireNonSecure = array_map('strtolower', func_get_args());

    if (in_array(strtolower($this->action), $requireNonSecure) || $requireNonSecure == array('*')) {
        if ($this->RequestHandler->isSSL()) {
            $this->redirect('http://' . rtrim(env('SERVER_NAME'), '/') . $this->here);
            return;
        }
    }
}

OTHER TIPS

This solution adds to SecurityComponent. It should work but there is a risk of redirect loops if both requireSecure and requireNonSecure are set.

SecurityPlusComponent:

class SecurityPlusComponent extends SecurityComponent {

    /**
     * List of actions that do not require an SSL-secured connection
     *
     * @var array
     * @access public
     * @see SecurityPlusComponent::requireNonSecure()
     */
    var $requireSecure = array();

    /**
     * Component startup. All security checking happens here.
     *
     * @param object $controller Instantiating controller
     * @access public
     */
        function startup(&$controller) {
            $this->_action = strtolower($controller->action);
            $this->_methodsRequired($controller);
            $this->_secureRequired($controller);
            $this->_nonSecureRequired($controller);
            $this->_authRequired($controller);
            $this->_loginRequired($controller);

            $isPost = ($this->RequestHandler->isPost() || $this->RequestHandler->isPut());
            $isRequestAction = (
                !isset($controller->params['requested']) ||
                $controller->params['requested'] != 1
            );

            if ($isPost && $isRequestAction && $this->validatePost) {
                if ($this->_validatePost($controller) === false) {
                    if (!$this->blackHole($controller, 'auth')) {
                        return null;
                    }
                }
            }
            $this->_generateToken($controller);
        }

    function requireNonSecure() {
        $this->_requireMethod('NonSecure', func_get_args());
    }

    /**
     * Check if access requires non secure connection (http)
     *
     * @param object $controller Instantiating controller
     * @return bool true if secure connection required
     * @access protected
     */
    function _nonSecureRequired(&$controller) {
        if (is_array($this->requireNonSecure) && !empty($this->requireNonSecure)) {
            $requireNonSecure = array_map('strtolower', $this->requireNonSecure);

            if (in_array($this->_action, $requireNonSecure) || $this->requireNonSecure == array('*')) {
                if ($this->RequestHandler->isSSL()) {
                    if (!$this->blackHole($controller, 'nonSecure')) {
                        return null;
                    }
                }
            }
        }
        return true;
    }
}

Modified app_controller forceSSL function:

function securityBlackhole($type) {
    $redirect = '';
    if (!empty($this->params['url']['redirect'])) {
        $redirect = '?redirect=' . $this->params['url']['redirect'];
    }

    // Force http (non-SSL)
    if($type == 'nonSecure') {
        $this->redirect('http://' . rtrim(env('SERVER_NAME'), '/') . $this->here . $redirect);

    // Force https (SSL)
    } else {
        $this->redirect('https://' . rtrim(env('SERVER_NAME'), '/') . $this->here . $redirect);
    }
}

Would be called like this in a controller:

function beforeFilter() {
    parent::beforeFilter();

    $this->SecurityPlus->validatePost = false; // disable CSRF protection
    $this->SecurityPlus->blackHoleCallback = 'securityBlackhole';
    $this->SecurityPlus->requireSecure('pay', 'index');
    $this->SecurityPlus->requireNonSecure('video');

    $this->Auth->allow('index');
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top