Home > Tipps & Tricks > Zend Framework (1.x) > Zend MVC: Erzwingen von https- und https-URLs mittels Plugin

Zend MVC: Erzwingen von https- und https-URLs mittels Plugin

Geschrieben von   admin on    Januar 8, 2011

Es ist eine häufig auftretende Problemstellung bei Webanwendungen, dass einige Adressen der Website per https, andere aber per http aufgerufen werden sollen.

Wenn man eine Zend Framework MVC-Anwendung erstellt, dann sucht man in der Dokumentation zum Zend Framework (Reference Guide) vergeblich. Es bieten sich aber zwei Vorgehensweisen an:

  • per mod_rewrite URL-Rewriting-Anweisungen
  • per Controller-Plugin

Die Vorgehensweise mit URL-Rewriting-Anweisungen ist in einem weiteren Artikel beschrieben.

Was die Lösung durch ein Controller-Plugin angeht, so bin ich auf diese Lösung gestossen, die aus der Konfiguration die Angaben von Modul, ggf. zusätzlich mit Controller, und ggf. zusätzlich mit Action ausliest, um die Teile der Anwendung zu benennen, die eine sichere URL benötigen. Die Idee halte ich für schlicht genial!

;Definitionen für sichere URLs in application.ini
modules.default.private.require_ssl = true
modules.myarea.require_ssl = true
;Definitionen für URLS bei denen die https vs. http-Unterscheidung keine Rolle spielen *darf*
;Das Schema ist analog, daher nur ein Beispiel
modules.myarea2.ignore_ssl = true


Nach modules. folgt der Modulname, dann ggf. der Controllername, danach ggf. der Actionname. Genauer ist es bei der ursprünglichen Lösung beschrieben. Das dort vorgestellte Plugin entsprach aber nicht ganz meinen Bedürfnissen: Ich musste, zumindest für Zend Framework Version 1.11.x, die Ermittlung der Optionen ändern, da ich die Konfiguration nicht zusätzlich in der Registry des Zend Frameworks speichern wollte. Außerdem sah die Lösung keine Möglichkeit vor, von https- auf http-URLs zurückzulenken. Das von mir umgeschriebene Plugin lenkt nun sämtliche nicht als sicher definierten URLs auf die http-Entsprechung um, sofern der Aufruf mittels https-URL erfolgte.

Nachträglich musste ich feststellen, dass es auch URLs gibt, bei denen kein Zwangs-Redirect durchgeführt werden darf. Dazu gehören in erster Linie AJAX-Requests, wie z.B. bei den immer öfter zu sehenden Auto-Complete-Feldern (oft auch Auto Suggest genannt): Der Redirect könnte im JavaScript nicht, bzw. nicht ohne erhebliche Klimmzüge, verarbeitet werden. Um dies zu erreichen, müssen explizit ignore_ssl-Anweisungen definiert werden, siehe letzte Zeile im obigen Codebeispiel.

Hier das von mir umgeschriebene Controller-Plugin:

/**
 * Controller Plugin for forcing https-URLs for certain modules, controllers of modules and 
 * actions of controllers of modules. Rewritten on base of  the plugin by Travis Boudreaux
 * @package kumarisoft_Controller_Plugin
 * @author kumarisoft, Johannes Schlimm
 */
class kumarisoft_Controller_Plugin_Ssl extends Zend_Controller_Plugin_Abstract {
    /**
     * Check the application.ini file for security settings.
     * If the url requires being secured, rebuild a secure url and redirect if required.
     * Otherwise rebuild an unsecure url an redirect if required.
     *
     * @param Zend_Controller_Request_Abstract $request
     * @return void
     * @author kumarisoft, Johannes schlimm
     */
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        if (defined('APPLICATION_ENV') AND constant('APPLICATION_ENV') == 'testing') {
            return;
        }
 
        $shouldSecureUrl = false;
 
        $options = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getOptions();
 
        //check configuration file for one of three require_ssl directives
        //secure an entire module with modules.module_name.require_ssl = true
        //secure an entire controller with modules.module_name.controller_name.require_ssl = true
        //secure an action with modules.module_name.controller_name.action_name.require_ssl = true
        $optionSetAndNotFalse = (
            (isset($options['modules'][$request->module]['require_ssl'])
                AND $options['modules'][$request->module]['require_ssl']) OR
            (isset($options['modules'][$request->module][$request->controller]['require_ssl']) 
                AND $options['modules'][$request->module][$request->controller]['require_ssl']) OR
            (isset($options['modules'][$request->module][$request->controller][$request->action]['require_ssl'])
                AND $options['modules'][$request->module][$request->controller][$request->action]['require_ssl'])
        );
        if ($optionSetAndNotFalse) {
            $shouldSecureUrl = true;
        }
 
        $this->_redirectIfSecureDoesntMatch($request, $shouldSecureUrl);
    } // function preDispatch
 
    /**
     * Check the request to see if it's secure or not secure, just as wanted.
     * If it isn't, rebuild the appropriate url, redirect and exit; except for those havin ignore_ssl = true
     *
     * @param Zend_Controller_Request_Abstract $request
     * @param bool $secure
     * @return void
     * @author kumarisoft, Johannes Schlimm
     */
    protected function _redirectIfSecureDoesntMatch(Zend_Controller_Request_Abstract $request, $secure = true)
    {
        $server = $request->getServer();
        if (!isset($server['HTTP_HOST'])) { // not set when running unit tests (at least ZF unit tests)
            return;
        }
        $hostname = $server['HTTP_HOST'];
 
        $options = Zend_Controller_Front::getInstance()->getParam('bootstrap')->getOptions();
 
        // check configuration for one of the possible ignore_ssl directives. If on e is matched: do nothing
        $optionIgnoreSetAndNotFalse = (
            (isset($options['modules'][$request->module]['ignore_ssl'])
            AND $options['modules'][$request->module]['ignore_ssl'])
            OR (isset($options['modules'][$request->module][$request->controller]['ignore_ssl'])
            AND $options['modules'][$request->module][$request->controller]['ignore_ssl'])
            OR (isset($options['modules'][$request->module][$request->controller][$request->action]['ignore_ssl'])
            AND $options['modules'][$request->module][$request->controller][$request->action]['ignore_ssl'])
        );
        if ($optionIgnoreSetAndNotFalse) {
            return;
        }
 
        if ($request->isSecure() != $secure) {
            $pathInfo = rtrim(ltrim($request->getPathInfo(), '/'), '/');
            if ('' != $pathInfo) {
                $pathInfo .= '/';
            }
 
            // Url scheme is not the wanted one. So we rebuild url with unsecure scheme
            $url = ($secure ? Zend_Controller_Request_HTTP::SCHEME_HTTPS : Zend_Controller_Request_HTTP::SCHEME_HTTP)
                 . '://' . $hostname . '/' . $pathInfo;
 
            // Append query string when not empty
            if (isset($_SERVER['QUERY_STRING']) AND !empty($_SERVER['QUERY_STRING'])) {
                 $url .= '?' . $_SERVER['QUERY_STRING'];
            }
 
            /** @var Zend_Controller_Action_Helper_Redirector */
            $redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
 
            // Workaround for unit tests
            if ('testing' != APPLICATION_ENV) {
                $redirector->gotoUrlAndExit($url);
            } else {
                $redirector->gotoUrl($url);
            }
        }
    } // function _redirectIfSecureDoesntMatch
} // class kumarisoft_Controller_Plugin_Ssl

Kommentare

Geschrieben von Johannes Schlimm am
Nachtrag: Aktuellen Berichten zufolge will Google dazu übergehen, Websites, deren Inhalt ausschließlich per https erreichbar ist, besser zu gewichten. Von daher empfiehlt es sich wohl eher, die ganze Website auf https laufen zu lassen.
Hintergrund: Beim Hin- und Herspringen zwischen http- und https-URLs ergibt sich eine Sicherheitslücke, sofern Cookies verwendet werden. Sitzungs-Cookies lassen sich nämlich bei http durch "Man in the middle"-Attacken abgreifen und anschließend missbrauchen, selbst wenn Formulare per https gesichert übertragen werden. Ein erfolgreicher Angreifer könnte so eine fremde Identität gegenüber Shop-Systemen usw. annehmen.
Kommentar schreiben



(Ihre E-Mail-Adresse wird nicht angezeigt.)


Ungültiger Sicherheitscode

Bitte klicken Sie das Bild an, um einen neuen Sicherheitscode zu laden.