Webサイトのお助け隊

WordPressのREST APIを安全に利用するための認証とアクセス制御

2 views
約27分

REST APIを無防備に公開していたことで起きた情報漏洩

クライアントから「サイトのユーザー情報が外部に漏れているみたいなんです」と連絡が来たのは、金曜日の夕方だった。週末を控えて、正直「勘弁してくれよ」と思ったけど、話を聞くとかなり深刻だった。

競合他社が、そのクライアントのサイトに登録している全ユーザーのメールアドレスと名前のリストを持っていたらしい。どこから漏れたのか調べてみたら、原因はWordPressのREST APIだった。

具体的には、/wp-json/wp/v2/usersというエンドポイントが認証なしで公開されていて、誰でもユーザー情報を取得できる状態になっていたんだ。デフォルトの設定では、ユーザーのID、名前、スラッグ、アバターURLなどが丸見えになる。

REST APIは便利だ。でも、セキュリティ設定を怠ると、サイトの情報が筒抜けになってしまう。

この記事では、WordPressのREST APIを安全に利用するための認証方法とアクセス制御について、実践的な設定方法を紹介する。REST APIの便利さを享受しながら、セキュリティリスクを最小限に抑える方法を、一緒に見ていこう。

WordPressのREST APIとは何か

REST APIの基本的な仕組み

WordPressのREST APIは、バージョン4.7から標準で搭載されている機能だ。簡単に言うと、WordPressのデータ(投稿、ページ、ユーザー、メディアなど)に外部からアクセスできる仕組みだよね。

従来のWordPressは、管理画面から操作するのが基本だった。でもREST APIを使えば、JavaScriptアプリケーション、モバイルアプリ、他のウェブサービスから、WordPressのデータを取得したり更新したりできる。

たとえば、こんなURLにアクセスすると、最新の投稿一覧がJSON形式で返ってくる:

https://example.com/wp-json/wp/v2/posts

これは確かに便利だ。フロントエンドとバックエンドを分離した開発ができるし、ヘッドレスCMSとしてWordPressを使うこともできる。

デフォルト設定の危険性

でも、問題はここからだ。REST APIはデフォルトで有効になっていて、しかも多くのエンドポイントが認証なしでアクセスできる状態になっている。

公開されているエンドポイントの例:

  • /wp-json/wp/v2/posts – 投稿一覧
  • /wp-json/wp/v2/pages – 固定ページ一覧
  • /wp-json/wp/v2/users – ユーザー情報
  • /wp-json/wp/v2/categories – カテゴリー一覧
  • /wp-json/wp/v2/tags – タグ一覧

これらのエンドポイントから、どんな情報が取得できるか試してみると分かるけど、かなり詳細なデータが取れてしまうんだ。

特に問題なのは、ユーザー情報だ。/wp-json/wp/v2/usersにアクセスすると、サイトに登録されている全ユーザーの情報が取得できる。これには、ユーザー名、表示名、投稿数などが含まれる。

攻撃者はこの情報を使って、ブルートフォース攻撃のターゲットを絞り込んだり、ソーシャルエンジニアリング攻撃を仕掛けたりする。実際、僕が見てきたケースでも、REST APIから取得したユーザー名を使った攻撃が何度もあった。

REST APIが必要かどうかを見極める

まず最初に考えるべきなのは、「そもそもREST APIが必要なのか」ということだ。

REST APIが必要なケース:

  • ヘッドレスCMSとしてWordPressを使っている
  • モバイルアプリからWordPressのデータにアクセスする
  • JavaScriptフレームワーク(React、Vueなど)でフロントエンドを構築している
  • 外部サービスとの連携が必要

でも、普通のブログやコーポレートサイトなら、REST APIは必要ないことが多い。ブロックエディタ(Gutenberg)は内部的にREST APIを使っているけど、外部からのアクセスを許可する必要はない。

必要ないなら、REST APIを完全に無効化するか、少なくとも外部からのアクセスを制限すべきだ。

REST APIを無効化する方法

完全に無効化する

REST APIをまったく使わないなら、完全に無効化するのが最もシンプルで安全だ。functions.phpに以下のコードを追加すればいい:

<?php
// REST APIを完全に無効化
add_filter( 'rest_authentication_errors', function( $result ) {
    if ( ! empty( $result ) ) {
        return $result;
    }
    if ( ! is_user_logged_in() ) {
        return new WP_Error(
            'rest_not_logged_in',
            'REST APIへのアクセスにはログインが必要です。',
            array( 'status' => 401 )
        );
    }
    return $result;
});
?>

このコードは、ログインしていないユーザーからのREST APIアクセスをすべて拒否する。管理画面にログインしているユーザーなら使えるから、ブロックエディタは正常に動作するんだ。

特定のエンドポイントだけを無効化

REST API自体は使いたいけど、特定のエンドポイント(特にユーザー情報)だけを制限したい場合は、こんな方法がある:

<?php
// ユーザー情報エンドポイントを無効化
add_filter( 'rest_endpoints', function( $endpoints ) {
    if ( isset( $endpoints['/wp/v2/users'] ) ) {
        unset( $endpoints['/wp/v2/users'] );
    }
    if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
        unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
    }
    return $endpoints;
});
?>

これで、ユーザー情報のエンドポイントだけがアクセス不可になる。他のエンドポイント(投稿やページ)は引き続き利用できる。

プラグインで管理する

コードを書きたくない人には、プラグインを使う方法もある。「Disable REST API」や「Disable WP REST API」といったプラグインを使えば、管理画面から簡単にREST APIの動作を制御できるんだ。

ただし、プラグインを使う場合は、定期的に更新されているか確認してほしい。メンテナンスが止まっているプラグインは、それ自体がセキュリティリスクになる可能性がある。

認証を実装する方法

REST APIを外部サービスやアプリケーションから利用する場合、適切な認証を実装する必要がある。WordPressは複数の認証方法をサポートしているから、用途に応じて選べるんだ。

Cookie認証(内部利用向け)

Cookie認証は、WordPressの標準的な認証方式だ。ブロックエディタやWordPress管理画面が内部的に使っている方式でもある。

Cookie認証は、同一ドメイン内でのみ使えるという制限がある。だから、外部サービスからのアクセスには向かない。でも、同じドメイン内のJavaScriptアプリケーションからアクセスする分には問題ない。

実装例:

JavaScriptからREST APIにアクセスする際、nonceを使ってリクエストを検証する:

// WordPress側でnonceを生成
wp_localize_script( 'my-script', 'wpApiSettings', array(
    'root' => esc_url_raw( rest_url() ),
    'nonce' => wp_create_nonce( 'wp_rest' )
) );

// JavaScript側でnonceを使ってリクエスト
fetch( wpApiSettings.root + 'wp/v2/posts', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': wpApiSettings.nonce
    },
    body: JSON.stringify({
        title: '新しい投稿',
        content: '投稿の内容',
        status: 'publish'
    })
})
.then( response => response.json() )
.then( data => console.log( data ) );

nonceは一定期間で無効になるから、セキュリティ的にも安心だ。

Basic認証(開発環境向け)

Basic認証は、HTTPの標準的な認証方式だ。ユーザー名とパスワードをBase64エンコードしてヘッダーに含める方式だよね。

注意点:Basic認証は本番環境では使うべきではない。HTTPSを使っていても、パスワードが比較的簡単にデコードできるからだ。開発環境やテスト環境でのみ使うようにしてほしい。

プラグイン「Basic Authentication Handler」をインストールすれば、簡単に設定できる:

# cURLでの使用例
curl -X GET \
  https://example.com/wp-json/wp/v2/posts \
  -u username:password

Application Passwords(推奨)

WordPress 5.6から追加されたApplication Passwords機能は、REST API認証のベストプラクティスだと言っていい。

この機能を使うと、WordPressの管理画面から専用のパスワードを生成できる。本来のログインパスワードとは別のパスワードだから、万が一漏洩しても、本アカウントへの影響は最小限に抑えられるんだ。

設定手順:

  1. WordPress管理画面の「ユーザー」→「プロフィール」を開く
  2. 「アプリケーションパスワード」セクションまでスクロール
  3. 新しいアプリケーション名を入力(例:「モバイルアプリ」)
  4. 「新しいアプリケーションパスワードを追加」をクリック
  5. 生成されたパスワードを安全に保管

このパスワードを使って、REST APIにアクセスできる:

# cURLでの使用例
curl -X GET \
  https://example.com/wp-json/wp/v2/posts \
  --user "username:xxxx xxxx xxxx xxxx xxxx xxxx"

Application Passwordsは、必要に応じて個別に無効化や削除ができる。これが、通常のパスワードと比べて安全な理由だ。

JWT認証(高度な利用向け)

JWT(JSON Web Token)認証は、より高度な認証方式だ。トークンベースの認証で、ステートレスな設計が可能になる。

JWTを使うには、プラグイン「JWT Authentication for WP REST API」をインストールする必要がある。

実装の流れ:

  1. wp-config.phpに秘密鍵を設定
<?php
define('JWT_AUTH_SECRET_KEY', 'your-top-secret-key-here');
define('JWT_AUTH_CORS_ENABLE', true);
?>
  1. .htaccessでAuthorizationヘッダーを有効化
# Apache用
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
  1. トークンを取得
curl -X POST \
  https://example.com/wp-json/jwt-auth/v1/token \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "admin",
    "password": "your-password"
  }'
  1. 取得したトークンでAPIにアクセス
curl -X GET \
  https://example.com/wp-json/wp/v2/posts \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...'

JWTは有効期限を設定できるし、トークンをリフレッシュする機能もある。外部サービスとの連携や、モバイルアプリでの利用に適しているんだ。

アクセス制御を細かく設定する

認証だけでなく、「誰が何をできるか」というアクセス制御も重要だ。WordPressにはユーザー権限の仕組みがあるけど、REST APIでもこれを活用できる。

カスタムエンドポイントでの権限チェック

独自のREST APIエンドポイントを作る場合、権限チェックを必ず実装すべきだ。

<?php
// カスタムエンドポイントの登録
add_action( 'rest_api_init', function() {
    register_rest_route( 'myplugin/v1', '/data', array(
        'methods' => 'GET',
        'callback' => 'get_custom_data',
        'permission_callback' => 'check_permission'
    ));
});

// 権限チェック関数
function check_permission() {
    // ログインユーザーのみアクセス可能
    return is_user_logged_in();

    // または、特定の権限を持つユーザーのみ
    // return current_user_can( 'edit_posts' );

    // または、管理者のみ
    // return current_user_can( 'manage_options' );
}

// データ取得関数
function get_custom_data( $request ) {
    return array(
        'message' => 'これは保護されたデータです',
        'data' => array( 'key' => 'value' )
    );
}
?>

permission_callbackを設定しないと、そのエンドポイントは誰でもアクセスできる状態になってしまう。これは危険だから、必ず設定してほしい。

ユーザー権限に応じた制御

WordPressには、管理者、編集者、投稿者、寄稿者、購読者という標準的な権限レベルがある。REST APIでも、これらの権限に応じて動作を変えることができるんだ。

<?php
add_action( 'rest_api_init', function() {
    register_rest_route( 'myplugin/v1', '/posts', array(
        'methods' => 'POST',
        'callback' => 'create_custom_post',
        'permission_callback' => function() {
            // 投稿者以上の権限が必要
            return current_user_can( 'publish_posts' );
        }
    ));

    register_rest_route( 'myplugin/v1', '/settings', array(
        'methods' => 'PUT',
        'callback' => 'update_settings',
        'permission_callback' => function() {
            // 管理者のみ
            return current_user_can( 'manage_options' );
        }
    ));
});
?>

この方法なら、エンドポイントごとに異なる権限要件を設定できる。細かい制御が必要な場合に便利だ。

IPアドレスによる制限

特定のIPアドレスからのアクセスのみを許可する方法もある。社内システムとの連携など、アクセス元が固定されている場合に有効だ。

<?php
add_filter( 'rest_authentication_errors', function( $result ) {
    // 既にエラーがある場合はそのまま返す
    if ( is_wp_error( $result ) ) {
        return $result;
    }

    // 許可するIPアドレスのリスト
    $allowed_ips = array(
        '192.168.1.100',
        '203.0.113.5',
    );

    // クライアントのIPアドレスを取得
    $client_ip = $_SERVER['REMOTE_ADDR'];

    // 許可リストにない場合はエラーを返す
    if ( ! in_array( $client_ip, $allowed_ips, true ) ) {
        return new WP_Error(
            'rest_forbidden',
            'このIPアドレスからのアクセスは許可されていません。',
            array( 'status' => 403 )
        );
    }

    return $result;
});
?>

ただし、この方法は固定IPアドレスが使える環境でないと意味がない。動的IPアドレスの場合は、別の方法を検討すべきだ。

レート制限の実装

REST APIへのアクセス頻度を制限することも、セキュリティ対策として有効だ。ブルートフォース攻撃やDDoS攻撃を防ぐことができる。

<?php
// トランジェントを使った簡易的なレート制限
add_filter( 'rest_authentication_errors', function( $result ) {
    if ( is_wp_error( $result ) ) {
        return $result;
    }

    $client_ip = $_SERVER['REMOTE_ADDR'];
    $transient_key = 'rest_api_rate_limit_' . md5( $client_ip );

    // 現在のアクセス回数を取得
    $access_count = get_transient( $transient_key );

    if ( $access_count === false ) {
        // 初回アクセス
        set_transient( $transient_key, 1, MINUTE_IN_SECONDS );
    } else if ( $access_count >= 60 ) {
        // 1分間に60回を超えた場合
        return new WP_Error(
            'rest_too_many_requests',
            'アクセス頻度が高すぎます。しばらくしてから再度お試しください。',
            array( 'status' => 429 )
        );
    } else {
        // アクセス回数を増やす
        set_transient( $transient_key, $access_count + 1, MINUTE_IN_SECONDS );
    }

    return $result;
});
?>

この例では、1分間に60回までのアクセスを許可している。これを超えると、429エラー(Too Many Requests)を返すんだ。

より本格的なレート制限が必要なら、CloudflareなどのCDNサービスを使う方法もある。サーバーレベルでレート制限をかけられるから、WordPressへの負荷も減らせる。

実践的なセキュリティ強化策

ここまで紹介した方法を組み合わせて、実際の運用に適したセキュリティ設定を構築していこう。

段階的なアクセス制限

いきなり全部を制限するんじゃなくて、段階的に制限を強めていくのがおすすめだ。急に厳しくすると、既存の機能が動かなくなる可能性がある。

推奨される段階:

  1. 第1段階:ユーザー情報の保護
    まずは最も危険なユーザーエンドポイントを制限する。
  2. 第2段階:認証の強制
    必要に応じて、特定のエンドポイントに認証を要求する。
  3. 第3段階:レート制限の導入
    アクセス頻度を制限して、攻撃を防ぐ。
  4. 第4段階:ログと監視
    REST APIへのアクセスをログに記録し、異常なアクセスパターンを検知する。

ログ記録と監視

REST APIへのアクセスをログに記録しておくと、問題が起きたときの調査が楽になる。

<?php
// REST APIアクセスのログ記録
add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) {
    $route = $request->get_route();
    $method = $request->get_method();
    $client_ip = $_SERVER['REMOTE_ADDR'];
    $user_id = get_current_user_id();

    // ログファイルに記録
    $log_entry = sprintf(
        "[%s] %s %s from %s (User: %s)\n",
        date( 'Y-m-d H:i:s' ),
        $method,
        $route,
        $client_ip,
        $user_id ? $user_id : 'Guest'
    );

    error_log( $log_entry, 3, WP_CONTENT_DIR . '/rest-api-access.log' );

    return $result;
}, 10, 3 );
?>

このログを定期的にチェックすることで、不審なアクセスパターンを見つけられる。

HTTPSの必須化

REST APIを使う場合、HTTPSは絶対に必要だ。認証情報がネットワーク上を流れるわけだから、暗号化されていないと簡単に盗まれてしまう。

wp-config.phpで、HTTPS接続を強制することができる:

<?php
// HTTPS接続を強制
define( 'FORCE_SSL_ADMIN', true );

// すべてのREST APIリクエストでHTTPSを要求
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 
     $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'http' ) {
    $_SERVER['HTTPS'] = 'off';
}
?>

Let’s Encryptを使えば、無料でSSL証明書を取得できる。HTTPSにしない理由はないよね。

セキュリティヘッダーの設定

REST APIのレスポンスに、適切なセキュリティヘッダーを追加することも重要だ。

<?php
// セキュリティヘッダーの追加
add_filter( 'rest_pre_serve_request', function( $served, $result, $request, $server ) {
    header( 'X-Content-Type-Options: nosniff' );
    header( 'X-Frame-Options: DENY' );
    header( 'X-XSS-Protection: 1; mode=block' );
    header( 'Referrer-Policy: no-referrer-when-downgrade' );

    return $served;
}, 10, 4 );
?>

これらのヘッダーは、XSSやクリックジャッキングなどの攻撃を防ぐのに役立つ。

CORSの適切な設定

外部ドメインからREST APIにアクセスする場合、CORS(Cross-Origin Resource Sharing)の設定が必要になる。でも、これを間違えると、誰でもアクセスできる状態になってしまうんだ。

<?php
// CORS設定(特定のドメインのみ許可)
add_action( 'rest_api_init', function() {
    remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' );

    add_filter( 'rest_pre_serve_request', function( $value ) {
        $allowed_origins = array(
            'https://myapp.com',
            'https://another-trusted-domain.com',
        );

        $origin = get_http_origin();

        if ( in_array( $origin, $allowed_origins, true ) ) {
            header( 'Access-Control-Allow-Origin: ' . $origin );
            header( 'Access-Control-Allow-Methods: GET, POST, PUT, DELETE' );
            header( 'Access-Control-Allow-Credentials: true' );
            header( 'Access-Control-Allow-Headers: Authorization, Content-Type' );
        }

        return $value;
    });
}, 15 );
?>

Access-Control-Allow-Origin: *のような設定は、開発環境以外では絶対に使わないでほしい。これは「すべてのドメインからのアクセスを許可する」という意味だからね。

よくある質問(FAQ)

Q1: REST APIを完全に無効化すると、ブロックエディタが使えなくなりますか?

いや、大丈夫だ。今回紹介した方法(ログインユーザーのみアクセス可能にする)なら、ブロックエディタは正常に動作する。

ブロックエディタは、WordPressの管理画面内で動作するから、Cookie認証が使われる。だから、外部からのアクセスを制限しても、ログインしているユーザーは問題なく使えるんだ。

ただし、add_filter( 'rest_enabled', '__return_false' );のような方法で完全に無効化すると、ブロックエディタも動かなくなる。この方法は使わないでほしい。

Q2: Application Passwordsが表示されない場合はどうすればいいですか?

Application Passwordsは、HTTPS環境でのみ使える機能だ。HTTP接続のサイトでは表示されない。

もしローカル環境などでHTTPSが使えない場合は、wp-config.phpに以下を追加すれば、強制的に有効化できる:

<?php
define( 'WP_ENVIRONMENT_TYPE', 'local' );
?>

ただし、本番環境では必ずHTTPSを使うべきだ。HTTPでApplication Passwordsを使うのは、セキュリティ上おすすめできない。

Q3: JWTトークンの有効期限はどのくらいに設定すべきですか?

用途によって異なるけど、一般的には以下が目安だ:

  • モバイルアプリ: 7日〜30日(長めに設定し、リフレッシュトークンを併用)
  • Webアプリケーション: 1時間〜24時間
  • サーバー間通信: 5分〜1時間(短めが安全)

トークンの有効期限を設定するには:

<?php
// wp-config.phpに追加
define('JWT_AUTH_EXPIRE_TIME', 3600); // 秒単位(3600秒 = 1時間)
?>

有効期限が短いほど安全だけど、ユーザーが頻繁に再ログインする必要がある。この balance を考えて設定してほしい。

Q4: REST APIへの攻撃を受けている場合、どう対処すればいいですか?

まず、ログを確認して攻撃元のIPアドレスを特定する。そして、そのIPアドレスを.htaccessやファイアウォールでブロックするんだ。

.htaccessでのブロック:

<IfModule mod_authz_core.c>
    # REST APIへのアクセスを制限
    <Files "wp-json">
        Require all denied
        Require ip 192.168.1.0/24
    </Files>
</IfModule>

大規模な攻撃を受けている場合は、Cloudflareなどのセキュリティサービスを導入することをおすすめする。サーバーに到達する前に、悪質なリクエストをブロックしてくれるからね。

Q5: カスタムエンドポイントを作る際の注意点は?

カスタムエンドポイントを作る際は、以下の点に注意してほしい:

  1. 必ずpermission_callbackを設定する
    設定しないと、誰でもアクセスできる状態になる。
  2. 入力値の検証とサニタイズ
    ユーザーからの入力は、必ず検証してサニタイズする。
<?php
register_rest_route( 'myplugin/v1', '/data', array(
    'methods' => 'POST',
    'callback' => 'save_data',
    'permission_callback' => function() {
        return current_user_can( 'edit_posts' );
    },
    'args' => array(
        'title' => array(
            'required' => true,
            'validate_callback' => function( $param ) {
                return is_string( $param );
            },
            'sanitize_callback' => 'sanitize_text_field'
        )
    )
));
?>
  1. エラーメッセージに機密情報を含めない
    エラーメッセージから、システムの内部構造が推測されることがある。
  2. データベースクエリはprepareを使う
    SQLインジェクション対策として、必ず$wpdb->prepare()を使う。

これらを守れば、安全なカスタムエンドポイントが作れる。

まとめ:REST APIのセキュリティは「設定次第」

REST APIは、WordPressの可能性を大きく広げる機能だ。ヘッドレスCMS、モバイルアプリ、外部サービスとの連携。これまでできなかったことが、REST APIのおかげで実現できるようになった。

でも、便利さの裏には危険も潜んでいる。デフォルトの設定のまま放置すると、サイトの情報が筒抜けになってしまう。実際、僕が見てきた多くのサイトが、REST APIのセキュリティ設定を見落としていたんだ。

この記事のポイントをおさらいしよう:

  • REST APIが必要かどうかを見極める
  • 不要なら無効化、必要なら適切な認証を実装
  • Application PasswordsやJWTなど、用途に応じた認証方式を選ぶ
  • アクセス制御を細かく設定し、権限を適切に管理
  • レート制限やログ記録で、異常なアクセスを検知
  • HTTPSは必須、セキュリティヘッダーも忘れずに

REST APIのセキュリティは、一度設定すれば終わりじゃない。新しいエンドポイントを追加するたび、プラグインをインストールするたび、セキュリティ設定を見直す必要がある。

でも、これらの対策を実施しておけば、REST APIの便利さを安心して享受できる。最初は面倒に感じるかもしれないけど、情報漏洩やハッキングの被害に遭うことを考えたら、この手間は惜しむべきじゃないよね。

REST APIは強力なツールだ。正しく使えば、WordPressの可能性を最大限に引き出せる。セキュリティをしっかり固めて、安全にREST APIを活用していこう。


WordPressのセキュリティ、不安に思っていませんか?

「自分のサイトは大丈夫だろうか…」
「何から手をつければいいか分からない…」

もしあなたが少しでもそう感じているなら、専門家によるセキュリティ診断を受けてみることを強くお勧めします。

>> WordPressセキュリティ無料診断はこちら

上記のサイトでは、WordPressのプロがあなたのサイトの脆弱性を無料で診断してくれます。問題が見つかれば、具体的な対策方法についてもアドバイスをもらえます。手遅れになる前に、一度プロの目でチェックしてもらい、安心を手に入れましょう。

FacebookでシェアTwitterでシェアPinterestでシェア