[WordPress] FTP ではなく SSH による通信を行う…のも微妙です
2018/11/28 23:41
  • これまで WordPress のプラグインやテーマのインストール、更新、削除などには FTP が使われてきましたが、
    セキュアな通信とは言えないので可及的速やかにやめましょう。やるのであれば SSH を使った通信をするべきです。

    といいつつ、実は SSH すら必要ない、ということがわかりましたのでまとめました。


    適切な所有者・グループを設定する

    プラグインやテーマのインストールなどをしようとして、 FTP や SSH の接続情報を入力するモーダルが
    表示される場合は適切な所有者・グループを設定できていませんので、素直に接続情報など入力せず所有者の確認をしましょう。

    所有者/グループの設定
    WordPress のフォルダ・ファイル群の所有者とグループを Apache の実行ユーザー、グループに設定します。

    $ sudo chown -R apache:apache /path/to/wordpress
    

    apache:apachewww-data:www-data で有ることが多いですが、設定前に httpd.conf ファイルを確認しましょう。

    この状態で、もう一度プラグインやテーマのインストールをしてみましょう。
    さくっとインストールできると思います。


    FTP, SFTP, SSH, direct など複数種類ありますが、今回の所有者・グループの変更によって選択される通信方法は direct です。

    direct の設定はあまりよくないイメージを持たれている方もいるかも知れませんが、別に wp-config.php で明示的に指定しているわけでもないし、WordPress が「だいじょうぶ」と判断したので結果として direct になっているだけで、特に問題ない認識です。

    って切り捨てるのもあれなので、コードを読みました。


    プラグイン・テーマのインストール/アンインストール時、本体のアップデート時などでファイルの取得方法を取得するために get_filesystem_method() が必ず呼ばれます。 該当箇所だけ抜粋します。

    https://core.trac.wordpress.org/browser/trunk/src/wp-admin/includes/file.php#L1563

    if ( ! $method ) {
    
    		$temp_file_name = $context . 'temp-write-test-' . time();
    		$temp_handle = @fopen($temp_file_name, 'w');
    		if ( $temp_handle ) {
    
    			// Attempt to determine the file owner of the WordPress files, and that of newly created files
    			$wp_file_owner = $temp_file_owner = false;
    			if ( function_exists('fileowner') ) {
    				$wp_file_owner = @fileowner( __FILE__ );
    				$temp_file_owner = @fileowner( $temp_file_name );
    			}
    
    			if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) {
    				// WordPress is creating files as the same owner as the WordPress files,
    				// this means it's safe to modify & create new files via PHP.
    				$method = 'direct';
    				$GLOBALS['_wp_filesystem_direct_method'] = 'file_owner';
    			} elseif ( $allow_relaxed_file_ownership ) {
    				// The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files
    				// safely in this directory. This mode doesn't create new files, only alter existing ones.
    				$method = 'direct';
    				$GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership';
    			}
    
    			@fclose($temp_handle);
    			@unlink($temp_file_name);
    		}
     	}
    

    コードを見る限り、テンポラリのファイルを作成し、そのテンポラリファイルの所有者と file.php の所有者を比較して$methoddirect にしています。
    テンポラリのファイルは PHP が作成していますが、PHP は Apache のモジュールとして動いているため実行ユーザーは Apache の実行ユーザーとなり、
    結果的にテンポラリファイルの所有者は Apache の実行ユーザーとなります。

    つまり WordPress のファイル群の所有者を apache にしておけば、そもそも通信方法を選択するモーダル自体でてこない、ということになります。

    参考:WordPressのアップグレードやプラグインの更新をFTP画面なしで行う方法


    どうやってそこにたどり着いたの?

    いきなり file.php の中身見せられてもなぁ…そもそもどうやってそこにたどり着くの?っていうのも気になると思いますので、テーマのインストールを例に、ちょっとだけ深追いしてみます。

    テーマのインストール


    まず呼ばれるのは wp-admin/theme-install.php

    テーマのインストールページに行き、試しに適当なテーマのインストールボタンをクリックしてモーダルを表示してみたりします。
    (このときデベロッパーツールなどで通信や要素を確認する)

    モーダルの要素


    モーダル要素の id が request-filesystem-credentials-dialog になっていることがわかったので php ファイルから該当箇所を検索します。
    https://core.trac.wordpress.org/browser/trunk/src/wp-admin/includes/file.php#L1915

    function wp_print_request_filesystem_credentials_modal() {
    	$filesystem_method = get_filesystem_method();
    	ob_start();
    	$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
    	ob_end_clean();
    	$request_filesystem_credentials = ( $filesystem_method != 'direct' && ! $filesystem_credentials_are_stored );
    	if ( ! $request_filesystem_credentials ) {
    		return;
    	}
    	?>
    	<div id="request-filesystem-credentials-dialog" class="notification-dialog-wrap request-filesystem-credentials-dialog">
    		<div class="notification-dialog-background"></div>
    		<div class="notification-dialog" role="dialog" aria-labelledby="request-filesystem-credentials-title" tabindex="0">
    			<div class="request-filesystem-credentials-dialog-content">
    				<?php request_filesystem_credentials( site_url() ); ?>
    			</div>
    		</div>
    	</div>
    	<?php
    }
    

    どうやらここっぽいので、 wp_print_request_filesystem_credentials_modal() を呼んでいる箇所を洗います。すると、 theme-install.php で呼ばれていることがわかりました。
    https://core.trac.wordpress.org/browser/trunk/src/wp-admin/theme-install.php#L366
    ^ ここですね。


    だいたいこんな感じで特定することができました。

    余談( JS 編)

    該当箇所は特定できましたが、実際にどうやって表示しているかもよくわからなかったのでさらに深追いしてみました。

    モーダルの表示に通信は発生しない
    テーマの「インストール」ボタンをクリックしたときにモーダルが表示されて、その際に通信が発生しないことの調べがついていたので、 JavaScript の onclick あたりで制御してるんだろうなっていう予測を立てます。


    対象の要素は theme-install class が付加されています。インストールボタンのクラス


    wp_enqueue_script( 'theme' ); でテーマの JS を読み込んでいることがわかるので対象の JS ファイルを探します。
    https://core.trac.wordpress.org/browser/trunk/src/js/_enqueues/wp/theme.js#L378
    しっかり onclick が設定されています。


    https://core.trac.wordpress.org/browser/trunk/src/js/_enqueues/wp/theme.js#L603

    	installTheme: function( event ) {
    		var _this = this;
    
    		event.preventDefault();
    
    		wp.updates.maybeRequestFilesystemCredentials( event );
    
    		$( document ).on( 'wp-theme-install-success', function( event, response ) {
    			if ( _this.model.get( 'id' ) === response.slug ) {
    				_this.model.set( { 'installed': true } );
    			}
    		} );
    
    		wp.updates.installTheme( {
    			slug: $( event.target ).data( 'slug' )
    		} );
    	},
    

    この installTheme() が呼ばれるので追っていくと、以下にたどり着きます。

    https://core.trac.wordpress.org/browser/trunk/src/js/_enqueues/wp/updates.js#L1501

    	wp.updates.requestForCredentialsModalOpen = function() {
    		var $modal = $( '#request-filesystem-credentials-dialog' );
    
    		$( 'body' ).addClass( 'modal-open' );
    		$modal.show();
    		$modal.find( 'input:enabled:first' ).focus();
    		$modal.on( 'keydown', wp.updates.keydown );
    	};
    

    どうやらここでモーダルを表示しているようです。
    ただ hidden されていた要素を表示しているだけのようです。
    (つまり、 display: none; で予め定義されている)
    https://core.trac.wordpress.org/browser/trunk/src/wp-admin/css/forms.css#L959
    .request-filesystem-credentials-dialog {
    	display: none;
    	/* The customizer uses visibility: hidden on the body for full-overlays. */
    	visibility: visible;
    }
    

    というわけで、いろいろ話は脱線しましたが理解が深まりました。
    WordPress よくできてますよね。


    人気ブログランキングへ ブログランキング・にほんブログ村へ
    ↑応援よろしくお願いします!m(_ _)m

  • 2018/11/28 23:41
  • その他
  • WordPressワードプレスFTPSSHSFTPdirectApachePHP所有者ownerchown
  • 古い記事へ
    CentOS6 系でのプロセスの自動起動の設定

profile picture

自己紹介的な何か

@wkmettyでついったーやってます。時々。 6年間勤めたゲーム会社を2018年2月に退職しフリーランスのプログラマに。 WordPress Core, WP-CLI コントリビューター。 お仕事募集中です。