公開したプラグインのセキュリティについて指摘をしてもらったので、直したときに勉強したことのメモ。

先日公開したプラグイン(Direct Register WPMU)について、セキュリティが心配だったので、チェックをお願いしたところ、見てくれました!

さらに、コメント欄でも、

なかなか興味深いプラグインなのですが、細かい点で問題があるようです。

* 「管理画面」の URL は get_admin_url() で取得してください。使い方は link-template.php 参照で。
* network_home_url() の出力も esc_attr() してから echo した方が無難だと思います。
* __() の中に日本語 (ローカライズ文字列) を入れるのはほとんど意味がありません (将来的言語リソースとして分離する予定なのでしょうが)。
* $_GET, $_POST が生で使われている部分が気になりますがこれは値を渡す先の add_signup_meta() の処理がどうなっているかに依存しますね。intval() などでバリデートした方が無難でしょう。

以上、コードを精査されることをおすすめします。

と教えてもらうことが出来ました。ありがとうございます。

以下、勉強したことなどのメモです。

$_GETでブログIDを持ってくるところ

プラグインでは、example.jp/wp-signup.php?from_blog_id=integerという形で、どのブログからやってきたのかを把握しています。
この、integerを$metaという配列に入れているのですが、このときそのまま入れてしまうのが危ないということです。

ブログIDは全て整数なので、(int)を使って整数に変換するか、intval()を使って整数なのかどうかを確かめてtrueなら処理を進めてfalseならどこかに飛ばして処理を終了する(DBには何もしない)、というのが安全性を高める、ということだと理解しました。


(int) : 整数への変換 | PHPマニュアル
intval() : intval() | PHPマニュアル

今回は、GETで受け取った値から整数部分を抜き出してくれるintval()を使いました。整数がなかったときに0を返してくれるので、そのときにはエラーメッセージを表示してやりなおしてもらうことに。


こちら(A HitchHackers guide through WordPress)を見ると、$metaは、シリアライズされているようですが、セキュリティを高めるためにやっているわけではないしなので、やっぱり必要そうです。
今回は整数であることが分かっているのですが、POSTでテキストを受け取るときにはどうしたらいいのでしょう。例えば、ツイッターのIDを聞いたり日本語のニックネームを付けられるようにするときなどですが。。
また勉強したいと思います。

管理画面URLの取得の仕方

<?php bloginfo('url') ?>/wp-admin/という風に取得していました。


get_admin_url( $blog_id, $path, $scheme )
という取得関数があるのですね。

ちなみに、$blog_idはそのまま。デフォルトは現在のサイトの。$pathはwp-admin/以下のパスを書くとそこに行けます。$scheme はhttpなのかhttpsなのかを指定できます。何も書かなければ調整してくれそうな感じです。

network_home_url()の出力について

esc_attr()してからechoとのことですが、ユーザに何かを入力してもらったものを出力するときは必ずという癖をつけないとですね。
この辺のことは、「動くものを作る」ことだけを考えているだけではいけないのだと肝に銘じたいです。


esc_attr() : 関数リファレンス/esc attr
カッコやクォーテーションマークなどをエンコードしてくれるWPの関数です。

ダウンロードとインストール

direct-register11からダウンロードしてください。
その後、管理画面からネットワークで有効化してください。

ウィジェットの操作については、前回記事からお願いします。

新しいソース

/*****************************************
-------  メタ情報ウィジェットカスタマイズ --------
******************************************/
class Colog_Meta extends WP_Widget {
	function __construct() {
		$widget_ops = array('classname' => 'widget_meta', 'description' => "ログイン/ログアウト、管理画面へのリンクを表示します。" );
		parent::__construct('meta', '管理メニュー', $widget_ops);
	}
	function widget( $args, $instance ) {
		extract($args);
		$title = apply_filters('widget_title', empty($instance['title']) ? '管理メニュー' : $instance['title'], $instance, $this->id_base);
		echo $before_widget;
		if ( $title )
			echo $before_title . $title . $after_title;
?>
<ul>
			<?php if( is_user_logged_in() ) { ?>
<li><a href="<?php echo get_admin_url() ?>">管理画面</a></li>
			<?php } ?>
<li><?php wp_loginout(); ?></li>
			<?php if ( !is_user_logged_in() ) {
	      		global $current_blog;
	      		$from_blog_id = $current_blog->blog_id;
	      		?>
<li><a href="<?php echo esc_attr(network_home_url('wp-signup.php?from_blog_id=')).$from_blog_id; ?>">「<?php bloginfo('name') ?>」に読者申請する</a></li>
				<?php
					if ( get_option('users_can_register') ) { ?>
<li><a href="<?php echo esc_attr(network_home_url('wp-signup.php')); ?>">新しいブログを作る</a></li>
				<?php
					}
			 } ?>
<!--
<li><a href="<?php bloginfo('rss2_url'); ?>" title="<?php echo esc_attr(__('Syndicate this site using RSS 2.0')); ?>"><?php _e('Entries <abbr title="Really Simple Syndication">RSS</abbr>'); ?></a></li>
<li><a href="<?php bloginfo('comments_rss2_url'); ?>" title="<?php echo esc_attr(__('The latest comments to all posts in RSS')); ?>"><?php _e('Comments <abbr title="Really Simple Syndication">RSS</abbr>'); ?></a></li>
<li><a href=" <a href="http://wordpress.org/"" target="_blank" class="advmk">http://wordpress.org/"</a> title="<?php echo esc_attr(__('Powered by WordPress, state-of-the-art semantic personal publishing platform.')); ?>">WordPress.org</a></li>
-->
			<?php wp_meta(); ?>
			</ul>
<?php
		echo $after_widget;
	}
	function update( $new_instance, $old_instance ) {
		$instance = $old_instance;
		$instance['title'] = strip_tags($new_instance['title']);
		return $instance;
	}
	function form( $instance ) {
		$instance = wp_parse_args( (array) $instance, array( 'title' => '' ) );
		$title = strip_tags($instance['title']);
?>
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" />
<?php
	}
}
add_action('widgets_init', create_function('', 'return register_widget("Colog_Meta");'));
/*****************************************
------- 読者登録の場合には、
		  ブログ/読者選択肢を表示しない --------
******************************************/
function dr_active_signup_changer() {
	$from_blog_id = isset( $_GET['from_blog_id'] ) ? $_GET['from_blog_id'] : '1';
	$from_blog_id = intval($from_blog_id);
	if ( $from_blog_id == 0 ) {
		die('変なことしてませんか?');
	}
	echo '<input type="hidden" name="from_blog_id" value="'.$from_blog_id.'" />';
	$from_blog_name = get_blog_option($from_blog_id, 'blogname');
	global $active_signup;
	if ( $from_blog_id == '1' ) {
		$active_signup = 'blog';
	} else {
		echo '
<h1>'.$from_blog_name.'に読者申請をする</h1>
';
		$active_signup = 'user';
	}
}
add_action('signup_hidden_fields','dr_active_signup_changer');
/*****************************************
------- 子ブログに直接登録する --------
******************************************/
function dr_add_blog_id_to_meta() {
	$secure_id = intval($_POST['from_blog_id']);
	if ( $secure_id == 0 ) {
		die('何かが変です。。');
	} else {
		$meta = array(
		"add_to_blog" => $secure_id,
		"new_role" => 'Subscriber'
		);
		return $meta;
	}
}
add_filter("add_signup_meta","dr_add_blog_id_to_meta");

↓ プラグインを作る方々への本、書きました。 ↓

“公開したプラグインのセキュリティについて指摘をしてもらったので、直したときに勉強したことのメモ。” への 5 件のフィードバック

  1. 初めまして

    direct-registerをテストさせてもらったのですが思うように動かないのでご相談させてください。
    WP3.3.1でマルチサイトをテスト中です。
    direct-registerはネットワークで有効化しました。
    ネットワーク管理者でユーザー一覧を見ると確かにユーザー登録されて子サイトのURLも表示されていますが、子サイトに移ってユーザー一覧を確認すると購読者数だけが増えて一覧には表示されていません。
    新規ユーザーでログインすると購読者としての権限が与えられていないようで管理パネルを開くこともできません。

    何かお気づきの点があったらご指摘お願いします。

  2. 気がつくのが遅れてしまってすみません。その後、いかがでしょうか?

  3. Yujiさんと同じく、権限がないためプロフィール編集などできないですね・・。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です