共有SSLをWordPressのサイトで使う

サイトを構築すると、「お問い合わせページ」など個人情報を扱うページはSSL接続(https://で始まる暗号化された接続)にしたい、と考えます。本稿では、さくらレンタルサーバー上で独自ドメインにて運営している、WordPressで構築したサイトにおいて、お問い合わせページ等を共有SSLにする方法を考えます。

本稿で述べる方法は、2013年9月27日時点で確認しておりますが、各位の環境やさくらインターネットさんの仕様変更によりうまく動作しない場合が多々考えられます。ですので、お試しになる場合は自己責任でお願いいたします。

はじめに

これまで2回にわたり.htaccessの編集のお話をしました。

私が.htacessの編集で苦しむに至ったきっかけは、サイトのお問い合わせページをSSLにしようとしたことがきっかけです。

サイトは独自ドメインで運用していますが、さくらレンタルサーバーでは、契約とともに頂けるさくらインターネットのサブドメイン(user.sakura.ne.jp:userは各ユーザー名)で共有SSLが利用できます。(以降、このサブドメインを「初期ドメイン」とします。)

初期ドメインに独立したお問い合わせページを作ってもいいのですが、サイトとのデザイン統合やユーザビリティに難があります。(お問い合わせページからも、サイトと同様のメニューで他のページに飛びたい~)

「WordPressで作ったサイト内のお問い合わせページだけ、初期ドメインの共有SSLにすることは可能なのでは?」

思えばこの発想がすべての悪夢の始まりでした。

これから書こうとする手段は、恐らく共有SSLを提供している他のレンタルサーバーでも応用可能と思います。しかし弩嵌り(どはまり)しそうであれば、サーバー証明書を取得して独自ドメインでSSL接続を提供することをおすすめします。(さくらさんにしても、今後の仕様がどうなるかわかりません。)

そのコストが惜しいならサーバー証明書なしでSSLにするか、それに対するブラウザの警告もあるのでいっそのことSSLを使わないことも選択できます。(個人情報のセキュリティに関しては後程考察したいと思います。)

環境説明

さくらレンタルサーバーの環境説明をいたします。

サイトは取得した独自ドメインにて運営しています。そのドキュメントルートは、初期ドメインのドキュメントルートの中に作成したディレクトリで、そこにWordPressをインストールしています。そのディレクトリを’wp’とすると以下の様になります。
/home/user/www/ ←初期ドメインのドキュメントルート
/home/user/www/wp/ ←独自ドメインのドキュメントルート。WordPressのルート

つまり意図せず初期ドメインから独自ドメインのサイトが覗けます。

そこで、独自ドメインのサイト内のパスが/secure/で始まるページをセキュアなページとして、そこへのアクセスを、全て初期ドメインのSSL接続にすることを考えます。

wp内に置いた.htaccessで、/secure/で始まるURLをhttps://初期ドメインへ、その他のURLをhttp://独自ドメインへリダイレクトしてやるだけです。(と最初は思いました。)

前提条件

まず「.htaccessのRewriteRuleを編集する前に」に書いた理解とテストを行います。

すると「さくらレンタルサーバーの共有SSLを使う」に書いたようなことが発見できます。

これらの前提の上で以下をお読みください。

特にFireFoxの環境について、先の記事では活躍しませんでしたが、今回はリダイレクトしまくりなので大活躍します。

また以降の説明はWordPressにマルチサイトを導入したうえでの説明になります。マルチサイトにしていない場合はもっとシンプルかと思います。

.htaccessの記述

私の試行錯誤はさておいて、対処方法をご説明いたします。

wp/.htaccessの記述は以下の様になります。

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /

# 2013.08.28 nagata
# 以下、初期ドメイン(user.sakura.ne.jp)の場合の処理

# secure以下へのhttpアクセスはhttpsにリダイレクト
RewriteCond %{HTTP_HOST} ^[_\-0-9A-Za-z]+\.sakura\.ne\.jp$
RewriteCond %{ENV:HTTPS} !^on$
RewriteRule ^secure/(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

# index.phpはスルー
RewriteCond %{HTTP_HOST} ^[_\-0-9A-Za-z]+\.sakura\.ne\.jp$
RewriteRule ^index\.php$ - [L]

# secure以下へのアクセスは、index.phpに書き換え
RewriteCond %{HTTP_HOST} ^[_\-0-9A-Za-z]+\.sakura\.ne\.jp$
RewriteRule ^secure/(.*)$ /wp/index.php [L]

# 上記以外はhttp://独自ドメインにリダイレクト
RewriteCond %{HTTP_HOST} ^[_\-0-9A-Za-z]+\.sakura\.ne\.jp$
RewriteRule ^(.*)$ http://独自ドメイン/$1 [R,L]

# 以上、初期ドメインの場合の処理
# 以下、独自ドメインの場合の処理

# secure以下へのアクセスは、https://初期ドメインにリダイレクト
RewriteRule ^secure/(.*)$ https://user.sakura.ne.jp/wp/secure/$1 [R,L]

# 以上、独自ドメインの処理
# 以下はオリジナル。
(後略)

(「secure」、「wp」、「http://独自ドメイン」、「user.sakura.ne.jp/wp/secure」などは、各位の環境に合わせて編集が必要です。)

ポイントとしては、初期ドメインについてのルール、独自ドメインについてのルール、オリジナル(WordPressが自動作成したルールと、マルチサイトにした際に転記したルール)を分離して、分かりやすくしています。(コメントも入れています。)

初期ドメインについてのルールでは、一番最初の条件に
RewriteCond %{HTTP_HOST} ^[_\-0-9A-Za-z]+\.sakura\.ne\.jp$

を記述し、user.sakura.ne.jpへのアクセスの場合に限定しています。(テストパターンは各位の初期ドメイン直書きでかまいません。)

各ルールについては、コメントをご参照ください。(文字コードはUTF-8なので、コメントに日本語も問題なしです。)

一点、このルールだと、「https://user.sakura.ne.jp/wp/index.php」へのアクセスがスルーされてしまいます。15行と16行の間に「RewriteCond %{REQUEST_URI} ^/wp/secure/」を書けばよさそうですが、うまくいきません。(GETメソッドに書き換えて環境変数を見る方法を試すと、secureへのアクセスでも%{REQUEST_URI}はなぜかindex.phpになっていました。)ユーザーが先のURLを入力することはレアケースですので、これだけはWordPress内でリダイレクトすることにします。

WordPressの対策

WordPressの側にも対策します。本体のコードは加工せずに、wp-config.php、プラグイン、テーマで対策します。

wp-config.php

wp-config.phpの「/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */」の前に、以下のコードを追加します。

(前略) 
// 初期ドメインからのアクセスも有効にするための処理 
if( preg_match( '/\.sakura\.ne\.jp$/', $_SERVER['HTTP_HOST']) ) { 
	define('YJD_INITIAL_DOMAIN', 'http://user.sakura.ne.jp/wp/'); 
} else {
	// マルチサイトの設定 
	(中略:マルチサイト化の際追加したdefine) 
} 

// さくらレンタルサーバSSL接続用修正 
if( isset($_SERVER['HTTP_X_SAKURA_FORWARDED_FOR']) ) { 
	$_SERVER['HTTPS'] = 'on'; 
	$_ENV['HTTPS'] = 'on'; 
} 

/* 編集が必要なのはここまでです ! WordPress でブログをお楽しみください。 */ 
(後略) 

2行目で初期ドメインからのアクセスかどうかを判定し、初期ドメインの場合は4行目でYJD_INITIAL_DOMAINという定数をdefineしています。(後述のプラグイン内で、これを見て処理をします。)

またマルチサイトのdefineはスキップします。(マルチサイトの処理内でリダイレクトが発生します。SSL接続したいページではマルチサイト機能は使わないので単にこうしました。)

11行目から14行目は、「さくらレンタルサーバーの共有SSLを使う」にて記載した通りです。

プラグインでの対策

以下の様なプラグインを作成して有効化します。

wp-content\plugins以下に適当なディレクトリを作成し、その中に以下のコードを適当な名前(sakura-ssl.phpなど)で保存します。するとプラグインの管理画面に「共有SSL部分利用.」が現れますので有効化します。

<?php
/*
Plugin Name: 共有SSL部分利用.
Plugin URI: http://www.yujakudo.com/blogs/
Description: さくらレンタルサーバーの共有SSLを部分的に利用するためのプラグイン.
Author: yujakudo
Version: 0.1
Author URI: http://www.yujakudo.com/blogs/
*/

/**	@defgroup	初期ドメインからのアクセスの時の処理.
 *	YJD_INITIAL_DOMAINが定義されている場合のみ機能する.
 *	異なるドメインからアクセスされることがないのであれば、このグループの機能は無視すべき.
 */
/*@{/

/**	REQUEST_URIがindex.phpならリダイレクト.
 *	REQUEST_URIがindex.phpなら、独自ドメインにリダイレクトする.
 *	下方にて、初期ドメインの場合にadd_actionされる.
 */
function yjd_redirect_to_home() {
	if( preg_match( '/^(\/index\.php|\/)$/', $_SERVER['REQUEST_URI']) ) {
		wp_redirect( get_option('home'), 302 );
		wp_exit();
	}
}

/**	ホームURLを初期ドメインに書き換える.
 *	ホームURL(独自ドメイン)を、初期ドメインに書き換える.
 *	初期ドメインは、YJD_INITIAL_DOMAINにdefineされている.
 *	@param	uri		対象URI
 *	@return	書き換え後URI.
 */
function yjd_replace_home( $uri) {
	$base = preg_replace( '/^(http:\/\/)?(.*)$/', "$2" , YJD_INITIAL_DOMAIN);
	$base = preg_replace( '/^(.*)\/$/', "$1" , $base);
	$home = preg_replace( '/^(http:\/\/)?(.*)$/', "$2" , get_option('home'));
	$home = preg_replace( '/^(.*)\/$/', "$1" , $home);
	return preg_replace( "/" . preg_quote($home, "/") . "/", $base , $uri );
}

//	初期ドメインの場合は、ホームを書き換えるようにする.
//	初期ドメインの場合は、主要なURI取得関数にて、ホームでなく初期ドメインのURIが返るようにする.
//	初期ドメインは、YJD_INITIAL_DOMAINにdefineされている.
if( defined('YJD_INITIAL_DOMAIN') ) {
	add_action( 'init', 'yjd_redirect_to_home' );

	add_filter( 'template_directory_uri', 'yjd_replace_home');
	add_filter( 'stylesheet_directory_uri', 'yjd_replace_home');
	add_filter( 'home_url', 'yjd_replace_home');
	add_filter( 'site_url', 'yjd_replace_home');
	add_filter( 'plugins_url', 'yjd_replace_home');
}

/**	隠しページか?.
 *	隠しページかどうかを取得する.
 *	暫定的に、secure以下のページは隠しページとする.
 */
function yjd_is_hidden( $post = 0 ) {
	$hidden_pages = "secure";
	$hidden_list = explode(",", $hidden_pages);
	
	if( count( $hidden_list )==0 )	return false;

	$page = get_post( $post );
	if( in_array($page->post_name, $hidden_list) )	return true;
	$ancestors = get_post_ancestors( $page );
	foreach( $ancestors as $ancestor ) {
		$slug = get_post( $ancestor )->post_name;
		if( in_array($slug, $hidden_list) )	return true;
	}
	return false;
}

/**	スラッグからIDを取得する.
 *	@param	name	スラッグ.
 *	@return	ID.
 */
function yjd_get_id_by_name( $name ) {
	return get_page_by_path($name)->ID;
}

/**	隠しページのリストを取得する.
 *	隠しページのリストを、指定の形で取得する.
 *	暫定的に、secure以下のページは隠しページとする.
 *	@param	type	戻り値の形式.'array'の時は配列、
 *					その他の時はカンマ区切りの文字列を返す.
 *	@param	expr	表現形式.'name'のときはスラッグ、
 *					その他の時はIDで表す.
 *	@return	指定の形式の隠しページリスト.
 */
function yjd_get_hidden_pages( $type = 'string', $expr = 'id' ) {
	$hidden_pages = "secure";
	$hidden_list = explode(',', $hidden_pages);

	if( $expr==='id' ) {
		$hidden_list = array_map( 'yjd_get_id_by_name', $hidden_list );
		$hidden_pages = implode( ',', $hidden_list );
	}

	if( $type==='array' ) {
		return $hidden_list;
	}
	return $hidden_pages;
}

/*@}*/

(スタイルとかコメントとか突っ込みどころは多々あると思いますがご容赦ください。^^;)

各関数の機能はコメントをご参照ください。

45行から53行にて、主要なURLを返す関数で、独自ドメインでなく初期ドメインが返るようにしています。これをしないと、ブラウザがcss、js、画像などの参照ファイルを独自ドメインに対する非SSLにてリクエストするので、ブラウザに警告が表示されてしまいます。(テーマによっては、その他のURLを返す関数についても同様に対処する必要があるかもしれません。)

メニュー等のリンクも初期ドメインに対するアクセスになりますが、先の.htaccessにてhttp://独自ドメインにリダイレクトされます。

また、関数yjd_is_hidden、yjd_get_hidden_pagesでは、暫定的に’secure’をハードコーディングしています。将来的にちゃんとしたプラグインを作成する際には、管理画面との連携等考慮したいと思います。

テーマの修正

/secure/の下に設置したお問い合わせページでは、パンくずナビやサイドバーのメニューウィジェットなど表示させたくないので対処しました。

以下の様にifで囲むだけですが。

if( !(function_exists('yjd_is_hidden') && yjd_is_hidden()) ) {
	(メニュー等の表示処理)
}

またサイトマップを自動的に作成している場合、secure以下も表示されてしまいますので対処します。

$args = 'title_li=';
if( function_exists('yjd_get_hidden_pages') ) {
	$hidden = yjd_get_hidden_pages();
	if( strlen( $hidden ) ) {
		$args .= '&exclude_tree=' . $hidden;
	}
}
wp_list_pages($args);

(wp_list_pagesのバグで、exclude_treeオプションが仕様通り機能しないという記述もみました。その場合はすべての隠しページをリストにしてexcludeオプションに指定してやればよいと思います。)

課題

以上のような方法で、サイトのお問い合わせページのSSL化は可能になりますが、以下の様な課題があります。

プラグインをちゃんと作る

現在は、隠しページや、初期ドメインの共有SSLを使うページは/secure/以下に固定しています。将来的には設定画面を作成し、設定可能なプラグインにすることを考えています。その場合は.htaccessではなく、WordPress内でリダイレクトすることになります。

(しかし現在のさくらレンタルサーバーでの利用においては500エラーが多発するという問題がありますので、あまりWordPressに処理を渡したくありません。)

管理画面との統合

現在のところ、wp-adminにログインしていても、初期ドメインに置いたページには上部の管理メニューが表示されません。またそれらのページは編集中にプレビューできません。

プレビューできないのは管理上ストレスですので対処が必要だと思いますが、果たして可能でしょうか。(恐らくデータベース内のsiteurlやhomeと異なるドメインからアクセスしているため?かと考え中。)

まとめ

さくらレンタルサーバー上で独自ドメインにて運営している、WordPressで構築したサイトにおいて、一部のページを初期ドメインの共有SSL接続にすることに成功しました。

しかしいろいろと大変なので、皆様にはサーバー証明を取得してSSLにすることをおすすめします。

サイト上で個人情報を扱う際のセキュリティに関しては、また稿を改め考察いたします。

コメントを残す