wordpress WP インストール時の注意点とセキュリティ

2013年4月19日

WordPress インストール時の個人的に気になった注意点やセキュリティなどに関して。

ユーザー名にadminを使用しない

ブルートフォース攻撃を防ぐために、必ず admin 以外のユーザー名を使う。
そのため、インストールの際に管理者の名前をデフォルトの admin を使わずに、別の名前で作成する。(admin でインストールしてあっても後から変更可能だが最初から admin  以外でインストールしておいたほうがよい。)

table_prefix の変更

以下は「WordPress の安全性を高める」から。

多くの既知の WordPress を狙った SQL インジェクション攻撃は、table_prefix がデフォルトの “wp_” であることを仮定しています。これを変更することは、隠蔽によるセキュリティですが、SQL インジェクション攻撃の一部は防ぐことができます。

制作時の以下の情報を記録しておく(バックアップ・リストア時に必要)

  • PHPのバージョン
  • MySQLのバージョン
  • WordPressをインストールしたディレクトリのフルパス
  • MySQL用のデータベース名、ユーザー名、接続用パスワード、データベースの文字コード、テーブルの接頭語
  • wp-config.php, .htaccess のコピーをとっておく

サイトを非公開にする場合

確認作業中など、サイトにアクセスされたくない場合は、以下を functions.php に記述する。
公開 URL にアクセスすると、WordPress のログイン画面にリダイレクトされる。

function validate_login() {
  if(!is_user_logged_in()) {
    auth_redirect();
  }
}
add_action('template_redirect', 'validate_login');

この時、ログイン画面にリダイレクトさせるようにしているが、「ログイン画面はここです」と言っているようなものなので、以下のサイトを参考に、ログインページを別のページにするとよいかも知れない。

ログインページを変える:WordPress私的マニュアル

以下は上記サイトからのコード(ログインページ名「anywhere-login.php」や「keyword」は適宜変更したほうが良いとのこと)

このログインページのファイル名は任意で構わないが、適度に長いファイル名が望ましい(ここでは’anywhere-login.php’)。配置場所は、wp-login.phpと同じディレクトリとする。
内容については、’ANYWHERE_LOGIN’を定義し、wp-login.phpを読み込んでいる。’keyword’については、適宜ユニークな文字に書き換えること。
(ログインページを変える:WordPress私的マニュアルより)

代わりのログインページ(anywhere-login.php)を以下の内容で作成し、wp-login.php と同じディレクトリに配置。

//anywhere-login.php
<?php
  define( 'ANYWHERE_LOGIN', sha1( 'keyword' ) );
  require_once './wp-login.php';
?>

以下を functions.php に記述。

define( 'ANYWHERE_LOGIN_PAGE', 'anywhere-login.php' );
  add_action( 'login_init', 'anywhere_login_init' );
  add_filter( 'site_url', 'anywhere_login_site_url', 10, 4 );
  add_filter( 'wp_redirect', 'anywhere_login_wp_redirect', 10, 2 );
  
  if ( ! function_exists( 'anywhere_login_init' ) ) {
    function anywhere_login_init() {
      if ( !defined( 'ANYWHERE_LOGIN' ) || sha1( 'keyword' ) != ANYWHERE_LOGIN ) {
        status_header( 403 );
        exit;
      }
    }
  }
  if ( ! function_exists( 'anywhere_login_site_url' ) ) {
    function anywhere_login_site_url( $url, $path, $orig_scheme, $blog_id ) {
      if ( $path == 'wp-login.php' && ( is_user_logged_in() || strpos( $_SERVER['REQUEST_URI'], ANYWHERE_LOGIN_PAGE ) !== false ) )
        $url = str_replace( 'wp-login.php', ANYWHERE_LOGIN_PAGE, $url );
        return $url;
     }
  }
  if ( ! function_exists( 'anywhere_login_wp_redirect' ) ) {
    function anywhere_login_wp_redirect( $location, $status ) {
      if ( strpos( $_SERVER['REQUEST_URI'], ANYWHERE_LOGIN_PAGE ) !== false )
        $location = str_replace( 'wp-login.php', ANYWHERE_LOGIN_PAGE, $location );
        return $location;
      }
  }

関連ページ:「WordPress でサイトを非公開にする場合

デバッグモード

また、開発中には、エラー(notice)を表示するように、デバッグモードにすることができる。
wp-config.php の以下を 「true」に変更する。
但し、公開後は必ず「false」に戻すようにする。

define('WP_DEBUG', false);

SSLを使用する

SSLが使用可能なサイトの場合には、SSLで管理者画面にアクセスするようする。
wp-config.phpに以下を追記するだけ。

define('FORCE_SSL_ADMIN', true);

但し、共用の SSL の場合は難しい。
(試してみたが、管理画面のURL などが異なってしまう。こちらを参照

.htaccess の設定

ディレクトリの参照を防止(ディレクトリの一覧を表示しない)

Options -Indexes

スクリプトインジェクション対策として攻撃者が script タグをフォームから入力しても処理しないようにする。

Options +FollowSymLinks
RewriteEngine On
# <script> タグ
RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR]
# PHP グローバルに関連するもの
RewriteCond %{QUERY_STRING} GLOBALS(=|[|%[0-9A-Z]{0,2}) [OR]
# _REQUEST を変更しようとするもの
RewriteCond %{QUERY_STRING} _REQUEST(=|[|%[0-9A-Z]{0,2})
#マッチしたURLへのアクセスを禁止("403 Forbidden"のレスポンスを返す)
RewriteRule ^(.*)$ index.php [F,L]

wp-config.php, wp-mail.php, install.php へのアクセスを拒否。
install.php を削除してあれば、その部分は不要。追記:アップグレードするとinstall.phpは再作成されるので記述しておいたほうが良い。)

<FilesMatch "^(wp-config\.php|wp-mail\.php|install\.php|\.ht)">
order allow,deny
deny from all
</FilesMatch>

参考:WordPressをセキュアに保つための10の方
参考:WordPressサイト用の.htaccess例 dogmap.jp
参考:RewriteCond – RewriteRuleの条件を設定

wp-config.php を安全な位置へ

wp-config.php ファイルを、WordPress をインストールした階層の一つ上に移動することができるので、ウェブスペースのルートにインストールしている場合は、wp-config.php をウェブルートの外側に格納できる。また、あなた (とウェブサーバー) だけがこのファイルを閲覧可能 (通常はパーミッション400または440) にしてください。

と「CodeX」にあるので可能であれば、wp-config.php をウェブスペースのルート(public_html や www)より1階層上に移し、パーミッションを「400」にする(環境によってはできない場合もある)。wp-config.php を後から編集する場合は一時的にパーミッションを「600」に変更し、編集後は元(400)に戻す。

ウェブスペースのルートにインストールしない場合は、他の方法が必要なので、検索すると下記サイトを見つけたので、その方法を試してみる。(wp-load.php を編集するので、アップグレードの際に再度 wp-load.php を編集する必要が出る可能性があるので、注意が必要。あまり実用的ではない。

WordPressのwp-config.phpファイルの場所をドキュメントルート以外のディレクトリへ変更する

WordPress を /public_html/my_siteNo2/wp にインストールしてあり、wp-config.php をルートディレクトリ直下( public_html と同じ階層)に配置した場合。

まず、wp-load.php の以下の1階層上の wp-config.php を探す部分を変更。

/** Define ABSPATH as this file's directory */
// 省略
} elseif ( file_exists( dirname(ABSPATH) . '/wp-config.php' ) && ! file_exists( dirname(ABSPATH) . '/wp-settings.php' ) ) {
  /** The config file resides one level above ABSPATH but is not part of another install */
  /** wp-config.phpがABSPATHの1階層上にあり、かつそれが他のインストールの一部でない場合 */
  require_once( dirname(ABSPATH) . '/wp-config.php' );
} else {
// 省略

変更後

/** Define ABSPATH as this file's directory */
// 省略
} elseif ( file_exists((dirname(dirname(dirname(ABSPATH) ))) . '/wp-config.php' ) && ! file_exists( dirname(dirname(dirname(ABSPATH))) . '/wp-settings.php' ) ) {
  /** 1階層上ではなくさらにその2階層上から探す。 */
  /** The config file resides one level above ABSPATH but is not part of another install */
  require_once((dirname(dirname(dirname(ABSPATH) ))). '/wp-config.php' );
} else { 
// 省略

そして、配置した wp-config.php の以下の部分を編集。

/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
  define('ABSPATH', dirname(__FILE__) . '/public_html/my_siteNo2/wp/');
  オリジナルは define('ABSPATH', dirname(__FILE__) . '/');

wp-config.phpのパーミッションを 400 に設定。
(但し、内容を変更する時には 600 などに変更しないとならない。)

注意点(追記):2013年6月22日
バージョンを「3.5.1」から「3.5.2」にアップグレードする際に「wp-config.php が見つからないので作成する必要がある」というようなメッセージが出て、アップグレードが途中で止まってしまい、ページも表示されなくなりました。調べてみると「wp-load.php」が上書きされていたので、以下のような対処行いました。

  • そのまま続けると、データベースの作成(データベース名やパスワードの入力)の画面になったので、中断。
  • バックアップしておいた「wp-load.php」の上記で変更した部分をコピーして、上書きされた「wp-load.php」の該当部分を変更。
  • ダッシュボードに戻ると「データベースをアップデートする必要がある。」と出たので「アップデートする」をクリックすると、すぐに完了し、正常に表示されるようになった
  • アップグレードのたびにこれを行うのは面倒だというのが感じたこと。

また、単にデータベース名やパスワードを保護するためなら、以下のようなファイルを作成し外部からアクセスできないディレクトリ「etc」以下などに配置し、wp-config.php の冒頭などで「require」で読み込み、wp-config.php の該当部分をコメントアウトするか削除することもできる(バックアップを必ず取る)。

//config-vars.php
<?php
define('DB_NAME', 'データベース名');  //wp-config.php に記載されている値
define('DB_USER', 'データベースユーザー名');
define('DB_PASSWORD', 'パスワード');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8');
define('TABLE_PREFIX', 'wp_');

define('AUTH_KEY',         'Dyk1u6Owb^wmN=-;Ay5z:)o>F*A`|,]dRJ^dNeu-jbft87z,_1%b2i(U%3(tfzQQ)');
define('SECURE_AUTH_KEY',  ')D<.O95~JREVR_5TEwx? UEZ1Hvzq2dW+dO3dP,W/HvpUTp74xW0b &#91;C:,Pb-14%2');
define('LOGGED_IN_KEY',    'BK`|&#93;R2T(W|xjbEe&#91;!m%+-lEl8l=s=,Cg!}J}*|TTZ;pP~DBi{tp,+w9b/#7Pr?FI');
define('NONCE_KEY',        '?FWn;*Khl4|+nO} GeqVj1#Pi!JO9+_z|r{vLW{+6~mm{&#91;Q8hp4rWY}L=;ZC5n+C$');
define('AUTH_SALT',        '/Z-xNV.Q!}Z.H+gbZf!Q.Ltd&#93;PfHgpHK.!hvs>,0d0<&#93;2zb&#93;i;#?*(dJsx+wo{_6p');
define('SECURE_AUTH_SALT', ':H||M>&_D*mteoS{kALWQh+OS9>UPFW|n1$,d#zB-mXMO|TA|~Dmab<`1i^0P3`4-');
define('LOGGED_IN_SALT',   'cm=8x|3`{_a)E:1ehaRnN)6/+~!5m-j-<lk{qsf|q&5wY|0@,x@r.RrGg?V36r$=+');
define('NONCE_SALT',       '&#93;&#91;s!#2&#91;f+xXK!-ythRU.2TM)`T53=G~;U>#p,H5cdn9zs[BXnUOyGH=yd%;Xr>#ne');
?>

以下は wp-config.php の変更箇所の抜粋

<?php
require '/home/xxx/etc/xxx/xxx/config-vars.php';  //上記ファイルを読み込む
/**
 * The base configurations of the WordPress.
 *
 * このファイルは、MySQL、テーブル接頭辞、秘密鍵、言語、ABSPATH の設定を含みます。
 * より詳しい情報は {@link http://wpdocs.sourceforge.jp/wp-config.php...
 *
 * @package WordPress
 */

// ** MySQL 設定 - こちらの情報はホスティング先から入手してください。 ** //
/** WordPress のためのデータベース名 */
//define('DB_NAME', '');  //値を消してコメントアウト

/** MySQL データベースのユーザー名 */
//define('DB_USER', '');

/** MySQL データベースのパスワード */
//define('DB_PASSWORD', '');

/** MySQL のホスト名 */
//define('DB_HOST', ');

/** データベースのテーブルを作成する際のデータベースのキャラクターセット */
//define('DB_CHARSET', '');
...
//$table_prefix  = 'wp_';
$table_prefix  =TABLE_PREFIX;
...
//define('AUTH_KEY',         '');
//define('SECURE_AUTH_KEY',  '');
//define('LOGGED_IN_KEY',    '');
//define('NONCE_KEY',        '');
//define('AUTH_SALT',        '');
//define('SECURE_AUTH_SALT', '');
//define('LOGGED_IN_SALT',   '');
//define('NONCE_SALT',       '');
&#91;/code&#93;

<dl>
<dt><a href="http://www.php.net/manual/ja/function.file-exists.php" title="PHP file_exists" target="_blank">file_exists</a></dt>
<dd>ファイルまたはディレクトリが存在するかどうか調べる</dd>
<dt><a href="http://php.net/manual/ja/function.dirname.php" title="PHP dirname()" target="_blank">dirname()</a></dt>
<dd>ファイルあるいはディレクトリへのパスを含む文字列を受け取って、 親ディレクトリのパスを返す。</dd>
<dt><a href="http://php.net/manual/ja/language.constants.predefined.php" title="PHP __FILE__" target="_blank">__FILE__</a></dt>
<dd>ファイルのフルパスとファイル名</dd>
<dt><a href="http://php.net/manual/ja/language.constants.predefined.php" title="PHP __DIR__" target="_blank">dirname(__FILE__)</a></dt>
<dd>その関数が書かれたPHPファイルの絶対パスを返す。そのファイルの存在するディレクトリ。「__DIR__(PHP 5.3.0 で追加)」と同じ意味。</dd>

</dl>

<h3>ログインにベーシック認証を使用</h3>
以下を .htaccess に記述し、.htpasswd (パスワード)ファイルを作成する。
# wp-login.php にベーシック認証を設定 <files wp-login.php> # 認証をかける領域名の指定。 ダイアログに表示される(任意の)文字列を指定。 AuthName "Login Password Required" AuthType Basic # パスワードファイルの指定(下記の注意参照) AuthUserFile /home/xxxxx/.htpasswd require valid-user </files> # 「.htaccess」や「.htpasswd」へのアクセスを拒否 <Files ~ "^.(htpasswd|htaccess)$"> deny from all </Files>

(注意)AuthUserFile(パスワードファイルの指定)はサーバのルートディレクトリからのフルパスで記述。パーミッションは604のようにしておく。
 
次に.htpasswd パスワードファイルを作成する。

アカウント名とパスワードを「:(コロン)」で区切って記述するだけ。
(例)userx:KBlkJenRWYwjU
複数のアカウントを指定する場合には、改行をして次の行に記述。
パスワードについては、暗号化された状態で保存される必要がある。

ベーシック認証と WordPress のユーザー名とパスワードは異なるものにする。

PHPでBasic認証のパスワードを作る

.htpasswd を使わずに PHP でベーシック認証をするには「PHPでベーシック(Basic)認証をかける」を参照。

追記:2013年6月27日
wp-login.php だけでなく、wp-admin へのリクエストの制限、パスワードの設定もしたほうがよい。「WordPress の wp-login.php をブルートフォースアタックから守る」を参照。

install.php の削除

/wp-admin/install.php はインストール後は不要なので削除。但し、アップグレードすると自動的に作成されてしまう。

その他

あまり意味はないかもしれないが、インストールディレクトリにある「readme-ja.html」と「readme.html」を削除。