check_admin_refererでエラーが出ていた真の原因

スパイラル土下座をしなければならない。

先日の日記で、
check_admin_refererって何動かないくせにでしゃばってんの?
とか書いた私でありましたが、真の原因がわかりました。

原因

isapi_rewrite
RewriteRuleにQSAオプションが無かったので、QueryStringが無視されてた。






すいませんでしたーッ

参考

mod_rewrite で query string の扱い
http://gohan.wotaku.jp/memo/583

check_admin_referer でエラー。もうもうもうもう一度お試しくださいくださいください

以下の内容は嘘偽りであり本当の原因はコチラであった。
ファーイーストリサーチ社では調査を打ち切る事にした。


BuddyPress1.5.1でのお話し。

現象

BuddyPressを使っていて、以下の操作をするとエラーが起きました。

  • トピックの削除
  • 注目トピックにする
  • 投稿できないようにする
  • アバターを消す(ユーザ・グループ共)

エラー画面は以下。

もう一度お試しさせて頂いても、結果は同じ。

エラーの理由がわからない
どこで出たかもわからない
ニャンニャンニャニャーーンwwwwwニャンニャンニャニャーンwwwwwww

調べてみた

わからなかった…。検索力たったの5か。ゴミめ。
このメッセージってマイナーなの?
キャッシュを消せばいけるでとかあったけど
キャッシュ消してもダメだった。

追ってみた

とりあえずどこでエラーが出てるのか知りたかったので、
BuddyPressの奥底に潜っていきました。バディだけに。←


そしたらエラーが出ている箇所を発見。
wp-content\plugins\buddypress\bp-groups\bp-groups-screens.php

トピックを注目させる(159行目〜)

// Sticky a topic
else if ( bp_is_action_variable( 'stick', 2 ) && ( isset( $bp->is_item_admin ) || isset( $bp->is_item_mod ) ) ) {
	// Check the nonce
	check_admin_referer( 'bp_forums_stick_topic' );

	if ( !bp_forums_sticky_topic( array( 'topic_id' => $topic_id ) ) )
		bp_core_add_message( __( 'There was an error when making that topic a sticky', 'buddypress' ), 'error' );
	else
		bp_core_add_message( __( 'The topic was made sticky successfully', 'buddypress' ) );

	do_action( 'groups_stick_forum_topic', $topic_id );
	bp_core_redirect( wp_get_referer() );
}

check_admin_referer( 'bp_forums_stick_topic' );
こいつや!!! こいつがエラーを吐いてたんや!!!!!11


注目を解除させる(173行目〜)

// Un-Sticky a topic
else if ( bp_is_action_variable( 'unstick', 2 ) && ( isset( $bp->is_item_admin ) || isset( $bp->is_item_mod ) ) ) {
	// Check the nonce
	check_admin_referer( 'bp_forums_unstick_topic' );

	if ( !bp_forums_sticky_topic( array( 'topic_id' => $topic_id, 'mode' => 'unstick' ) ) )
		bp_core_add_message( __( 'There was an error when unsticking that topic', 'buddypress'), 'error' );
	else
		bp_core_add_message( __( 'The topic was unstuck successfully', 'buddypress') );

	do_action( 'groups_unstick_forum_topic', $topic_id );
	bp_core_redirect( wp_get_referer() );
}

check_admin_referer( 'bp_forums_unstick_topic' );
こいつや!!! こいつがエラーを吐いてたんや!!!!!11

他のエラー発生個所も、全部check_admin_referer関数が原因だった模様。

こいつなんなの? 動かないくせに居座るとか…。

check_admin_referer(action)って何だよ

正直WordPress始めたてのボクチンにはわからんチンであった。
でもどうやらセキュリティ上大切らしい。
正常なURLからそのアクションが実行されたかをチェックする為だそう…。

使い方は、まずアクションを許可するページに

<?php wp_nonce_field(action); ?>

と記述しておく。
そうするとハッシュが格納されたhiddenフィールドが生成される。
そいつをPOSTする事で、check_admin_referer(action)によって、
「ちゃんとwp_nonce_fieldを置いたページから実行しただろうな?」
というチェックを行う。

これを使えば管理画面からしか呼び出せないような安全なプラグインが開発できるね!

みたいなイメージ? この理解あってる?

なぜエラーが出ていたか?

対応したwp_nonce_fieldが無い。 どこにもない。なのにチェックしている。
と理解した。何故なら実際にないし、コメントアウトしたら動いたからだ。

ていうか動いたとしてもエラーが出た箇所はPOSTじゃなくアンカータグのリンクになってる。
hiddenを置いたとしてもハッシュはPOSTされないのでは無いか?
ていうかチェックいるの? グループ消すとこではこの処理入ってないよ?

わからんチン

わからんけどコメントアウトしたら動いた。
しかしログインした状態でURL直叩きしてもトピックが消せるので怖い。

  • ファーイーストリサーチ社では今後もこの件については継続調査する。たぶん

そういえばWebクリエイターボックス+では動いてるっぽくない?
と思って確認しにいこうと思ったら、







ワロタwwwwwwwwww






ワロタ………昨日登録したばかりだというのに…


コメントアウトしたところ

wp-content\plugins\buddypress\bp-groups\bp-group-screens.php

  • check_admin_referer( 'bp_forums_stick_topic' );
  • check_admin_referer( 'bp_forums_unstick_topic' );
  • check_admin_referer( 'bp_forums_close_topic' );
  • check_admin_referer( 'bp_forums_open_topic' );
  • check_admin_referer( 'bp_forums_delete_topic' );
  • check_admin_referer( 'bp_forums_delete_post' );
  • check_admin_referer( 'bp_group_avatar_delete' );

wp-content\plugins\buddypress\bp-xprofile\bp-xprofile-actions.php


以上の内容は嘘偽りであり本当の原因はコチラであった。
ファーイーストリサーチ社では調査を打ち切る事にした。

BuddyPressでグループ名に日本語を使うと起こる面倒な事とその対策

BuddyPressのバージョンは1.5.1です。

仕様

BuddyPressはグループ名やトピック名をそのままURLに使います。
名前がマルチバイトの場合はURLエンコードされたものが利用されます。
記事を作るときのスラッグと同じです。

例:
グループ名を「日本語」として作った場合の、「日本語グループ」のURLは
http://example/group/%93%fa%96%7b%8c%ea


URLに利用する為の文字列を保存しているのは

グループの場合 wp_bp_groups -> slug というレコードです。
トピックの場合 wp_bb_topics -> topic_slub というレコードです。

グループにアクセスするとBadRequest

上記レコード、長さが100しか無い為、
日本語でグループ名をつけると11文字くらいでURLエンコードした文字が削られるのか、
そのグループにアクセスできなくなるという現象が起こります。

また、僕がやった場合はIIS6.0でやっていたからか404エラーが返ってきて
1文字のグループ名であろうがアクセスできなくなりました。
これはまぁ別の理由なんだろうけどドイヒーな話です。

どうしたらいいのかなぁ

ネットで色々調べてみた所

・一旦英語でグループを作ってから名前を日本語に戻す
・slugの長さを100→255にする事でもう少し文字が入るようになる

みたいな運用でのカバーをすれば良いって書いてありましたが、
トピックを作るのは管理人じゃないし、
そんなん使う人が面倒くさいしやってられないなぁと思ったので、
名前=スラッグ という仕様を変えたろうと思いました。

グループの対応

触る場所:wp-content\plugins\buddypress\bp-groups\bp-groups-classes.php
93行目くらい

// $this->slug からuniqid() に変更
$sql = $wpdb->prepare(
	"INSERT INTO {$bp->groups->table_name} (
		creator_id,
		name,
		slug,
		description,
		status,
		enable_forum,
		date_created
	) VALUES (
		%d, %s, %s, %s, %s, %d, %s
	)",
		$this->creator_id,
		$this->name,
		uniqid(), /* ←ココ!! */
		$this->description,
		$this->status,
		$this->enable_forum,
		$this->date_created
);

はい。INSERT文を生成するところでslugをuniqid()にしてしまいます。

トピックの対応

触る場所:wp-content\plugins\buddypress\bp-forums\bbpress\bb-includes
223行目付近

$topic_slug = uniqid(); // 追加
$bbdb->insert( $bbdb->topics, compact( $fields ) );
$topic_id = $bbdb->insert_id;
$bbdb->query( $bbdb->prepare( "UPDATE $bbdb->forums SET topics = topics + 1 WHERE forum_id = %d", $forum_id ) );
wp_cache_delete( $forum_id, 'bb_forum' );
wp_cache_flush( 'bb_forums' );
wp_cache_flush( 'bb_query' );
wp_cache_flush( 'bb_cache_posts_post_ids' );
do_action( 'bb_new_topic', $topic_id );

INSERTを発行する直前で、slugをuniqid()で書き換えちゃいます。

結果

これで再度グループを作ってみると

uniqidは現在時刻を元にidを生成するので、こんなURLになります。
日本語のグループを作っても大丈夫。

ただし、ミリ秒まで同じタイミングでuniqidを実行すると同じ戻り値になる
すなわち同じスラッグになるからバグるという諸刃の剣。
人気あるサイトにはお勧めできない。

また、URLは意味のある文字で構成されていないダメだという方にも向きません。


BuddyPressでトピックが作れない

BuddyPressのバージョンは1.5.1です。

現象

BuddyPressをインストールして、
グループを作ってみる → OK
トピックを作ってみる → 「投稿されたトピックはありません」

は?今作ったんだが?

原因

データベースに、トピックを保存するためのテーブルが作られていない為

対策

phpMyAdmin等で、

CREATE TABLE IF NOT EXISTS `wp_bb_posts` (
	        `post_id` bigint(20) NOT NULL auto_increment,
	        `forum_id` int(10) NOT NULL default 1,
	        `topic_id` bigint(20) NOT NULL default 1,
	        `poster_id` int(10) NOT NULL default 0,
	        `post_text` text NOT NULL,
	        `post_time` datetime NOT NULL default '0000-00-00 00:00:00',
	        `poster_ip` varchar(15) NOT NULL default '',
	        `post_status` tinyint(1) NOT NULL default 0,
	        `post_position` bigint(20) NOT NULL default 0,
	        PRIMARY KEY (`post_id`),
	        KEY `topic_time` (`topic_id`, `post_time`),
	        KEY `poster_time` (`poster_id`, `post_time`),
	        KEY `post_time` (`post_time`),
	        FULLTEXT KEY `post_text` (`post_text`)
	) ENGINE = MYISAM;

とクエリを発行すればOKです。
テーブル名の接頭辞は環境によって書き換えてね。