web-dev-qa-db-ja.com

どうすれば確認できますか AJAX ナンスは正しく実装されていますか?

私はAJAXで動作するシンプルな "お気に入りの投稿"ボタンがあります。 WordPressのナンスを使用するとセキュリティが向上することを理解していますが、その理由や方法がよくわからない。これはまた私がnonceを正しくそして安全に実行したかどうか確認することを不可能にします。

jQueryスクリプト

function favorites_javascript() {       
    $ajax_nonce = wp_create_nonce( "ajax-nonce-favorites" );

    ?>

    <script>
        ( function( $ ) {               
            var ajaxurl = "<?php echo admin_url('admin-ajax.php'); ?>";

            // ...

            // Data to be sent to function
            var data = {
                'action': 'favorites_callback',
                'security': '<?php echo $ajax_nonce; ?>',

                'post_id': post_id,
                'button_action' : button_action
            };

            // Send Data
            jQuery.post(ajaxurl, data, function(response) {     

                // ...

            });

            // ...

        } )( jQuery );
    </script> 

    <?php
} add_action( 'wp_footer', 'favorites_javascript' );

PHPコールバック

function favorites_callback() {
    check_ajax_referer( 'ajax-nonce-favorites', 'security' );

    if ( !is_user_logged_in() ) {
        wp_send_json_error( 'Error!' );
    }

    // Process Data

    wp_die();
} add_action( 'wp_ajax_favorites_callback', 'favorites_callback' );

これはAJAX用のWordPress nonceの正しい実装ですか?ナンスが機能し安全であるかどうかを確認するために、このコードをどのように「テスト」しますか。

1
Swen

あなたのコードはいくつかの微妙な点で不正確ですが、なぜ私たちが最初にワードプレスではノンスであるのか、そしてそれが何に対して保護するのかを知る必要があるのです。

一般的な定義によるNonceは一度だけ使用される数であり、それはリプレイ攻撃と戦うことを意味しています。リプレイ攻撃では、ユーザーの操作をやり直すためにそのコンポーネントを理解せずにトランザクション(httpリクエスト)をリプレイするだけです。悪い状況につながる可能性がある簡単な例は、誰かがあなたの買い物注文を再生し、あなたが関与していなくてももっと同じものを注文する可能性がある場合です。あなたが他の人のネットワークを使う典型的な状況(SSLが役に立つかどうかわからない).

攻撃者が使用すべきURL /パラメータを予測するのは難しいため、nonceは CSRF 攻撃を回避するのに適した方法であることも発見されました。

ワードプレスでは、重要度の順序が逆になっています。最初の優先順位はCSRF攻撃と戦うことであり、リプレイ攻撃との闘いは二次的な重要性です。これは、ワードプレスのノンスが、古典的な「1回限り」の番号ではなく、「1日に固有の」番号になることにつながります。

では、Wordpressはどのようにしてagnsunst CSRFを保護するためにnonceを使用するのでしょうか。コンテキストに基づいてナンスを生成します。 wp_create_nonceコードで見ることができます

function wp_create_nonce($action = -1) {
    $user = wp_get_current_user();
    $uid = (int) $user->ID;
    if ( ! $uid ) {
        /** This filter is documented in wp-includes/pluggable.php */
        $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
    }

    $token = wp_get_session_token();
    $i = wp_nonce_tick();

    return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
}

コンテキストがアクション名、ユーザーID、および現在のログインセッションであり、これにより、ユーザーがナンスの使用を知っている場合でも、異なるアクション、異なるユーザー、さらには同じユーザーの異なるセッションに対して異なるナンスが生成されます。自分のユーザーでアクションを実行するとき(たとえば、投稿を削除するなど)のアクションを実行すると、同じnonceを持つ特別なURLを作成して投稿を削除することはできません。

そして、これがあなたの最初の間違いです(これは多くの人がしています)。これはログインしていないユーザーにナンスを適用しようとしています。ユーザーコンテキストがないと、nonceの全体が単にアクションから派生し、「攻撃者」は同じURLを共有して、ログインしていないユーザー(またはボット)によって使用される可能性があります。

CSRF防止のためにあなたのコードが正しく機能することをテストするには?それが単純なURLの場合、別のユーザーとしてログインしたときに再送信してみてください。 URL自体にナンスが含まれているリクエストを使用する方が簡単ですが、おそらくブラウザ開発者ツール(認証Cookieの変更と再送信)またはcurl/wgetを使用して行うことができます。

あなたのコードに欠けている他の部分は一種のリプレイ攻撃に対する保護です。あなたが投稿Xが好きであることを私が知っていると仮定して、私はあなたが投稿Yも好きであるように要求を再送信して見せさせることができますか?今必要なことは、リクエストのpost_idパラメータを変更するだけです。

この種の攻撃から保護する方法は、ナンスのaction部分を動的にし、何らかの方法で重要なパラメータ、あなたの場合はpost idを含めることです。 Coreは通常delete-post-{post_id}という種類のアクションを使用します。これにより、異なる投稿に対して異なるnonceが生成されます。

テスト方法?ブラウザ開発者ツールを使用して、同じnonceを使用して異なる投稿をお気に入りにできないようにpost idパラメータを変更します。

phpunitタイプのテストの場合は、ワードプレスのコアナンス関数をラップし、それらが異なるユーザー、異なるセッションに対して同じナンスを与えないことをチェックするあなた自身のナンスジェネレータを書くことがおそらく望ましいでしょうさまざまなアクション、さまざまなキー要求パラメータ。

1
Mark Kaplun

それについて WPコーデックス で読むと、実装は正しいようです。

簡単なテストは、$.ajax()securityパラメータを付けずに "偽造された" ajaxリクエストを作成するか、または異なる時間とページから取得した一回だけで同じリクエストを作成することです。

(参考のためにナンスとは何ですか、 wiki

2
Fabrizio Mele