REST API'sinden alınan nonce geçersiz ve wp_localize_script içinde üretilen nonce'den farklı


10

Google'dan gelenler için: Ne yaptığınızı gerçekten bilmiyorsanız , büyük olasılıkla REST API'sından nonces almamalısınız . REST API ile Çerez tabanlı kimlik doğrulaması okunur anlamına eklentileri ve temaları. Tek sayfalık bir uygulama için muhtemelen OAuth kullanmalısınız .

Bu soru, tek sayfalı uygulamalar oluştururken gerçekte nasıl kimlik doğrulaması yapmanız gerektiği konusunda dokümantasyonun açık olmadığı / açık olmadığı, JWT'lerin web uygulamaları için gerçekten uygun olmadığı ve OAuth'un çerez tabanlı kimlik doğrulamasından daha zor uygulandığı için var.


El kitabının , Omurga JavaScript istemcisinin nonces'i nasıl işlediğine dair bir örneği var ve örneği takip edersem, / wp / v2 / posts gibi yerleşik uç noktaların kabul ettiği bir nonce alıyorum.

\wp_localize_script("client-js", "theme", [
  'nonce' => wp_create_nonce('wp_rest'),
  'user' => get_current_user_id(),

]);

Ancak, Backbone kullanmak söz konusu değildir ve temalar da öyle, bu yüzden aşağıdaki eklentiyi yazdım:

<?php
/*
Plugin Name: Nonce Endpoint
*/

add_action('rest_api_init', function () {
  $user = get_current_user_id();
  register_rest_route('nonce/v1', 'get', [
    'methods' => 'GET',
    'callback' => function () use ($user) {
      return [
        'nonce' => wp_create_nonce('wp_rest'),
        'user' => $user,
      ];
    },
  ]);

  register_rest_route('nonce/v1', 'verify', [
    'methods' => 'GET',
    'callback' => function () use ($user) {
      $nonce = !empty($_GET['nonce']) ? $_GET['nonce'] : false;
      return [
        'valid' => (bool) wp_verify_nonce($nonce, 'wp_rest'),
        'user' => $user,
      ];
    },
  ]);
});

JavaScript konsolunda biraz uğraştım ve aşağıdakileri yazdım:

var main = async () => { // var because it can be redefined
  const nonceReq = await fetch('/wp-json/nonce/v1/get', { credentials: 'include' })
  const nonceResp = await nonceReq.json()
  const nonceValidReq = await fetch(`/wp-json/nonce/v1/verify?nonce=${nonceResp.nonce}`, { credentials: 'include' })
  const nonceValidResp = await nonceValidReq.json()
  const addPost = (nonce) => fetch('/wp-json/wp/v2/posts', {
    method: 'POST',
    credentials: 'include',
    body: JSON.stringify({
      title: `Test ${Date.now()}`,
      content: 'Test',
    }),
    headers: {
      'X-WP-Nonce': nonce,
      'content-type': 'application/json'
    },
  }).then(r => r.json()).then(console.log)

  console.log(nonceResp.nonce, nonceResp.user, nonceValidResp)
  console.log(theme.nonce, theme.user)
  addPost(nonceResp.nonce)
  addPost(theme.nonce)
}

main()

Beklenen sonuç iki yeni gönderi, ancak Cookie nonce is invalidbirinciden alıyorum ve ikincisi gönderiyi başarıyla yaratıyor. Muhtemelen nonces farklı olduğu için, ama neden? Her iki istekte de aynı kullanıcı olarak oturum açtım.

resim açıklamasını buraya girin

Yaklaşımım yanlışsa, nonce'yi nasıl edinmeliyim?

Düzenle :

Ben globaller karıştırmasını çalıştı çok şans olmadan . Wp_loaded eylemini kullanarak biraz şanslı var:

<?php
/*
Plugin Name: Nonce Endpoint
*/

$nonce = 'invalid';
add_action('wp_loaded', function () {
  global $nonce;
  $nonce = wp_create_nonce('wp_rest');
});

add_action('rest_api_init', function () {
  $user = get_current_user_id();
  register_rest_route('nonce/v1', 'get', [
    'methods' => 'GET',
    'callback' => function () use ($user) {
      return [
        'nonce' => $GLOBALS['nonce'],
        'user' => $user,
      ];
    },
  ]);

  register_rest_route('nonce/v1', 'verify', [
    'methods' => 'GET',
    'callback' => function () use ($user) {
      $nonce = !empty($_GET['nonce']) ? $_GET['nonce'] : false;
      error_log("verify $nonce $user");
      return [
        'valid' => (bool) wp_verify_nonce($nonce, 'wp_rest'),
        'user' => $user,
      ];
    },
  ]);
});

Şimdi yukarıdaki JavaScript'i çalıştırdığımda iki gönderi oluşturuldu, ancak doğrulama uç noktası başarısız!

resim açıklamasını buraya girin

Wp_verify_nonce hata ayıklamaya gittim:

function wp_verify_nonce( $nonce, $action = -1 ) {
  $nonce = (string) $nonce;
  $user = wp_get_current_user();
  $uid = (int) $user->ID; // This is 0, even though the verify endpoint says I'm logged in as user 2!

Biraz giriş ekledim

// Nonce generated 0-12 hours ago
$expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 );
error_log("expected 1 $expected received $nonce uid $uid action $action");
if ( hash_equals( $expected, $nonce ) ) {
  return 1;
}

// Nonce generated 12-24 hours ago
$expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
error_log("expected 2 $expected received $nonce uid $uid action $action");
if ( hash_equals( $expected, $nonce ) ) {
  return 2;
}

ve JavaScript kodu şimdi aşağıdaki girişlerle sonuçlanır. Gördüğünüz gibi, doğrulama bitiş noktası çağrıldığında uid 0 olur.

[01-Mar-2018 11:41:57 UTC] verify 716087f772 2
[01-Mar-2018 11:41:57 UTC] expected 1 b35fa18521 received 716087f772 uid 0 action wp_rest
[01-Mar-2018 11:41:57 UTC] expected 2 dd35d95cbd received 716087f772 uid 0 action wp_rest
[01-Mar-2018 11:41:58 UTC] expected 1 716087f772 received 716087f772 uid 2 action wp_rest
[01-Mar-2018 11:41:58 UTC] expected 1 716087f772 received 716087f772 uid 2 action wp_rest

Yanıtlar:


3

Şuna daha yakından bakın function rest_cookie_check_errors().

Nonce'yi geçtiğinizde /wp-json/nonce/v1/get, ilk etapta nonce göndermezsiniz. Bu işlev, bu kodla kimlik doğrulamanızı geçersiz kılar:

if ( null === $nonce ) {
    // No nonce at all, so act as if it's an unauthenticated request.
    wp_set_current_user( 0 );
    return true;
}

Bu yüzden REST çağrısından ya da temadan almaktan farklı bir nonce alıyorsunuz. REST çağrısı, alma isteğinde geçerli bir nonce göndermediğiniz için giriş kimlik bilgilerinizi (bu durumda çerez yetkilendirme yoluyla) kasıtlı olarak tanımıyor.

Şimdi, wp_loaded kodunuzun çalışmasının nedeni, bu geri kalan kod girişinizi geçersiz kılmadan önce nonce'yi alıp bir globale kaydetmenizdi. Doğrulama başarısız olur, çünkü geri kalan kod doğrulama yapılmadan önce giriş bilgilerinizi geçersiz kılar.


Bu işleve bile bakmadım, ama muhtemelen mantıklı. Mesele şu ki, neden GET isteği için geçerli bir nonce içermeliyim? (Şimdi anlıyorum, ama açık olmaktan çok uzak) / verify uç noktasının tüm noktası, nonce'nin hala geçerli olup olmadığını ve bayat mı yoksa geçerli değilse, yeni bir nonce olup olmadığını kontrol edebilmemdir.
Christian

Rest_cookie_check_errors kaynağına dayanarak, uç noktamı kontrol etmemesi için değiştirmemeliyim $_GET['nonce'], ancak nonce üstbilgisi veya $_GET['_wpnonce']parametresini değiştirmeliyim. Doğru?
Christian

1

Bu çözüm çalışırken, önerilmez . OAuth tercih edilen seçimdir.


Sanırım anladım.

Ben düşünüyorum wp_get_current_user uygun kullanıcı nesnesini almak için başarısız olarak, bu wp_verify_nonce bozuldu.

Otto tarafından gösterildiği gibi değil.

Neyse ki bir filtresi var: $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );

Bu filtreyi kullanarak aşağıdakileri yazabildim ve JavaScript kodu olması gerektiği gibi yürütüldü:

resim açıklamasını buraya girin

<?php
/*
Plugin Name: Nonce Endpoint
*/

$nonce = 'invalid';
add_action('wp_loaded', function () {
  global $nonce;
  $nonce = wp_create_nonce('wp_rest');
});

add_action('rest_api_init', function () {
  $user = get_current_user_id();
  register_rest_route('nonce/v1', 'get', [
    'methods' => 'GET',
    'callback' => function () use ($user) {
      return [
        'nonce' => $GLOBALS['nonce'],
        'user' => $user,
      ];
    },
  ]);

  register_rest_route('nonce/v1', 'verify', [
    'methods' => 'GET',
    'callback' => function () use ($user) {
      $nonce = !empty($_GET['nonce']) ? $_GET['nonce'] : false;
      add_filter("nonce_user_logged_out", function ($uid, $action) use ($user) {
        if ($uid === 0 && $action === 'wp_rest') {
          return $user;
        }

        return $uid;
      }, 10, 2);

      return [
        'status' => wp_verify_nonce($nonce, 'wp_rest'),
        'user' => $user,
      ];
    },
  ]);
});

Düzeltmeyle ilgili bir güvenlik sorunu tespit ederseniz, lütfen bana bir not verin, şu anda küreselden başka bir şey göremiyorum.


0

Tüm bu koda baktığınızda, probleminiz kapakların kullanımı gibi görünüyor. At initaşamada sadece kanca belirlesin ve tüm çekirdeğin gibi verileri değerlendirmek değil bitmiş yükleme vardı ve başlatılıyor.

İçinde

add_action('rest_api_init', function () {
  $user = get_current_user_id();
  register_rest_route('nonce/v1', 'get', [
    'methods' => 'GET',
    'callback' => function () use ($user) {
      return [
        'nonce' => $GLOBALS['nonce'],
        'user' => $user,
      ];
    },
  ]);

$userkapatılması kullanılmak üzere erken bağlı, ama size kimse vaat çerez zaten ele aldığını ve onlar üzerinde bir kullanıcı tabanlı doğrulanmış oldu. Daha iyi bir kod

add_action('rest_api_init', function () {
  register_rest_route('nonce/v1', 'get', [
    'methods' => 'GET',
    'callback' => function () {
    $user = get_current_user_id();
      return [
        'nonce' => $GLOBALS['nonce'],
        'user' => $user,
      ];
    },
  ]);

Wordpress'teki herhangi bir kancada her zaman olduğu gibi, mümkün olan en son kancayı kullanın ve asla gerekmeyen bir şeyi önceden hesaplamaya çalışmayın.


Set_current_user init & after_setup_theme'den önce neyin çalıştığını ve hangi sırayla çalıştığını anlamak için Sorgu Monitörleri eylemleri ve kancaları bölümünü kullandım, $ kullanıcısı dışında ve kapanmadan önce tanımlanmayla ilgili bir sorun olmamalıdır.
Christian

@Christian ve hepsi json API bağlamında alakalı olmayabilir. Sorgu monitörü bu bağlamda çalışırsa çok
şaşırırdım
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.