TYPOlightでコンテンツの一部にSSLを使用する

現在は、SslRedirectが大変良くなっていますので、こちらの情報は使わないで下さい!

TYPOlight webCMSは、基本機能の中では、コンテンツの一部にSSLを利用することはできません。本家フォーラムで機能拡張を使うことによって一部のコンテンツだけSSLで動作するように設定ができます。

ここでは、その機能拡張の使い方と、それでは対応できないケースへの対策を紹介します。

※このモジュールの場合phpスクリプトの中で、location:でジャンプさせる形式ですので、その点はご注意下さい。

ページや機能単位でSSLを使う機能拡張 SslRedirect

本家フォーラムのトピックスFlag to set if a page requires (is forced to use) SSLExtension:SslRedirectで公開されている機能拡張の紹介をしています。ダウンロードは上記トピックスまたは、いつまであるかわかりませんが旧Extensionリスト内にある、extention:SslRedirectから行えます。(2008/11/30現在)

なお、私が利用を見当した時点での問題点は次の通りです。

  • この機能拡張は残念ながら2.6.2以降で公開されているエクステンションカタログには追加されていないので、2008/11/30現在は利用のために、FTPでモジュールをアップロードする必要があります。
  • デフォルトのテンプレートのbaseタグの使用法は、フォームをHTTPプロトコルで送信してしまうのでテンプレートの確認が必要です。
  • 複数のドメインそれぞれに別のSSLを設定する場合は、通常のサブドメインとSSL通信を行うサブドメインが等しくなければ行けません。
  • SSLのURLが特殊な形になることが多い共用SSLには対応していません。

使用方法を簡単に紹介します。

  1. モジュールを作成する
    モジュールの作成
    設定は名前をつけてモジュールタイプを選ぶだけでOK
    モジュールの設定画面
  2. モジュールを配置する
    • ページレイアウトにモジュールを追加する方法(複数のコンテンツに一度に設定できる)
      左メニューの「レイアウト>ページレイアウト」を選択後、SSL通信で表示させたいページレイアウトを選択して次のように設定。
      tl_files/typolight/ssl/set_pagelayout.png
    • アーティクルにモジュールを追加する方法(都度追加する場合)
      SSLのモジュールを選んだスクリーンショット

どちらの設定でも、指定ページにアクセスすると自動的にhttps://から始まるURLに転送されます。

大変便利なのですが、Coreserver/Xreaのような共用SSLでメールフォームの送信さえできないので修正ファイルを作ってみました。

共有SSL・baseタグに対応した改良版

現在このサイトのお問い合わせをSSLを通して使うために使っているモジュールです。次のような共用SSLでも利用することができます。

  • 表示するサイトのサブドメインと全く異なるサブドメイン支配下のディレクトリにあるようなURLでアクセスしなければならない
  • PHPではHTTPSプロトコルでデータが来たことが取得できない(転送されている)

これは、localconfig.phpの設定を増やして、ちょっとだけソースを変更することで対応しました。実際のコード(修正部分だけ抜粋)です。

TL_ROOT…TYPOlightをインストールしたフォルダのこと

修正したコンストラクタ部分だけ

	public function __construct () {
		if (TL_MODE != 'BE' && !isset ($_SERVER['HTTPS']) /*ここから追加*/&& !isset ($_SERVER['HTTP_VIA']) /*ここまで追加*/) {
			// Redirect to secure connection (beware of rewrite) and check if there
			// is a custom domain specified for secure connections
        	$url = 'https://' . 
        		(
        			isset ($GLOBALS['TL_CONFIG']['sslRedirectDomain'])
        			    ? $GLOBALS['TL_CONFIG']['sslRedirectDomain'] : $_SERVER['HTTP_HOST']
				) /*ここから追加*/. (
					isset ($GLOBALS['TL_CONFIG']['sslRedirectDomainOptionDir'])
						? "/".$GLOBALS['TL_CONFIG']['sslRedirectDomainOptionDir'] : ""
				)/*ここまで追加*/ . $_SERVER['REQUEST_URI'];
        	
        	// Anything already sent?
        	if(!headers_sent ()) {
        		header ('Location: '. $url);
        	} // end if
		} // end if
	}

変更後のTL_ROOT/system/config/localconfig.phpの設定(オプション)

/* オリジナルからある設定。HTTPSのサブドメインと通常のドメインが違う場合設定 */
$GLOBALS['TL_CONFIG']['sslRedirectDomain']="ssldomain";
/* 追加設定。Coreserver/Xrea/アイルの共用サーバなど。
   HTTPSのときサブディレクトリーが必要な場合 */
$GLOBALS['TL_CONFIG']['sslRedirectDomainOptionDir']="subdirectory";

さらにPOSTができるように変更しておく

ここまででだいたいはうまくいくのですが、TYPOlightではformタグのアクションはサイトドメイン直下からの相対指定です。ページレイアウトファイルでbaseを設定することで大概のデザインはつじつまを合わせているはずなので、フォームが正常に送信できません。次のような現象が起きます。

  • baseタグはhttp://サイト構造で設定したドメイン OR アクセスしたドメイン/で設定されるので、POSTしたURLがHTTPSと認定されずlocationでジャンプしてしまう。
  • 共用SSLの場合、フォームのアクションと現在のドメインが異なるのでTYPOlightのリファラを確認する設定に阻まれてデータがPOSTできなくなってしまう。

そこで、フォームのアクションのURLをHTTPSに書き換える処理と、TYPOlightのコアの代わりにrefererを確認する処理を加えます。フックで実現します。フックのタイミングなどは開発者向けドキュメント(日本語訳)を確認して下さい。

フックの設定は、モジュール支配下のクラスファイルと設定ファイルで行います。

フォルダ構造

モジュールフォルダ
 ├config
 │ └config.php(フックの設定を書く)
 ├ModuleSslRedirect.php(コンストラクタでlocationを使ってジャンプ処理しているんで今回は利用しない)
 └CorrectRequest.php(フックで呼び出すメソッドだけを定義しているクラス)

CorrectRequestクラス

<?php
/**
 * check referer instead of TYPOlight core's and form's actoin via HTTPS
 * @author Risa Yuguchi<risa@r-studio.jp>
 * @copyright 2008 RsStudio
 */
class CorrectRequest {

	/**
	 * constructer
	 */
	public function __construct () {}
	
	/**
	 * check referer instead of TYPOlight core's
	 * @param $arrPost Array from form POST
	 * @param $arrForm
	 * @param $arrFiles
	 */
	public function processFormData ($arrPost, $arrForm,$arrFiles) {
		//環境クラスを取得
		$objEnvironment = Environment::getInstance();
		//HOSTを比較するために、現在のホストとリファラからホスト情報を取り出す
		$self = parse_url($objEnvironment->url);
		$referer = parse_url($objEnvironment->httpReferer);
		if (!strlen($referer['host']) || $referer['host'] != $self['host'])
		{
			if (!isset ($GLOBALS['TL_CONFIG']['sslRedirectDomain']) || $referer['host'] != $GLOBALS['TL_CONFIG']['sslRedirectDomain']) {
				trigger_error(sprintf('The current host address (%s) does not match the current referer host address (%s)', $self['host'], $referer['host']), E_USER_ERROR);
				exit;
			}
		}

	}

	public function outputFrontendTemplate($strContent, $strTemplate)
	{
		$objEnvironment = Environment::getInstance();
		$self = parse_url($objEnvironment->url);
		$referer = parse_url($objEnvironment->httpReferer);
		if (isset ($_SERVER['HTTPS']) || isset ($_SERVER['HTTP_VIA'])) {
			if (isset ($GLOBALS['TL_CONFIG']['sslRedirectDomain'])) {
	        	$url = 'https://' . 
        		(
        			isset ($GLOBALS['TL_CONFIG']['sslRedirectDomain'])
        			    ? $GLOBALS['TL_CONFIG']['sslRedirectDomain'] : $_SERVER['HTTP_HOST']
				) . (
					isset ($GLOBALS['TL_CONFIG']['sslRedirectDomainOptionDir'])
						? $GLOBALS['TL_CONFIG']['sslRedirectDomainOptionDir'] : ""
				);
        	
				$replaceStr = '<form action="' . $url .'/';
				$strContent=preg_replace('#<form action="#i',$replaceStr,$strContent);
			}
		}

		return $strContent;
	}
}
?>

config/config.php

<?php
if (!defined('TL_ROOT')) die('You can not access this file directly!');

// Add the module as frontend module
$GLOBALS['FE_MOD']['miscellaneous']['SSL-Redirect'] = 'ModuleSslRedirect';
// Add hooks
$GLOBALS['TL_HOOKS']['processFormData'][] = array('CorrectRequest', 'processFormData');  
$GLOBALS['TL_HOOKS']['outputFrontendTemplate'][] = array('CorrectRequest', 'outputFrontendTemplate');  

?>