サポート » 使い方全般 » 「nonce」の使い方について

  • お世話になります。
    「nonce」の使い方について教えて頂けませんでしょうか。

    現在HTMLにこのようなボタンがあり、クリックするとuser metaの「’allow’」が更新されるPHPがあります。(JSは割愛)

    ▼HTML
    <button type="button" data-target_id="1">このIDを許可</button>

    ▼PHP

    function my_enqueue_scripts_ajax() {
      $handle = 'my_script';  	
      $jsFile = 'path/to/myscript.js';
      wp_register_script($handle, $jsFile, ['jquery']);
      $action= 'my_ajax_action';
      wp_localize_script($handle, 'MY_AJAX', [
        'api'    => admin_url( 'admin-ajax.php' ),
        'action' => $action,
        'nonce'  => wp_create_nonce( $action ),
      ]);
      wp_enqueue_script($handle);	
    }
    add_action( 'wp_enqueue_scripts', 'my_enqueue_scripts_ajax' );	
    
    add_action( 'wp_ajax_my_ajax_action', 'my_ajax_event' );
    add_action( 'wp_ajax_nopriv_my_ajax_action', 'my_ajax_event' );
    function my_ajax_event() {
      $action = 'my_ajax_action';
      if( check_ajax_referer($action, 'nonce', false) ) {	
    		$u_id = get_current_user_id();
    		$target_id = esc_html( $_POST['target_id'] );
    		update_user_meta( $u_id, 'allow', $target_id );
    	}
      die();
    }

    で質問なのですが、ボタンが複数あったときは、どのようにしたらよろしいのでしょうか?

    つまりHTMLがこのようになっているときです。

    ▼HTML

    <button type="button" data-target_id="1">このIDを許可</button>
    <button type="button" data-target_id="2">このIDを許可</button>

    このようなときは、ボタン1つごとに別々のnonceを生成しなければならないのでしょうか?(もしそうであれば、それはどのように?)

    それとも「nonce」は上のPHPだけあれば、ボタンがいくつあっても適切なセキュリティで処理されるのでしょうか?

    「nonce」というのがよくわかっておらず以上のような疑問に至っておりますが、自力で解決できません。

    どなたかお助け頂けますと幸いでございます。
    宜しくお願い申し上げます。

15件の返信を表示中 - 16 - 30件目 (全40件中)
  • @munyagu さん

    どうもありがとうございます。やはりボタンごとに必要なんですね。困りました。

    仰るようにcheck_ajax_refererの第一引数に$action . $idのように id を含ませるとなると、そこでチェックするidごとの個別のnonceはどうやって発行すればいいのでしょうか?

    質問のコードやネットの情報ですと、nonceは「ページに1つ」が発行されるだけで、「ボタンごとに」というのが難しく感じます。

    @ishitaka さん

    お世話になってます。

    >アクション名に ID が入っていると思っていました。

    とのことですので、やはり個別のIDでチェックする必要はあるということですね。

    そのようなケースでのコードなど、簡単なもので構いませんのでよろしければお聞かせ頂けませんでしょうか。

    コメントにはこのようにありますが、

    アクション名: my_ajax_action+ID+動作(allow,add,other)

    しかしこのIDは、my_enqueue_scripts_ajaxが書かれているテンプレート(functionms.php)では不明なんですよね。どうやってその不明なIDをアクション名として入れればいいでしょうか。

    何度も申し訳ございません。

    ボタン出力時に nonce も併せて出力し、ajax リクエスト時に data に含ませるんではダメなんでしょうか?

    やはり個別のIDでチェックする必要はあるということですね。

    必要はありません。入れたほうがベターだということです。
    アクション毎に nonce を別々にするのが望ましいということです。アクションを別々にする必要はありません。

    Nonce に関しては下記ページをご覧ください。
    https://wpdocs.osdn.jp/WordPress_Nonce

    上記ページの説明でわからないのであれば、私にはそれ以上簡単に説明することは難しいです。

    • この返信は5 ヶ月、 1 週前に  ishitaka さんが編集しました。
    • この返信は5 ヶ月、 1 週前に  ishitaka さんが編集しました。

    @munyagu さん

    ボタンに個別のnonceとは、たとえばこう出力しますよね。

    <?php
    function echo_button( $target_id, $action_name ){
      $nonce = wp_create_nonce( $action_name . $target_id );
      echo '<button type="button" data-action_name="'.$action_name.'" data-target_id="'.$target_id.'" data-nonce="'.$nonce.'">このIDを許可</button>';
    }
    ?>

    では上記PHPのようにして下記のボタンが出力されたとき、このnonceはどのようにしてチェックすればいいと思いますか?

    <button type="button" data-action_name="allow" data-target_id="1" data-nonce="xxx">このIDを許可</button>

    それについて

    >ajax リクエスト時に data に含ませる

    と仰ってますが、それが私にはイメージできず。。すみません。
    もしよろしければサンプルを教えて頂けませんでしょうか。

    @ishitaka さん

    レベルが違いすぎると合わせるのが大変ですよね。すみませんでした。

    先にも書きましたが、私は WordPress での ajax の書き方はよく知りません。
    ですので、私がサンプルを書いたとしても、ググれば出てくる、
    ・ボタン押下時にイベントターゲットのエレメントの -nonce 属性の値を取ってくる
    ・その値を ajax の data に含める
    というだけのものです。
    そのままでは WordPress 上で動きませんし、JavaScript と ajax の基本的な動作を理解されていれば書けるものです。

    rararan998 さんがボタン押下時の処理としてどのような JavaScript を書いているのか貼って頂いたらそれに対してコメントできるかもしれません。

    また、リクエストの改ざんではなく、CSRF 対策だけを考えておられるなら、ishitaka さんが言われているように ID を含める必要は無いと思います。

    @munyagu さん

    ありがとうございます。
    「ググれば出てくる」の2点のJSは分かります。
    このようなイメージですよね。

    $(document).on("click","button", function(){		
    	var action_name = $(this).attr('data-action_name');
    	var target_id = $(this).attr('data-target_id');
    	var nonce = $(this).attr('data-nonce');
    	$.ajax({
    		url: MY_AJAX_um.api,
    		type: 'POST',
    		data: {
    			action: MY_AJAX.action,
    			nonce: MY_AJAX.nonce,
    			action_name: action_name,
    			target_id: target_id,
    			nonce: nonce,
    		}
    	})
    	.done(function(res){
    	}
    	.fail(){
    	}
    });

    分からないのは、ajaxのdataを受け取ったPHPで、その送られたnonceが正しいかどうかをチェックする方法です。

    チェックの方法について、質問のコードではこうしていますよね。

    if( check_ajax_referer($action, 'nonce', false) ) {

    でもこうしてチェックされるnonceは、my_enqueue_scripts_ajaxが発行する1つのnonceに対してのチェックだけだと思うんです。

    でも今回はボタンごとに個別のnonceをチェックしたいわけです。

    そのようなとき、

    if( check_ajax_referer($action, 'nonce', false) ) {

    の部分はどのように書き換えるべきか?

    ということについてお教えて頂けますとうれしいです。

    • この返信は5 ヶ月、 1 週前に  rararan998 さんが編集しました。理由: JSを追記

    まず、JavaScript のソースにある MY_AJAX というオブジェクトが何か分からないことをお伝えしておきます。
    また、POST している action が何に使われるものか私には分からないので無視します。

    check_ajax_refererの第一引数には、wp_create_nonceの引数と同じものを渡すことはご理解されていることと思います。

    なので、同じように POSTした action_name の後に POST した target_id をくっつければいいんじゃないでしょうか。

    ※先にも書いたように、id と nonce をセットでソースに書き出しているので、両方セットで改ざんすることが可能であり、id を含ませている意味は半減する気がします。
    CSRF 的には問題ないと思いますが。

    @munyagu さん

    こんばんは。夜分に失礼いたします。

    >JavaScript のソースにある MY_AJAX というオブジェクトが何か分からない
    「MY_AJAX」は普通にmy_enqueue_scripts_ajax()で作ったものです。
    あ、「MY_AJAX_um」のことですか?でしたらタイプミスでそれは「MY_AJAX」の間違いです。

    >POST している action が何に使われるものか私には分からないので無視します。
    たしかに。笑

    >なので、同じように POSTした action_name の後に POST した target_id をくっつければいいんじゃないでしょうか。
    具体的にはどのようなソースになるということでしょうか?イメージが掴めず辛いです。

    >id と nonce をセットでソースに書き出している
    あぁ、そうですよね。そしたら改ざんを防ぐにはどのように出力すればよいのでしょうか?

    具体的にはどのようなソースになるということでしょうか?イメージが掴めず辛いです。

    POST データを取得する方法は以下サイトを参照して下さい。
    http://php.net/manual/ja/reserved.variables.post.php
    文字列をくっつける方法はご存知と思います。

    そしたら改ざんを防ぐにはどのように出力すればよいのでしょうか?

    ID と nonce を両方ソースに出力する原理上、WordPress の nonce ではソースに出力されている ID についてはリクエスト中の nonce を書き換え可能なことを回避することはできません。
    難読化したりすることは出来るでしょうが。

    少なくとも、nonce を nonce という名前で出したり、ボタンの属性として出力してセットだと分かりやすくするのはやめたらいいと思います。

    @munyagu さん

    POST データを取得する方法は以下サイトを参照して下さい。

    POST データはわかります。

    $action_name = esc_html( $_POST['action_name'] );
    $target_id = esc_html( $_POST['target_id'] );
    $check_nonce = $action_name.$target_id;

    などですよね。

    お聞きしたいのは次のことです。(すみません重複した内容ですが)

    まずチェックの方法について、質問のコードではこうしていますよね。

    if( check_ajax_referer($action, 'nonce', false) ) {

    でもこうしてチェックされるnonceは、my_enqueue_scripts_ajaxが発行する1つのnonceに対してのチェックだけだと思うんです。(つまり2つ前の返信のJSにあるnonce: MY_AJAX.nonce,です。)

    でも今回はボタンごとに個別のnonceをチェックしたいわけです。(つまりさきほど書いた$check_nonceです。)

    そのようなとき、

    if( check_ajax_referer($action, 'nonce', false) ) {

    の部分はどのように書き換えるべきか?

    ということを、具体的なソースコードでもってお聞きできましたら幸いです。

    少なくとも、nonce を nonce という名前で出したり、ボタンの属性として出力してセットだと分かりやすくするのはやめたらいいと思います。

    あの、こればかりお聞きして申し訳ないのですが、では、どうすれば?という話に。すみません笑

    @rararan998 さんのコードで、button 毎に nonce を振ることがどうなのかは、とりあえず置いておくとして、@rararan998 さんのコードで、button 毎に nonce を振ってみました。

    <button type="button" data-action_name="allow" data-target_id="1" data-nonce="<?php echo wp_create_nonce( 'my_ajax_action_1_allow' ); ?>">このIDを許可</button>
    <button type="button" data-action_name="add" data-target_id="1" data-nonce="<?php echo wp_create_nonce( 'my_ajax_action_1_add' ); ?>">このIDを許可</button>

    functions.php:

    function my_enqueue_scripts_ajax() {
    
    	(省略)
    
    	$action= 'my_ajax_action';
    	wp_localize_script( $handle, 'my_ajax', [
    		'api'    => admin_url( 'admin-ajax.php' ),
    		'action' => $action,
    	//	'nonce'  => wp_create_nonce( $action ),
    	]);
    	wp_enqueue_script( $handle );
    }
    add_action( 'wp_enqueue_scripts', 'my_enqueue_scripts_ajax' );
    
    add_action( 'wp_ajax_my_ajax_action', 'my_ajax_event' );
    add_action( 'wp_ajax_nopriv_my_ajax_action', 'my_ajax_event' );
    function my_ajax_event() {
    	if ( ! isset( $_POST['action_name'] ) || ! isset( $_POST['target_id'] ) || ! isset( $_POST['nonce'] ) ) {
    		die();
    	}
    
    	$target_id = $_POST['target_id'];
    	$action_name = $_POST['action_name'];
    
    	if ( check_ajax_referer( "my_ajax_action_{$target_id}_{$action_name}", 'nonce', false ) ) {
    
    		(省略)
    
    	}
    	die();
    }

    myscript.js:

    (function($) {
      $(document).on("click", "button", function(){
        var action_name = $(this).attr('data-action_name');
        var target_id = $(this).attr('data-target_id');
        var nonce = $(this).attr('data-nonce');
        $.ajax({
          url: my_ajax.api,
          type: 'POST',
          data: {
            action: my_ajax.action,
            nonce: nonce,
            action_name: action_name,
            target_id: target_id,
          }
        }).done(function(res){
        });
      });
    })(jQuery);

    または、

    <button type="button" data-action_name="allow" data-target_id="1">このIDを許可</button>
    <button type="button" data-action_name="add" data-target_id="1">このIDを許可</button>

    functions.php:

    function my_enqueue_scripts_ajax() {
    
    	(省略)
    
    	$action= 'my_ajax_action';
    	wp_localize_script( $handle, 'my_ajax', [
    		'api'    => admin_url( 'admin-ajax.php' ),
    		'action' => $action,
    		'allow_nonce' => wp_create_nonce( 'my_ajax_action_1_allow' ),
    		'add_nonce' => wp_create_nonce( 'my_ajax_action_1_add' ),
    	]);
    	wp_enqueue_script( $handle );
    }
    add_action( 'wp_enqueue_scripts', 'my_enqueue_scripts_ajax' );
    
    add_action( 'wp_ajax_my_ajax_action', 'my_ajax_event' );
    add_action( 'wp_ajax_nopriv_my_ajax_action', 'my_ajax_event' );
    function my_ajax_event() {
    	if ( ! isset( $_POST['action_name'] ) || ! isset( $_POST['target_id'] ) || ! isset( $_POST['nonce'] ) ) {
    		die();
    	}
    
    	$target_id = $_POST['target_id'];
    	$action_name = $_POST['action_name'];
    
    	if ( check_ajax_referer( "my_ajax_action_{$target_id}_{$action_name}", 'nonce', false ) ) {
    
    		(省略)
    
    	}
    	die();
    }

    myscript.js:

    (function($) {
      $(document).on("click", "button", function(){
        var action_name = $(this).attr('data-action_name');
        var target_id = $(this).attr('data-target_id');
        $.ajax({
          url: my_ajax.api,
          type: 'POST',
          data: {
            action: my_ajax.action,
            nonce: (action_name == 'allow') ? my_ajax.allow_nonce : my_ajax.add_nonce,
            action_name: action_name,
            target_id: target_id,
          }
        }).done(function(res){
        });
      });
    })(jQuery);

    @ishitakaさん

    なるほどこのようにチェックできるんですね。

    if ( check_ajax_referer( "my_ajax_action_{$target_id}_{$action_name}", 'nonce', false ) ) {

    変数を{}で囲む({$target_id}{$action_name}など)はどういった効果が期待されるテクニックなのでしょうか。初めてみました。大変勉強になります。どうもありがとうございます。

    そしてお二方のご高察ではボタンごとにnonceを出力するときに、ボタンと一緒に出力するのはよろしくないとのこと。

    そこで、それをやめたのが「または」のコードになるわけですね。
    つまりボタンと一緒に出力するのをやめて、my_enqueue_scripts_ajax()を介してコメントアウトの形で出力したぞと。

    どうもありがとうございます。大変わかりやすかったです。やっと全体の流れが理解できました。

15件の返信を表示中 - 16 - 30件目 (全40件中)
  • このトピックに返信するにはログインが必要です。