Website von Henning Pingel
 
TYPO3 Backend Via SSL Proxy

Breaking news: TYPO3 4.2 has reverse proxy support

If you use TYPO3 version 4.2.x, you don't need the patch below any more. Masi has added some code to the core to get it work. New options are available in the install tool:

  • reverseProxyIP: list of IP addresses. If TYPO3 is behind one or more (intransparent) reverese proxies the IP addresses must be added here
  • reverseProxyHeaderMultiValue: "none","first","last": defines which values of a proxy header (eg HTTP_X_FORWARDED_FOR) to use, if more than one is found. "none" discards the value, "first" and "last" use the first/last of the values in the list.
  • reverseProxyPrefix: optional prefix to be added to the internal URL (SCRIPT_NAME and REQUEST_URI).
  • reverseProxySSL: '*' or list of IP addresses of proxies that use SSL (https) for the connection to the client, but an unencrypted connection (http) to the server. If '*' all proxies defined in SYS[proxyIP] use SSL.
  • reverseProxyPrefixSSL: prefix to be added to the internal URL (SCRIPT_NAME and REQUEST_URI) when SSL accessing the server via an SSL proxy. This setting overrides SYS[proxyPrefix].

Check bug 7397 on the bugtracker for further details. (I currently don't have time to add details here.)

Abstract

This is a tutorial on how to enable the TYPO3 backend to be accessed via a SSL proxy through manual changes to the TYPO3 php file class.t3lib_div.php. At this time the changes suggested here are not meant to be used on a productive environment. The code changes weren't tested by many users yet so it is possible that there are hidden errors in it. It is planned to integrate the ideas from patches that other TYPO3 developers have already provided on the TYPO3 bugtracker regarding reverse proxies. Finally it should hopefully lead to code that can be integrated into the TYPO3 core.

 

If you have comments, questions or ideas regarding this tutorial please add a comment at the end of this page.

 

I started writing this tutorial on January 09, 2007. It was last updated on September 10, 2008. I regret the tutorial is not available in German language.

Introduction

Many web hosting service providers offer low-cost web space packages that are good enough to host a few small websites based on TYPO3. One drawback of a low-cost solution is that encryption of the communication between client and server via SSL/HTTPS is not (fully) included. The webmaster of a webspace package either has only limited freedom of configuring the web server - so that he can't set it up for HTTPS, or he may not be willing to pay the amount of money that an individual SSL certificate will cost.

 

Some web hosting agencies offer so called SSL proxies to be used as an alternative to a full-featured encryption solution with an own SSL certificate. A small hobby website project may never ask its users to submit credit card numbers or other personal information, so SSL encryption is not necessarily must-have in that regard. But the adminstrative interface of the content management system - in our case the TYPO3 backend - seems to be a sensible part of the website. If username and password for a backend user's account or the admin-password or the install tool password get's captured in some way by another person, this might involve some sleepless nights for the webmaster.

 

TYPO3 is of course able to deal with SSL/HTTPS, there is core functionality for this and there are even TYPO3 extensions like https_enforcer too that make it easier to set up some frontend pages to be only accessed encrypted. But something is still missing: Support for SSL proxies (or more general: reverse proxies) is not included yet in the TYPO3 core and not available as an extension. Until now it is not possible for owners of low cost webspace to have this out-of-the-box without any further modifications of the setup. Or to put it in other words: The TYPO3 backend does not work via a SSL proxy.

 

Let's change it.

 

If you want to follow the steps of this tutorial to see how the suggested code change works for you, please give me a feedback via a comment or an email. Please tell me if it works for you - or what does not. Without feedback the coding will not be improved and will therefore probably not integrated into the core.

Important notes

Before you read this tutorial or try to implement any code shown here to your own TYPO3 installation please keep the following notes in mind:

  • Work done so far by others: Recently I discovered that long ago others like Christian Boltz or Daniel Pötzinger have done good work on this backend access problem and have already provided patches. But I'm afraid that all these patches didn't make it into the TYPO3 core yet. There are three bugs filed in TYPO3 bugtracker. I will try to integrate the work that has already been done into this tutorial. The relevant bugs I found are:
  • Caution: WORK IN PROGRESS! This tutorial is not a finished, polished product, it has beta status. If the code will once be proven by tests I will add it to the TYPO3 bugtracker. There are still bugs in the code I provide. Don't use the code in a productive environment. I tested my code changes using TYPO3 4.0.4 / Apache 1.3.x / PHP 4.x on a Webpack hosted by Hosteurope.de. I don't know if it works with other setups of other webhosters.
  • Backend only: At this time, the tutorial is only meant to enable SSL access to the TYPO3 backend. I don't concentrate on testing an encrypted TYPO3 frontend via an SSL proxy at this time. It is likely that frontend access via SSL proxy will not work properly. Previewing frontend pages from out the backend is not working properly yet. Some frontend extensions don't work properly with the changed settings.
  • Multiple domains on one TYPO3 system: Be aware that if you provide multiple websites using one TYPO3 installation with several domain records you have to do far more tests to see if things work for you. For my setup it seems to work fine.
  • Avoiding solution using vhost/mod_rewrite: There are solutions for the problem that are based on mod_rewrite rules. In this tutorial I don't want to use this way to solve the problem because I think it is unnecessary to use mod_rewrite for this purpose if we make some changes to TYPO3's php coding itself. If you want more information about other approaches that leave the TYPO3 core coding untouched check these links:
  • Possible troubles with other extensions: Please also keep in mind that some TYPO3 extensions (for frontend or backend etc.) might still prevent you to use the SSL proxy for backend (or frontend) access because they weren't designed to work using an SSL proxy. Maybe I'm wrong but I got the impression that one frontend extension I use makes it impossible to get access to the frontend through the SSL proxy.
  • I can't provide a TYPO3 extension for this: If you want to try the code snippet provided here you have to apply it yourself to the php classes of your TYPO3 system. It is not possible to provide an easy-to-install extension because the TYPO3 class we need to change is not extendable via the TYPO3 XCLASS mechanism (Have a look here for further information). I regret I don't have any time to assist you in case you destroy your TYPO3 system when applying the code snippets.

What is a SSL proxy server?

A SSL proxy server is a form of reverse proxy server that encrypts the webpages using SSL before delivering it. In this tutorial I will use the terms "reverse proxy" and "SSL proxy" as synonyms. Keep in mind that you can use reverse proxies for other purposes too.

 

Learn more about proxy servers here:

http://en.wikipedia.org/wiki/Proxy_server

 

There are plans to improve the performance of the website typo3.org through reverse proxy servers. This has nothing to do with SSL proxies, but if you are interested, please check this web page:

http://wiki.typo3.org/index.php/TYPO3.org_-_improvements

 

Status Quo: TYPO3 doesn't notice a reverse proxy

The checks of TYPO3 regarding the settings of environmental variables don't notice if a page was requested through a SSL or reverse proxy. Basically the value of HTTP_HOST stays the same regardless if the web page delivered by TYPO3 was requested through a reverse proxy or not. Since TYPO3 trusts the value of HTTP_HOST it has no chance to notice the presence of a SSL proxy. Because of that the links and HTTP redirects of TYPO3 are generated wrongly. The user is redirected to a non-HTTPS page or stuck in a loop.

 

Luckily we are able to detect the presence of a reverse proxy through the php $_SERVER environment variables.

Learn more about your SSL proxy server

Even if you know that your web hoster offers a SSL proxy it is not clear which proxy server product is used. It will most likely be Apache/mod_proxy, but there is a chance that it might be a different product. You should find out about it because not all products can be detected in the same way.

 

If it is not documented which kind of SSL proxy server is used you may be able to detect the type of the reverse proxy through the HTTP header that the proxy sends you if you request a page that does not exist.

 

If you use Mozilla Firefox you can install the browser extension LiveHTTPHeaders  to  view the HTTP header. Example: Your SSL proxy host name is

 

ssl.mysslproxy.com

 

Normally you reach your website through the proxy via

 

https://ssl.mysslproxy.com/www.mydomain.com

 

But to see the SERVER-String of the proxy you have to request a page from the proxy that is not part of your own domain (or any other domain). You just want to be sure that the page you request now comes straight from the proxy and is not passed through the proxy from another web server. The safest solution is to ask for a page that does not exist. It could look like this:

 

https://ssl.mysslproxy.com/this-page-definitly-doesnt-exist.html

 

Use Firefox with LiveHTTPHeaders to request the described kind of non existing page. Right click on the web page (probably a 404 error message) and choose "View Page Info" from the context menu. The extension has inserted a tab called "Header" into the tab strip. Within this tab you will find the table "Response". In there hopefully is a row with the parameter "SERVER" and a value for it.

 

If you're lucky you will find out that the proxy server is based on Apache/mod_proxy with OpenSSL. Then it should like this line:

 

Apache/x.x.x (Unix) Debian GNU/Linux mod_ssl/x.x.x OpenSSL/x.x.x

 

(The x's are placeholders for version numbers.)

Detect Apache/mod_proxy using PHP

The Apache module mod_proxy adds three lines to each HTTP header that is sent to the ordinary web server as part of each request.  Those requests contain (amongst others) three special parameters in the HTTP header (the values of these parameters are explained below):

  • X-Forwarded-For,
  • X-Forwarded-Host,
  • X-Forwarded-Server.

If you are curious where these lines come from please take a look at the source code of the Apache module proxy/mod_proxy_http. Search for the string "X-Forwarded", for example in the file mod_proxy_http.c.

 

If these HTTP headers are present, the ordinary web server turns them into three server variables to make them accessible for us through the PHP array $_SERVER. We have to use these array entries to detect the presence of the Apache based reverse proxy:

  • HTTP_X_FORWARDED_FOR (contains IP address of user. Normally we use REMOTE_ADDR for this, but in case of a reverse proxy REMOTE_ADDR contains the IP of the proxy),
  • HTTP_X_FORWARDED_HOST (contains proxy host name),
  • HTTP_X_FORWARDED_SERVER (contains proxy server name, in most cases probably the same value as HTTP_X_FORWARDED_HOST).

But we must not trust the assumption that the request came from our reverse proxy just because these three parameters are there and their values look all right. The content of these values can easily be faked using any other reverse proxy server or - even easier - using tools to generate HTTP requests like cURL.

 

To make sure that only the expected reverse proxy is talking with our TYPO3 system we have to check if the IP address in REMOTE_ADDR is the one of our proxy server.

 

However we are not able to see if the reverse proxy encrypted the web page before it was delivered. (This is important, because we have to be aware to not only think of the protocol HTTPS, because it could be just an HTTP reverse proxy.)

Other proxy server products (Squid, Varnish, ...)

Besides Apache/mod_proxy there are many other proxy server products that are dedicated reverse proxy servers (Varnish) or may be used as a reverse proxy (Squid). But when a SSL proxy is needed, Apache/mod_proxy will most likely be the choice. Sqid and Varnish don't support HTTPS natively, other tools have to be used in addition to achieve that.

 

My impression is: The HTTP header entries created by Apache/mod_proxy I listed above may not be the same using other proxy servers. So the code snippet below may only work if Apache/mod_proxy is used as a proxy server.

 

The header "X-Forwarded-For" seems to be used by all three proxy servers. But it seems that Squid and Varnish don't use "X-Forwarded-Host" and "X-Forwarded-Server". For further information please check the Squid sources, especially the file  HttpHeader.c, or the source code of Varnish.

What needs to be changed in TYPO3?

The most important function for our purpose is function getIndpEnv($getEnvName) in class.t3lib_div.php (linked is the trunk version in repository).

 

This function is responsible for providing all information of the web server's environment that TYPO3 needs to know (independent from which web server is being used). All the spots where the function relies on the value of variable HTTP_HOST are failing in case of a SSL proxy. Those parts have to be changed to make them aware of the SSL proxy. This is the case with at least the following parameters handed over to the function via $getEnvName:

  • HTTP_HOST (does not contain the host name of the proxy server, for correct link generation this has to be replaced by HTTP_X_FORWARDED_HOST)
  • TYPO3_REQUEST_HOST
  • HTTP_REFERER
  • TYPO3_HOST_ONLY

Besides these the processing of more values has to be changed even if they don't contain the host name:

  • SCRIPT_NAME (folder name needs to be added)
  • REQUEST_URI (folder name needs to be added)
  • TYPO3_SSL (If the reverse proxy uses SSL encryption we have to force this to TRUE in case we see that HTTP_X_FORWARDED_HOST is not empty because TYPO3 and PHP have no way to find out that a) the delivered page will be encrypted by the reverse proxy and b) that protocol HTTPS is used instead of HTTP.)
  • TYPO3_PORT
  • REMOTE_ADDR (contains IP of SSL proxy server instead of IP of user, we have to use environmental variable HTTP_X_FORWARDED_FOR instead)

Workaround to test the backend through a SSL proxy

The following instructions are meant as a temporary workaround to make it easy to enable SSL Proxy backend support for testing purposes on your TYPO3 4.1.1 installation without the need to change a lot of coding in a lot of different places. The patch provided below will only change class.t3lib_div.php.

 

The existing function getIndpEnv() will be renamed to getIndpEnvClassic(). A new wrapper function named getIndpEnv() will be added that will return the values needed for successfully using a SSL proxy. If no reverse proxy is detected the wrapper function will then call the function getIndpEnvClassic().

 

The patch only works with TYPO3 version 4.1.1. It is not a well designed solution meant to be integrated into the TYPO3 core. It's only a "fast-food" product. Be aware that an upgrade to a newer version of TYPO3 will overwrite your manual changes. Also be aware that this code will not disable unencrypted HTTP access to the backend! Without using the SSL Proxy you can login without SSL through using own domain names. To disable HTTP access have a look at the TYPO3 configuration parameter lockSSL in your Install Tool (or in localconf.php).

 

Please follow these steps to enable SSL proxy support for your TYPO3 backend for testing purposes:

  1. Backup the file /t3lib/class.t3lib_div.php of your TYPO3 4.1.1 installation to be able to get rid of the changes you are making now in case of a problem.
  2. Download the patch displayed below and apply it to your TYPO3 4.1.1 installation. (Typo3.org provides a short tutorial on using the diff/patch tools.) The file /t3lib/class.t3lib_div.php will be modified.
  3. After applying the patch open file /t3lib/class.t3lib_div.php to change the IP address of the used reverse proxy in line 3024: Replace the string 0.0.0.0 in the line $sslProxyIP = '0.0.0.0'; with the local IP address of your SSL proxy (see value of $_SERVER['REMOTE_ADDR']). Don't take the external IP address (not the one that you get via ping ssl.mysslproxy.com).
  4. Save the file (and upload it if necessary).

Use this patch in step 2 (you can also download the patch as a file from here):

diff -ru t3lib.411.original/class.t3lib_div.php t3lib.411.reverseproxypatch/class.t3lib_div.php
--- t3lib.411.original/class.t3lib_div.php	2007-04-14 11:45:06.436670400 +0200
+++ t3lib.411.reverseproxypatch/class.t3lib_div.php	2007-04-14 13:03:56.077560000 +0200
@@ -3007,6 +3007,63 @@
 	}
 
 	/**
+	 * This patch should not be used in a productive environment.
+	 * 
+	 * Wrapper method (Version 0.06 from 2007/04/14)
+	 * for old getIndpEnv that has been renamed to getIndpEnvClassic
+	 * This patch should not be used in a productive environment.
+	 * 
+	 * This function serves as a temporary workaround to enable 
+	 * SSL proxy support in TYPO3 4.1.1. You have to change the value 
+	 * of $sslProxyIP manually to the IP address of you reverse proxy.
+	 * 
+	 * More information regarding this method you will find here:
+	 * http://www.henningpingel.de/TYPO3-Backend-Via-SSL-Proxy.124.0.html
+	 */
+	function getIndpEnv($getEnvName) {
+        $sslProxyIP = '0.0.0.0'; //fill in the local IP address of your SSL proxy here
+		$retVal = '';	   
+		if ($_SERVER["REMOTE_ADDR"] == $sslProxyIP && $_SERVER["HTTP_X_FORWARDED_HOST"] != ''){ // && TYPO3_MODE=='BE'){
+			$sslProxy_folder = $_SERVER['HTTP_HOST'];
+			$sslProxy_host = $_SERVER['HTTP_X_FORWARDED_HOST'];
+			switch ((string)$getEnvName)	{
+				case 'HTTP_HOST':
+					$retVal = $sslProxy_host;
+				break;
+				case 'SCRIPT_NAME':
+				case 'REQUEST_URI':
+					$retVal = '/' . $sslProxy_folder . t3lib_div::getIndpEnvClassic($getEnvName);
+				break;
+				case 'TYPO3_REQUEST_HOST':
+					$retVal = 'https://'. $sslProxy_host; //this is now forced to https, this is a problem if reverse proxy does not use SSL
+				break;
+				case 'TYPO3_SSL':
+					$retVal = TRUE;//this is now forced to https, but this means a problem if the reverse proxy does not use SSL
+				break;
+				case 'TYPO3_HOST_ONLY':
+					$p = explode(':',$sslProxy_host);
+					$retVal = $p[0];
+				break;
+				case 'TYPO3_PORT':
+					$p = explode(':',$sslProxy_host);
+					$retVal = $p[1];
+				break;
+				case 'REMOTE_ADDR':
+					$retVal =  $_SERVER["HTTP_X_FORWARDED_FOR"]; //this contains ip address of enduser, REMOTE_ADDR contains IP of proxy
+				break;
+				default:
+					$retVal = t3lib_div::getIndpEnvClassic($getEnvName);
+			}
+		}
+		else{
+			$retVal = t3lib_div::getIndpEnvClassic($getEnvName);
+		}		
+		//error_log("debug getIndpEnv case :" . $getEnvName . " - " . $retVal); //enable this for debugging purpose
+		
+		return $retVal;
+	}
+
+	/**
 	 * Abstraction method which returns System Environment Variables regardless of server OS, CGI/MODULE version etc. Basically this is SERVER variables for most of them.
 	 * This should be used instead of getEnv() and $_SERVER/ENV_VARS to get reliable values for all situations.
 	 * Usage: 221
@@ -3014,7 +3071,7 @@
 	 * @param	string		Name of the "environment variable"/"server variable" you wish to use. Valid values are SCRIPT_NAME, SCRIPT_FILENAME, REQUEST_URI, PATH_INFO, REMOTE_ADDR, REMOTE_HOST, HTTP_REFERER, HTTP_HOST, HTTP_USER_AGENT, HTTP_ACCEPT_LANGUAGE, QUERY_STRING, TYPO3_DOCUMENT_ROOT, TYPO3_HOST_ONLY, TYPO3_HOST_ONLY, TYPO3_REQUEST_HOST, TYPO3_REQUEST_URL, TYPO3_REQUEST_SCRIPT, TYPO3_REQUEST_DIR, TYPO3_SITE_URL, _ARRAY
 	 * @return	string		Value based on the input key, independent of server/os environment.
 	 */
-	function getIndpEnv($getEnvName)	{
+	function getIndpEnvClassic($getEnvName)	{
 		/*
 			Conventions:
 			output from parse_url():
@@ -3284,7 +3341,9 @@
 	function getHostname($requestHost=TRUE)	{
 		$host = '';
 		if ($requestHost && (!defined('TYPO3_cliMode') || !TYPO3_cliMode))	{
-			$host = $_SERVER['HTTP_HOST'];
+			//$host = $_SERVER['HTTP_HOST']; //old version, why is getIndpEnv not used for this???
+			$host = getIndpEnv('HTTP_HOST');
+			//error_log("hepi: getHostname was called"); //enable this for debugging purposes
 		}
 		if (!$host)	{
 				// will fail for PHP 4.1 and 4.2

To be continued

This is a work in progress article. I have only limited time to work on it. Please be patient. Stay tuned. There are things that are not covered in here yet, for example:

  • I know that we should put the IP address of the SSL proxy into the localconf.php.
  • If there are more than one SSL proxys (load balanced) the IP address check needs to accept more than one ip address.
  • Extensions have to be checked if they access $_SERVER directly.
  • misc/phpcheck/incfile.php has to be cared for too.
  • From Apache version 2.0.31 on the module mod_proxy has a ProxyPreserveHost Directive. This shouldn't be set to ON (OFF is the default). See: http://httpd.apache.org/docs/2.0/mod/mod_proxy.html#proxypreservehost
  • Could we also use mod_proxy_http to rewrite absolute links in backend html sources? (http://apache.webthing.com/mod_proxy_html/)
  • To make page preview work via SSL we have to enable the stuff for the frontend too. Also there has to be created an additional domain record for the website(s) containing the ssl proxy host name and the subdirectory name. This domain record should be the first domain in the defined list of domains. Now it should work via SSL, but on the other hand it won't work any more via HTTP.
  • THIS WAS ALREADY FIXED IN CORE:I know that in class.t3lib_div.php there is at least one more line to be changed in function getHostname($requestHost=TRUE) because $_SERVER['HTTP_HOST'] is accessed directly there.

If you have questions or feedback regarding this tutorial please add a comment at the end of this page or send me an email:

henningT3(at)henningpingel.de

 

Best regards

Henning

 

Any Comments? Please add them here!

4 Comments
#1 Henning wrote at 14.01.2007 22:43 email homepage

Hi,

I have installed this nice TYPO3 extension to add comments to this page. Please use it! Razz

Cheers,
Henning

#2 steffen wrote at 14.01.2007 23:21 email homepage

nice solution, i will check it out when i will find some time.
Maybe Christian is sending a comment too Big Grins

btw - nice commenting system Big Grins Cool

#3 Siddhartha wrote at 19.01.2007 12:17 email homepage

Hey Henning,

I am happy to see this progress you make in the arena of Typo 3! Sharing solutions is a noble task indeed! smile

Keep it up and keep the typos at bay. zwinker

Ciao,
Sid!

#4 ede wrote at 14.02.2012 19:13 email

the limitation of the 'new' reverse proxy code to a list of known ip's is useless when dealing with rotating lists like strato's https://ssl-id1.de/<my.domain.de>

i worked around that by convincing typo3 that the presence of HTTP_X_FORWARDED_HOST alone signals that a reverse proxy is in the line. i pretty much did so by assigning the settings reverseProxyIP and reverseProxySSL the proxy's ip, which is stored in $_SERVER['REMOTE_ADDR'].

here's the code

t3lib/class.t3lib_div.php
[CODE]
public static function getIndpEnv($getEnvName) {
...

$retVal = '';

/* Needed config values as of version 4.5.4
* $TYPO3_CONF_VARS['SYS']['reverseProxySSL'] = '*';
* $TYPO3_CONF_VARS['SYS']['reverseProxyPrefixSSL'] = '/group-of-pictures.com';
* $TYPO3_CONF_VARS['SYS']['reverseProxyHeaderMultiValue'] = 'first';
*/
if ($_SERVER["HTTP_X_FORWARDED_HOST"] != ''){
$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxyIP'] = $_SERVER['REMOTE_ADDR'];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['reverseProxySSL'] = $_SERVER['REMOTE_ADDR'];
}

switch ((string) $getEnvName) {
... here comes the big switch block
[/CODE]

Don't forget to add the necessary configuration as described i8n the code above!!!

For initial redirection of the backend i use a mod rewrite config similar to Horst's in #6 above.

Hope this helps someone... ede