Application Password در وردپرس برای REST API چیست و چگونه امن پیادهسازی میشود؟
راهنمای Application Password؛ بررسی ساخت، استفاده و لغو. برای اتصال اپلیکیشن کاربرد دارد. اشتباه رایج، نبود HTTPS، نبود لغو و نبود حداقل دسترسی است. تسلط بر آن برای امنیت ضروری است.
Application Password در وردپرس یک مکانیزم احراز هویت اختصاصی برای دسترسیهای برنامهای به REST API (Representational State Transfer Application Programming Interface) است که از نسخه ۵.۶ به هسته وردپرس اضافه شد. این مکانیزم بهجای کوکی نشست، یک رمز عبور مستقل برای هر برنامه تولید میکند که میتواند بهصورت مستقل ابطال شود و برخلاف رمز اصلی کاربر، در فرآیند 2FA (Two-Factor Authentication) قفل نمیشود. Application Password برای سناریوهایی طراحی شده که در آن یک اسکریپت، اپلیکیشن موبایل، ابزار اتوماسیون یا سرویس خارجی نیاز دارد تا با API سایت شما تعامل کند، اما نمیتواند و نباید از رمز اصلی کاربر استفاده کند. پیادهسازی نادرست این مکانیزم میتواند به یک در پشتی دائمی تبدیل شود؛ چون برخلاف نشستهای عادی که با خروج کاربر باطل میشوند، یک Application Password تا زمانی که بهصورت دستی ابطال نشود، معتبر باقی میماند. این متن مسیر عملی پیادهسازی امن Application Password را از ساختار پایه تا مدیریت چرخه عمر، محدودسازی دسترسی و یکپارچهسازی با معماری سازمانی، با تمرکز بر تصمیمهای امنیتی و کد قابل اجرا بررسی میکند.
نخستین باری که روی یک پروژه فروشگاهی وردپرسی کار میکردم و باید یک اسکریپت پایتون بهطور خودکار سفارشها را از REST API ووکامرس میخواند، با یک دوراهی روبهرو شدم: از رمز اصلی ادمین استفاده کنم یا یک مکانیزم اختصاصی بسازم. آن زمان Application Password تازه به وردپرس اضافه شده بود و همان تصمیم، به یکی از پایههای امنیتی همه پروژههای بعدی من تبدیل شد.
Application Password در وردپرس دقیقاً چیست؟
Application Password یا «رمز عبور برنامه» یک مکانیزم احراز هویت است که در وردپرس ۵.۶ به هسته اضافه شد و هدف آن، فراهم کردن یک مسیر امن برای دسترسی برنامهای به REST API است. برخلاف رمز عبور اصلی کاربر، یک Application Password بهصورت مستقل برای هر برنامه تولید میشود و میتواند بهطور جداگانه ابطال شود بدون اینکه رمز اصلی کاربر تحت تأثیر قرار گیرد.
Application Password در سطح فنی، یک رشته ۲۴ کاراکتری است که در قالب چهار بلوک ششکاراکتری با فاصله نمایش داده میشود. فرمت آن بهصورت xxxx xxxx xxxx xxxx xxxx xxxx است و کاراکترهای آن از مجموعه حروف لاتین بزرگ و کوچک و اعداد انتخاب میشوند. این ساختار، هم خوانایی را برای کاربر بهبود میبخشد و هم فضای جستوجو را برای مهاجمان بهطور چشمگیری بزرگ میکند.
نکته کلیدی در درک Application Password این است که هدف آن، جانشینی 2FA یا جایگزینی رمز اصلی نیست. Application Password در کنار سایر مکانیزمها کار میکند و دقیقاً برای سناریوهایی طراحی شده که در آنها تعامل انسانی وجود ندارد. در این سناریوها، نه 2FA قابل پیادهسازی است و نه کوکی نشست قابل استفاده. اگر با مفهوم 2FA و نحوه کار آن آشنا نیستید، مطلب Two-Factor Authentication برای وردپرس پیشنیاز ضروری این بحث است.
مشخصات فنی این مکانیزم در راهنمای رسمی وردپرس با عنوان Application Passwords مستندسازی شده است. ایده اصلی این مکانیزم، الهام گرفته از مفهوم App Password در سرویسهای ابری مانند Google و GitHub است که سالها پیش از وردپرس، این مفهوم را پیادهسازی کرده بودند.
| ویژگی | رمز عبور اصلی | Application Password |
|---|---|---|
| هدف | ورود انسان | دسترسی برنامهای |
| سازگاری با 2FA | نیازمند | مستقل |
| قابلیت ابطال مستقل | خیر | بله |
| تعداد مجاز | یک رمز | چندین مورد |
| انقضای خودکار | خیر | خیر (در حالت پیشفرض) |
Application Password یک رمز دوم برای کاربر نیست؛ یک هویت مستقل برای یک برنامه مشخص است که میتوان آن را در هر لحظه باطل کرد.
چرا وردپرس به Application Password نیاز داشت؟
پیش از نسخه ۵.۶، توسعهدهندگانی که میخواستند به REST API وردپرس از یک برنامه خارجی دسترسی داشته باشند، سه گزینه داشتند که هیچکدام ایدهآل نبود.
گزینه اول: استفاده از رمز اصلی کاربر
در این روش، برنامه خارجی از نام کاربری و رمز اصلی برای احراز هویت استفاده میکرد. این رویکرد سه مشکل جدی داشت. نخست، اگر برنامه هک میشد، رمز اصلی کاربر افشا میشد و مهاجم میتوانست بهطور کامل کنترل حساب را در دست بگیرد. دوم، تغییر رمز اصلی توسط کاربر، همه برنامههای متصل را بهطور همزمان از کار میانداخت. سوم، در سایتهایی که 2FA فعال بود، این روش بهطور کامل کار نمیکرد.
گزینه دوم: ساخت یک کاربر اختصاصی برای هر برنامه
این رویکرد امنیت را افزایش میداد اما پیچیدگی مدیریتی چشمگیری ایجاد میکرد. برای هر سرویس خارجی، یک کاربر جدید با یک رمز مستقل ساخته میشد. اگر تعداد سرویسها به ده عدد میرسید، مدیریت ده کاربر و ده رمز، خود به یک چالش امنیتی تبدیل میشد. علاوه بر این، این کاربران در فهرست کاربران وردپرس دیده میشدند و میتوانستند با نقشهای اشتباه، دسترسیهای ناخواسته به دست آورند.
گزینه سوم: استفاده از توکن OAuth یا JWT سفارشی
این رویکرد نیازمند پیادهسازی یک سیستم OAuth یا JWT کامل بود که پیچیدگی بالایی داشت. برای پروژههای کوچک و متوسط، این هزینه قابل توجیه نبود. همچنین، نگهداری و بهروزرسانی این سیستمها نیازمند تخصص امنیتی بالایی بود که در همه تیمها وجود نداشت.
راهحل وردپرس: Application Password
Application Password یک راهحل میانه ارائه داد که سه مزیت اصلی داشت. نخست، سادگی: کاربر با چند کلیک میتواند یک Application Password جدید بسازد و به برنامه خارجی بدهد. دوم، جداسازی: هر Application Password مستقل است و ابطال یکی، بقیه را تحت تأثیر قرار نمیدهد. سوم، سازگاری با 2FA: از آنجا که Application Password از مسیر 2FA عبور نمیکند، برنامههای خارجی بدون نیاز به تعامل انسانی میتوانند احراز هویت شوند. اگر میخواهید با معماری REST API وردپرس بیشتر آشنا شوید، مطلب REST API در وردپرس راهنمای کامل راهنمای جامعی ارائه میدهد.
تفاوت Application Password با JWT و OAuth
سه مکانیزم اصلی برای احراز هویت برنامهای در وردپرس وجود دارد: Application Password، JWT و OAuth. هر یک از اینها برای سناریو خاصی طراحی شده و انتخاب نادرست میتواند امنیت یا کارایی پروژه را تحت تأثیر قرار دهد.
Application Password در برابر JWT
JWT یا JSON Web Token یک مکانیزم Stateless است که در آن، پس از احراز هویت اولیه، یک توکن امضاشده تولید میشود که اطلاعات کاربر و انقضا را در خود دارد. JWT برای APIهایی که حجم درخواست بالا دارند، کارآمدتر است چون نیاز به Query به دیتابیس در هر درخواست ندارد. اما JWT یک دام جدی دارد: ابطال توکن پیش از انقضا نیازمند یک لیست سیاه (Blacklist) است که خود به یک Query دیتابیس تبدیل میشود. Application Password در مقابل، در هر درخواست از دیتابیس بررسی میشود اما ابطال آن فوری و بدون هزینه اضافی است.
Application Password در برابر OAuth
OAuth یک استاندارد کامل برای واگذاری دسترسی محدود به برنامههای خارجی است. در OAuth، کاربر بدون افشای رمز خود، به برنامه اجازه میدهد با محدوده مشخصی از دسترسیها عمل کند. OAuth برای ادغام با سرویسهای بزرگ مانند Google، GitHub یا فروشگاههای آنلاین مناسب است اما پیادهسازی آن در وردپرس نیازمند زیرساخت سنگین است. Application Password برای سناریوهای سادهتر و درونسازمانی مناسبتر است.
معیار انتخاب
| معیار | Application Password | JWT | OAuth |
|---|---|---|---|
| پیچیدگی پیادهسازی | پایین | متوسط | بالا |
| Stateless بودن | خیر | بله | بستگی دارد |
| ابطال فوری | بله | نیازمند Blacklist | بله |
| مناسب برای | اسکریپتها، ابزار اتوماسیون | SPA، اپلیکیشن موبایل | ادغام با سرویسهای خارجی |
| پشتیبانی پیشفرض وردپرس | بله | خیر | خیر |
اگر با مکانیزم JWT و سناریوهای استفاده از آن آشنا نیستید، مطلب JWT (JSON Web Token) چیست و چه کاربردی در احراز هویت دارد؟ راهنمای جامعی ارائه میدهد. همچنین برای درک عمیقتر OAuth، مطلب OAuth چیست و ورود با گوگل در عمل چطور کار میکند؟ دیدگاه عملی خوبی است.
مکانیزم کار و ساختار فنی
Application Password در سطح هسته وردپرس از چند جزء تشکیل شده است که هر یک نقش مشخصی در تولید، ذخیرهسازی و اعتبارسنجی دارند.
ساختار داده در دیتابیس
Application Password در جدول wp_usermeta ذخیره میشود، اما نه بهصورت متن آشکار. هسته وردپرس دو متادیتای مستقل برای هر Application Password ذخیره میکند. متادیتای اول بهصورت یک آرایه با کلید _application_passwords ذخیره میشود و شامل نام، UUID، زمان ساخت، زمان آخرین استفاده و آخرین IP است. متادیتای دوم بهصورت جداگانه با کلیدهای _application_password_<uuid> ذخیره میشود و حاوی هش رمز است.
// ساختار متادیتای Application Password در wp_usermeta
[
'_application_passwords' => [
[
'uuid' => 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
'name' => 'woocommerce-sync',
'password' => '$wp$2y$10$abcdefghijklmnopqrstuv', // هش
'created' => 1704067200,
'last_used' => 1704153600,
'last_ip' => '203.0.113.10',
],
],
]
نکته مهم در این ساختار، ذخیرهسازی هش است نه رمز خام. وردپرس از تابع wp_hash_password استفاده میکند که امروز بر پایه الگوریتم bcrypt کار میکند. حتی اگر دیتابیس سایت شما بهطور کامل نشت کند، مهاجم نمیتواند رمزهای خام را استخراج کند. برای درک عمیقتر اصول مدیریت رمز عبور، مطلب مدیریت رمز عبور امن چه اصولی دارد؟ راهنمای جامعی ارائه میدهد.
مکانیزم اعتبارسنجی
هنگام دریافت یک درخواست REST API، وردپرس سه مرحله را برای اعتبارسنجی Application Password طی میکند. مرحله اول، استخراج رمز از هدر Authorization با فرمت Basic Authentication. مرحله دوم، یافتن کاربر بر پایه نام کاربری و بررسی همه Application Passwordهای ثبتشده برای آن کاربر. مرحله سوم، مقایسه رمز ارسالی با هشهای ذخیرهشده با استفاده از wp_check_password.
// استخراج و اعتبارسنجی Application Password
function wk_authenticate_application_password( $input_user, $username, $password ) {
if ( ! class_exists( 'WP_Application_Passwords' ) ) {
return $input_user;
}
$user = get_user_by( 'login', $username );
if ( ! $user ) {
return $input_user;
}
$item = WP_Application_Passwords::chunk_password( $password );
$stored = WP_Application_Passwords::get_user_application_passwords( $user->ID );
foreach ( $stored as $app_password ) {
if ( wp_check_password( $item, $app_password['password'], $user->ID ) ) {
// بهروزرسانی زمان و IP آخرین استفاده
WP_Application_Passwords::record_application_password_usage(
$user->ID,
$app_password['uuid']
);
return $user;
}
}
return $input_user;
}
add_filter( 'authenticate', 'wk_authenticate_application_password', 20, 3 );
این کد نمونه نشان میدهد که Application Password از همان مکانیزم authenticate استفاده میکند که رمز عبور معمولی، اما در یک لایه جداگانه. هسته وردپرس این کار را بهطور خودکار انجام میدهد و نیازی به پیادهسازی سفارشی نیست، مگر برای سناریوهای پیشرفته مانند محدودسازی دامنه دسترسی.
هدر Authorization و Basic Auth
Application Password از پروتکل استاندارد HTTP Basic Authentication استفاده میکند. در این پروتکل، نام کاربری و رمز بهصورت base64(username:password) ترکیب و در هدر Authorization ارسال میشوند. نکته مهم این است که Base64 یک رمزنگاری نیست، فقط یک کدگذاری است. بنابراین، استفاده از Application Password تنها در ترکیب با HTTPS امنیت دارد. اگر سایت شما HTTPS ندارد، Application Password در مسیر شبکه قابل استخراج است.
# نمونه درخواست با curl
curl -X GET https://example.com/wp-json/wp/v2/posts \
-u "username:xxxx xxxx xxxx xxxx xxxx xxxx"
# نمایش هدر Authorization در قالب خام
Authorization: Basic dXNlcm5hbWU6eHh4eCB4eHh4IHh4eHgg...
# نمایش معادل code شده
echo -n "username:xxxx xxxx xxxx xxxx xxxx xxxx" | base64
ساخت Application Password در پنل کاربری
Application Password در پنل کاربری وردپرس قابل ساخت است. مسیر دسترسی: پروفایل کاربر ← بخش Application Passwords. این بخش فقط برای کاربران واردشده قابل مشاهده است و نیازی به افزونه اضافی ندارد.
ساخت گامبهگام
برای ساخت یک Application Password جدید، کاربر یک نام توصیفی وارد میکند. سپس وردپرس یک رمز ۲۴ کاراکتری تولید میکند که فقط یک بار نمایش داده میشود. پس از بستن پنجره نمایش، دیگر امکان بازیابی رمز وجود ندارد و در صورت فراموشی، باید Application Password جدیدی ساخته شود.
ساخت با WP-CLI
در سرورهای دسترسیدار، میتوان Application Password را از طریق WP-CLI هم ساخت. این روش در پروژههای اتوماسیون و استقرار، بسیار مفید است.
# ساخت Application Password جدید برای کاربر مشخص
wp user application-password create admin woocommerce-sync --porcelain
# فهرست همه Application Passwordهای یک کاربر
wp user application-password list admin
# حذف یک Application Password بر پایه UUID
wp user application-password delete admin a1b2c3d4-e5f6-7890-abcd-ef1234567890
# بازبینی جزئیات یک Application Password
wp user application-password get admin a1b2c3d4-e5f6-7890-abcd-ef1234567890
ساخت با کد سفارشی
در پروژههای خودکارسازی، میتوان Application Password را با کد سفارشی ساخت. این روش در ابزارهای استقرار خودکار یا در فرآیند آنبوردینگ کاربران مفید است.
function wk_create_app_password( $user_id, $name ) {
if ( ! class_exists( 'WP_Application_Passwords' ) ) {
return new WP_Error( 'missing_core', 'Application Password در دسترس نیست' );
}
$result = WP_Application_Passwords::create_new_application_password(
$user_id,
[ 'name' => sanitize_text_field( $name ) ]
);
if ( is_wp_error( $result ) ) {
return $result;
}
return [
'password' => $result[0],
'uuid' => $result[1]['uuid'],
];
}
// نمونه استفاده
$app_password = wk_create_app_password( 1, 'sync-service' );
if ( ! is_wp_error( $app_password ) ) {
error_log( 'Application Password created: ' . $app_password['uuid'] );
}
استفاده از Application Password در REST API
پس از ساخت Application Password، برنامه خارجی میتواند با ارسال هدر Authorization به REST API وردپرس متصل شود. وردپرس بهطور خودکار این هدر را میخواند و در صورت معتبر بودن، کاربر جاری را در بافت درخواست تنظیم میکند.
نمونه درخواست با curl
# دریافت فهرست نوشتهها
curl -X GET "https://example.com/wp-json/wp/v2/posts" \
-H "Authorization: Basic $(echo -n 'admin:xxxx xxxx xxxx xxxx xxxx xxxx' | base64)"
# ساخت نوشته جدید
curl -X POST "https://example.com/wp-json/wp/v2/posts" \
-H "Authorization: Basic $(echo -n 'admin:xxxx xxxx xxxx xxxx xxxx xxxx' | base64)" \
-H "Content-Type: application/json" \
-d '{"title":"نوشته جدید","content":"محتوا","status":"draft"}'
نمونه درخواست با JavaScript
const username = 'admin';
const appPassword = 'xxxx xxxx xxxx xxxx xxxx xxxx';
const credentials = btoa( `${ username }:${ appPassword }` );
fetch( 'https://example.com/wp-json/wp/v2/posts', {
method: 'POST',
headers: {
'Authorization': `Basic ${ credentials }`,
'Content-Type': 'application/json',
},
body: JSON.stringify( {
title: 'نوشته جدید',
content: 'محتوای نوشته',
status: 'draft',
} ),
} )
.then( response => response.json() )
.then( data => console.log( 'Created:', data.id ) )
.catch( error => console.error( 'Error:', error ) );
نمونه درخواست با Python
import requests
from requests.auth import HTTPBasicAuth
username = 'admin'
app_password = 'xxxx xxxx xxxx xxxx xxxx xxxx'
response = requests.get(
'https://example.com/wp-json/wp/v2/posts',
auth=HTTPBasicAuth( username, app_password ),
timeout=10,
)
if response.status_code == 200:
posts = response.json()
print( f'Found { len( posts ) } posts' )
else:
print( f'Error: { response.status_code } - { response.text }' )
نمونه درخواست با PHP
$username = 'admin';
$app_password = 'xxxx xxxx xxxx xxxx xxxx xxxx';
$response = wp_remote_get(
'https://example.com/wp-json/wp/v2/posts',
[
'headers' => [
'Authorization' => 'Basic ' . base64_encode( $username . ':' . $app_password ),
],
'timeout' => 10,
]
);
if ( is_wp_error( $response ) ) {
error_log( 'Request failed: ' . $response->get_error_message() );
} else {
$posts = json_decode( wp_remote_retrieve_body( $response ), true );
error_log( 'Found ' . count( $posts ) . ' posts' );
}
درخواست با هدر X-WP-Nonce و Application Password
در برخی سناریوها، وردپرس از هدر X-WP-Nonce برای اعتبارسنجی اضافی استفاده میکند. اما Application Password از این مکانیزم مستقل است و نیازی به ارسال Nonce ندارد. اگر با مفهوم Nonce آشنا نیستید، مطلب Nonce در وردپرس چیست و چرا امنیت فرمها بدون آن یک توهم است؟ راهنمای جامعی ارائه میدهد.
استفاده در WP-CLI و اسکریپتهای خودکار
Application Password در ابزارهای اتوماسیون و اسکریپتهای خط فرمان، بسیار کارآمد است. این مکانیزم بهجای وابستگی به جلسه تعاملی کاربر، یک مسیر امن و قابل ابطال برای اسکریپتهای خودکار فراهم میکند.
استفاده در اسکریپتهای استقرار
#!/bin/bash
# نمونه اسکریپت استقرار محتوا
WP_USER="deploy"
WP_APP_PASSWORD="xxxx xxxx xxxx xxxx xxxx xxxx"
WP_URL="https://example.com"
# بارگذاری فایل CSV و ساخت نوشتهها
while IFS=, read -r title content; do
curl -s -X POST "${WP_URL}/wp-json/wp/v2/posts" \
-u "${WP_USER}:${WP_APP_PASSWORD}" \
-H "Content-Type: application/json" \
-d "{\"title\":\"${title}\",\"content\":\"${content}\",\"status\":\"draft\"}"
done < posts.csv
استفاده در اسکریپتهای پایش
# بررسی وضعیت REST API وردپرس
WP_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
-u "monitor:xxxx xxxx xxxx xxxx xxxx xxxx" \
"https://example.com/wp-json/wp/v2/users/me")
if [ "$WP_RESPONSE" = "200" ]; then
echo "WordPress REST API: OK"
else
echo "WordPress REST API: FAILED (HTTP $WP_RESPONSE)"
fi
استفاده در Cron Jobs
# نمونه Cron Job که هر ۱۵ دقیقه سفارشهای جدید را بررسی میکند
*/15 * * * * /usr/bin/python3 /opt/scripts/sync-orders.py >> /var/log/sync.log 2>&1
# محتوای sync-orders.py
# import requests
# from requests.auth import HTTPBasicAuth
#
# response = requests.get(
# 'https://example.com/wp-json/wc/v3/orders',
# params={ 'status': 'processing', 'per_page': 50 },
# auth=HTTPBasicAuth( 'api-user', 'xxxx xxxx xxxx xxxx xxxx xxxx' ),
# timeout=30,
# )
# orders = response.json()
# for order in orders:
# process_order( order )
ابطال، مدیریت و چرخه عمر
مدیریت Application Passwordها یکی از کمتوجهشدهترین بخشهای این مکانیزم است. برخلاف نشستهای عادی که خودکار منقضی میشوند، Application Password تا زمانی که بهصورت دستی ابطال نشود، معتبر باقی میماند.
ابطال از پنل کاربری
در پنل کاربری، هر Application Password با نام و زمان آخرین استفاده نمایش داده میشود. کاربر میتواند با یک کلیک، آن را باطل کند. پس از ابطال، هر درخواستی که با آن رمز ارسال شود، خطای ۴۰۱ دریافت میکند.
ابطال با WP-CLI
# ابطال یک Application Password مشخص
wp user application-password delete admin a1b2c3d4-e5f6-7890-abcd-ef1234567890
# ابطال همه Application Passwordهای یک کاربر
wp user application-password delete admin --all
# فهرست Application Passwordهایی که بیش از ۹۰ روز استفاده نشدهاند
wp user application-password list admin \
--fields=uuid,name,last_used,last_ip \
--format=table
ابطال با کد سفارشی
// ابطال یک Application Password مشخص
function wk_revoke_app_password( $user_id, $uuid ) {
return WP_Application_Passwords::delete_application_password( $user_id, $uuid );
}
// ابطال همه Application Passwordهای یک کاربر
function wk_revoke_all_app_passwords( $user_id ) {
$stored = WP_Application_Passwords::get_user_application_passwords( $user_id );
$count = 0;
foreach ( $stored as $item ) {
if ( WP_Application_Passwords::delete_application_password( $user_id, $item['uuid'] ) ) {
$count++;
}
}
return $count;
}
// ابطال خودکار Application Passwordهای غیرفعال
add_action( 'wk_daily_cleanup', function() {
$users = get_users( [ 'fields' => 'ID' ] );
$now = time();
foreach ( $users as $user_id ) {
$stored = WP_Application_Passwords::get_user_application_passwords( $user_id );
foreach ( $stored as $item ) {
if ( empty( $item['last_used'] ) ) {
continue;
}
$inactive_days = ( $now - $item['last_used'] ) / DAY_IN_SECONDS;
if ( $inactive_days > 90 ) {
WP_Application_Passwords::delete_application_password( $user_id, $item['uuid'] );
error_log( sprintf(
'[WK-APP-PASS] Auto revoked: user=%d uuid=%s inactive=%d days',
$user_id,
$item['uuid'],
$inactive_days
) );
}
}
}
} );
برنامهریزی ابطال خودکار با WP-Cron
// ثبت رویداد Cron روزانه
add_action( 'init', function() {
if ( ! wp_next_scheduled( 'wk_daily_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'wk_daily_cleanup' );
}
} );
// ثبت در زمان فعالسازی افزونه
register_activation_hook( __FILE__, function() {
if ( ! wp_next_scheduled( 'wk_daily_cleanup' ) ) {
wp_schedule_event( time(), 'daily', 'wk_daily_cleanup' );
}
} );
// حذف رویداد در زمان غیرفعالسازی
register_deactivation_hook( __FILE__, function() {
wp_clear_scheduled_hook( 'wk_daily_cleanup' );
} );
اگر با مکانیزم WP-Cron و مدیریت وظایف زمانبندیشده در وردپرس آشنا نیستید، مطلب چرا کرون وردپرس کار نمیکند و چگونه مشکلات زمانبندی خودکار را رفع کنیم؟ راهنمای عملی خوبی است.
محافظت از Endpointهای سفارشی
در پروژههای واقعی، اغلب نیاز است که Endpointهای سفارشی REST API ساخته شود که فقط با Application Password قابل دسترسی باشند. پیادهسازی این Endpointها نیازمند دقت در لایه permission_callback است.
ساخت Endpoint سفارشی با Application Password
add_action( 'rest_api_init', function() {
register_rest_route( 'wk/v1', '/orders/(?P\d+)', [
'methods' => 'GET',
'callback' => 'wk_get_order_callback',
'permission_callback' => 'wk_check_app_password_permission',
'args' => [
'id' => [
'validate_callback' => function( $value ) {
return is_numeric( $value ) && $value > 0;
},
'sanitize_callback' => 'absint',
],
],
] );
} );
function wk_check_app_password_permission( WP_REST_Request $request ) {
if ( ! is_user_logged_in() ) {
return new WP_Error(
'rest_forbidden',
'دسترسی نیازمند احراز هویت است',
[ 'status' => 401 ]
);
}
if ( ! current_user_can( 'read_orders' ) ) {
return new WP_Error(
'rest_forbidden',
'شما مجاز به دسترسی به این منبع نیستید',
[ 'status' => 403 ]
);
}
return true;
}
function wk_get_order_callback( WP_REST_Request $request ) {
$order_id = absint( $request['id'] );
$order = wc_get_order( $order_id );
if ( ! $order ) {
return new WP_Error(
'order_not_found',
'سفارش مورد نظر یافت نشد',
[ 'status' => 404 ]
);
}
return rest_ensure_response( [
'id' => $order->get_id(),
'status' => $order->get_status(),
'total' => $order->get_total(),
] );
}
محدودسازی بر پایه نام Application Password
در پروژههای سازمانی، ممکن است نیاز باشد که محدودسازی دقیقتری انجام شود: مثلاً یک Application Password مشخص فقط به بخش خاصی از API دسترسی داشته باشد. برای این کار، باید نام Application Password را در بافت درخواست استخراج کرد.
function wk_get_app_password_name() {
if ( ! is_user_logged_in() ) {
return null;
}
$user_id = get_current_user_id();
$stored = WP_Application_Passwords::get_user_application_passwords( $user_id );
// استخراج رمز از هدر Authorization
$header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if ( ! preg_match( '/^Basic\s+(.+)$/i', $header, $matches ) ) {
return null;
}
$decoded = base64_decode( $matches[1] );
$parts = explode( ':', $decoded, 2 );
if ( count( $parts ) < 2 ) {
return null;
}
$raw_password = WP_Application_Passwords::chunk_password( $parts[1] );
foreach ( $stored as $item ) {
if ( wp_check_password( $raw_password, $item['password'], $user_id ) ) {
return $item['name'];
}
}
return null;
}
محدودسازی دامنه دسترسی Application Password
در وردپرس هسته، Application Password به همه Capabilityهای کاربر متصل است. این یعنی یک Application Password که برای کاربر Administrator ساخته شده، به همه قابلیتهای مدیریتی دسترسی دارد. در پروژههای سازمانی، این سطح دسترسی معمولاً بیش از حد است.
الگوی محدودسازی بر پایه نام Application Password
add_filter( 'user_has_cap', function( $allcaps, $caps, $args, $user ) {
if ( ! is_user_logged_in() || ! wp_doing_rest() ) {
return $allcaps;
}
$app_password_name = wk_get_app_password_name();
if ( ! $app_password_name ) {
return $allcaps;
}
// محدودسازی Application Password با نام مشخص
$restrictions = [
'sync-orders' => [
'deny_caps' => [ 'edit_users', 'delete_users', 'promote_users' ],
],
'reporting' => [
'deny_caps' => [ 'edit_posts', 'delete_posts', 'publish_posts' ],
],
];
if ( ! isset( $restrictions[ $app_password_name ] ) ) {
return $allcaps;
}
foreach ( $caps as $cap ) {
if ( in_array( $cap, $restrictions[ $app_password_name ]['deny_caps'], true ) ) {
$allcaps[ $cap ] = false;
}
}
return $allcaps;
}, 10, 4 );
الگوی محدودسازی بر پایه UUID
استفاده از نام Application Password برای محدودسازی، یک دام دارد: نام قابل تغییر است. الگوی دقیقتر، استفاده از UUID است که پس از ساخت تغییر نمیکند.
function wk_get_app_password_uuid() {
static $cached_uuid = null;
if ( null !== $cached_uuid ) {
return $cached_uuid;
}
if ( ! is_user_logged_in() ) {
return null;
}
$user_id = get_current_user_id();
$stored = WP_Application_Passwords::get_user_application_passwords( $user_id );
$header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if ( ! preg_match( '/^Basic\s+(.+)$/i', $header, $matches ) ) {
return null;
}
$decoded = base64_decode( $matches[1] );
$parts = explode( ':', $decoded, 2 );
if ( count( $parts ) < 2 ) {
return null;
}
$raw_password = WP_Application_Passwords::chunk_password( $parts[1] );
foreach ( $stored as $item ) {
if ( wp_check_password( $raw_password, $item['password'], $user_id ) ) {
$cached_uuid = $item['uuid'];
return $cached_uuid;
}
}
return null;
}
ملاحظات امنیتی و دامهای پنهان
Application Password در ظاهر ساده است اما در عمل، چند دام امنیتی پنهان دارد که در پروژههای واقعی بارها مشاهده شدهاند.
دام اول: نبود انقضای پیشفرض
Application Password بهطور پیشفرض تاریخ انقضا ندارد. اگر یک رمز ساخته شود و فراموش شود، میتواند سالها در دیتابیس باقی بماند. این رفتار، کاملاً با اصول امنیتی مدرن که بر پایه چرخه عمر و بازبینی دورهای بنا شده، در تضاد است. راهحل عملی، پیادهسازی یک مکانیزم ابطال خودکار بر پایه مدت عدم استفاده است که در بخش قبلی توضیح داده شد.
دام دوم: نبود محدودیت IP
Application Password بهطور پیشفرض هر IP را میپذیرد. اگر رمز افشا شود، مهاجم از هر نقطهای در جهان میتواند از آن استفاده کند. راهحل، پیادهسازی محدودیت IP در یک فیلتر authenticate سفارشی است.
add_filter( 'authenticate', function( $user, $username, $password ) {
if ( is_wp_error( $user ) || ! $user instanceof WP_User ) {
return $user;
}
if ( empty( $_SERVER['HTTP_AUTHORIZATION'] ) ) {
return $user;
}
$allowed_ips = get_user_meta( $user->ID, 'wk_app_password_ips', true );
if ( empty( $allowed_ips ) ) {
return $user;
}
$current_ip = $_SERVER['REMOTE_ADDR'] ?? '';
if ( ! in_array( $current_ip, (array) $allowed_ips, true ) ) {
return new WP_Error(
'app_password_ip_denied',
'دسترسی از این IP مجاز نیست'
);
}
return $user;
}, 30, 3 );
دام سوم: افشای رمز در URL و لاگ
هرگز Application Password را در URL قرار ندهید. برخی ابزارها امکان ارسال Basic Auth در URL را فراهم میکنند (مانند https://user:pass@example.com) که این کار، رمز را در لاگ سرور، تاریخچه مرورگر و Referer Header ذخیره میکند. همیشه از هدر Authorization استفاده کنید.
دام چهارم: استفاده بدون HTTPS
Application Password در برابر شنود شبکه آسیبپذیر است اگر سایت روی HTTP اجرا شود. همیشه HTTPS را برای کل سایت فعال کنید و از ریدایرکت اجباری HTTP به HTTPS مطمئن شوید. اگر با تنظیم HTTPS در وردپرس آشنایی ندارید، مطلب چگونه SSL سایت را نصب و فعال کنیم؟ راهنمای عملی خوبی است.
دام پنجم: افشا در مخازن کد
یکی از شایعترین اشتباهات، ذخیره Application Password در مخازن عمومی Git است. حتی اگر بعداً فایل حذف شود، تاریخچه Git آن را نگه میدارد. همیشه از متغیرهای محیطی یا سیستمهای مدیریت Secrets مانند HashiCorp Vault، AWS Secrets Manager یا GitHub Secrets استفاده کنید.
# نمونه درست: استفاده از متغیر محیطی
export WP_APP_USER='sync-user'
export WP_APP_PASSWORD='xxxx xxxx xxxx xxxx xxxx xxxx'
curl -u "${WP_APP_USER}:${WP_APP_PASSWORD}" \
"https://example.com/wp-json/wp/v2/posts"
# نمونه نادرست: ذخیره در فایل کد
# const WP_PASSWORD = 'xxxx xxxx xxxx xxxx xxxx xxxx'; // هرگز
دام ششم: نبود لاگگیری از استفاده
وردپرس بهطور پیشفرض زمان و IP آخرین استفاده هر Application Password را ثبت میکند، اما این داده کافی نیست. در پروژههای سازمانی، نیاز به لاگ کامل هر درخواست است که شامل مسیر، متد، وضعیت پاسخ و جزئیات درخواست باشد.
Application Password یک در پشتی نیست اگر با دقت مدیریت شود؛ اما بدون چرخه عمر مشخص، به همان اندازه خطرناک میشود.
لاگگیری و پایش
پایش استفاده از Application Password یک بخش حیاتی از معماری امنیتی است که در پروژههای واقعی بارها نادیده گرفته میشود.
لاگگیری در سطح REST API
add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) {
if ( ! is_user_logged_in() ) {
return $result;
}
$uuid = wk_get_app_password_uuid();
if ( ! $uuid ) {
return $result;
}
$log_entry = [
'ts' => current_time( 'mysql', true ),
'user_id' => get_current_user_id(),
'uuid' => $uuid,
'route' => $request->get_route(),
'method' => $request->get_method(),
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
'user_agent' => substr( $_SERVER['HTTP_USER_AGENT'] ?? '', 0, 200 ),
];
error_log( '[WK-APP-PASS] ' . wp_json_encode( $log_entry ) );
return $result;
}, 10, 3 );
ارسال لاگ به سیستم متمرکز
در پروژههای سازمانی، لاگها باید به یک سیستم متمرکز مانند ELK، Splunk یا Datadog ارسال شوند. این کار امکان تحلیل سریع و همبستگی با لاگهای دیگر لایهها را فراهم میکند.
function wk_send_to_logstash( $entry ) {
$payload = wp_json_encode( [
'@timestamp' => gmdate( 'c' ),
'source' => 'wordpress-app-password',
'host' => gethostname(),
'message' => $entry,
] );
wp_remote_post( 'http://logstash.internal:8080/', [
'headers' => [ 'Content-Type' => 'application/json' ],
'body' => $payload,
'timeout' => 2,
'blocking' => false,
] );
}
هشدار بر پایه آستانه
function wk_check_app_password_rate( $user_id, $uuid ) {
$key = 'wk_app_password_rate_' . $uuid;
$count = (int) get_transient( $key );
if ( $count > 1000 ) {
wp_mail(
get_option( 'admin_email' ),
'هشدار: نرخ بالای درخواست Application Password',
sprintf(
'Application Password با UUID %s بیش از ۱۰۰۰ درخواست در ساعت داشته است.',
$uuid
)
);
}
set_transient( $key, $count + 1, HOUR_IN_SECONDS );
}
اشتباهات رایج در استفاده
در بررسی پروژههای متعدد، الگوهای تکرارشونده زیر پرتکرارترین خطاها در استفاده از Application Password بودهاند.
- ذخیره Application Password در مخازن کد یا فایلهای پیکربندی بدون رمزنگاری.
- استفاده از یک Application Password مشترک برای چند برنامه.
- عدم بازبینی دورهای فهرست Application Passwordها و حذف موارد غیرفعال.
- استفاده از Application Password روی HTTP بهجای HTTPS.
- ارسال رمز در URL بهجای هدر Authorization.
- نبود محدودیت IP برای Application Passwordهای حساس.
- ساخت Application Password برای کاربران Administrator برای کارهای ساده.
- نادیده گرفتن لاگگیری و از دست دادن دید روی رخدادهای امنیتی.
- نبود برنامه ابطال خودکار برای Application Passwordهای غیرفعال.
- استفاده از Application Password بهجای OAuth در ادغام با سرویسهای خارجی بزرگ.
- نبود تست دورهای اعتبار و کارکرد Application Passwordها.
- اعتماد به Application Password بهعنوان جایگزین 2FA بهجای مکمل آن.
هر یک از این خطاها میتواند Application Password را از یک مکانیزم امن به یک بردار حمله تبدیل کند. برای مرور جامع خطاهای امنیتی مرتبط، مطلب اشتباهات رایج در احراز هویت (Authentication) کاربران کدامند؟ راهنمای عملی خوبی است.
پرسشهای پرتکرار درباره Application Password
در ادامه به پرسشهایی پاسخ میدهیم که بیشترین جستجو و بازخورد کاربران در ارتباط با Application Password داشتهاند.
Application Password با 2FA تعامل دارد؟
Application Password از مسیر 2FA عبور نمیکند و به همین دلیل برای برنامههای خودکار مناسب است. اما این به معنای نادیده گرفتن امنیت نیست. توصیه میشود 2FA برای ورود انسان فعال باشد و Application Password برای دسترسی برنامهای استفاده شود. در واقع، این دو مکانیزم مکمل یکدیگرند، نه جایگزین. اگر با اصول 2FA آشنا نیستید، مطلب Two-Factor Authentication برای وردپرس راهنمای جامعی ارائه میدهد.
آیا Application Password منقضی میشود؟
در هسته وردپرس، Application Password تاریخ انقضا ندارد. این یک تصمیم طراحی است که در نسخههای اخیر مورد بحث قرار گرفته اما هنوز پیادهسازی نشده است. راهحل عملی، پیادهسازی ابطال خودکار بر پایه مدت عدم استفاده در کد سفارشی است. بازه توصیهشده برای بازبینی، ۹۰ روز است.
چند Application Password میتوان برای یک کاربر ساخت؟
هسته وردپرس محدودیتی برای تعداد Application Passwordها تعیین نکرده است. اما از منظر عملیاتی، تعداد بالای Application Passwordها مدیریت را دشوار میکند. توصیه میشود برای هر برنامه، یک Application Password مجزا ساخته شود و در بازههای منظم، فهرست آنها بازبینی گردد.
آیا میتوان Application Password را به یک IP مشخص محدود کرد؟
در هسته وردپرس، این قابلیت وجود ندارد. اما با استفاده از فیلتر authenticate میتوان یک لایه سفارشی اضافه کرد که IP درخواست را با فهرست مجاز مقایسه کند. این کار در پروژههای سازمانی که Application Passwordها از IPهای ثابت استفاده میکنند، توصیه میشود.
آیا Application Password در برابر فیشینگ مقاوم است؟
Application Password برای تعامل برنامهای طراحی شده و در فرآیند فیشینگ انسانی قرار نمیگیرد. اما اگر رمز Application Password بهطور مستقیم افشا شود — مثلاً از طریق یک مخزن عمومی یا یک فایل پیکربندی نادرست — مهاجم میتواند از آن استفاده کند. بنابراین، محافظت اصلی، مدیریت صحیح رمز است، نه مقاومت در برابر فیشینگ.
تفاوت Application Password با رمز عبور عادی چیست؟
رمز عبور عادی برای ورود انسان طراحی شده و در برابر 2FA قرار میگیرد. Application Password برای دسترسی برنامهای طراحی شده و از 2FA عبور میکند. همچنین، Application Password قابل ابطال مستقل است در حالی که رمز عبور عادی، یک رمز واحد برای همه کاربردها است.
آیا Application Password در Multisite کار میکند؟
Application Password در Multisite پشتیبانی میشود اما رفتار آن به بافت سایت جاری گره خورده است. اگر یک Application Password برای کاربری در سایت A ساخته شود، در سایت B معتبر نیست مگر اینکه کاربر در آن سایت هم عضو باشد. این رفتار، امنیت را افزایش میدهد اما در طراحی افزونههای شبکهای باید در نظر گرفته شود.
آیا میتوان Application Password را در افزونه سفارشی محدود کرد؟
بله، با استفاده از فیلتر user_has_cap و استخراج UUID Application Password جاری از هدر Authorization، میتوان قابلیتهای کاربر را بر پایه Application Password محدود کرد. این الگو در پروژههای سازمانی که هر Application Password به بخش مشخصی از API دسترسی دارد، بسیار مفید است.
آیا Application Password در REST API ووکامرس کار میکند؟
بله، Application Password در همه Endpointهای REST API وردپرس از جمله ووکامرس کار میکند. فقط باید مطمئن شوید که کاربر مربوطه به Capabilityهای لازم برای دسترسی به منابع ووکامرس مجهز است. برای درک عمیقتر REST API ووکامرس، مطلب اتصال ووکامرس به سرویسهای خارجی با API راهنمای خوبی است.
چگونه Application Password را در محیط لوکال تست کنم؟
Application Password در محیط لوکال کار میکند اما دو نکته باید رعایت شود. نخست، برخی سرورهای لوکال مانند XAMPP یا WAMP هدر Authorization را بهطور پیشفرض حذف میکنند. باید با یک Directive در فایل .htaccess یا پیکربندی سرور، این هدر را فعال کنید. دوم، در محیط لوکال، HTTPS معمولاً غیرفعال است که برای تست کافی است اما در تولید باید HTTPS اجباری باشد.
# فعالسازی هدر Authorization در Apache
RewriteEngine On
RewriteCond %{HTTP:Authorization} ^(.*)$
RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]
# در Nginx
location / {
fastcgi_pass_request_headers on;
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
}
نگاهی در سطح معماری سازمانی
در سطح مهندسی ارشد، Application Password را باید بهعنوان یکی از اجزای یک معماری هویت سازمانی در نظر گرفت، نه یک قابلیت مستقل. این معماری سه محدودیت ساختاری در بافت وردپرس دارد که باید صادقانه پذیرفته شوند.
محدودیت اول، نبود Scoped Permission در Application Password است. برخلاف OAuth که اجازه میدهد محدوده دسترسی هر برنامه مشخص شود، Application Password از همه Capabilityهای کاربر ارث میبرد. این یعنی یک Application Password برای Administrator، به همه قابلیتهای مدیریتی دسترسی دارد. راهحل، پیادهسازی یک لایه Scope سفارشی بر پایه UUID است که در بخشهای قبلی توضیح داده شد. این رویکرد، الگویی مشابه Scoped Token در OAuth پیادهسازی میکند.
محدودیت دوم، نبود چرخه عمر خودکار است. Application Password تا زمان ابطال دستی معتبر میماند و این با اصول مدیریت هویت مدرن که بر پایه Rotation و Expiration بنا شده، در تضاد است. راهحل توصیهشده، پیادهسازی یک مکانیزم Rotation دورهای است که در آن هر Application Password پس از مدت مشخص — مثلاً ۹۰ روز — ابطال و یک رمز جدید ساخته میشود. این فرآیند میتواند بهصورت خودکار انجام شود و در صورت نیاز، از طریق Webhook به برنامه خارجی اطلاع داده شود.
محدودیت سوم، نبود قابلیت Pause موقت است. در برخی سناریوها، نیاز است که دسترسی یک برنامه بهطور موقت متوقف شود — مثلاً در زمان نگهداری سایت یا در زمان تحقیق یک حادثه امنیتی. Application Password چنین قابلیتی ندارد و ابطال آن، بازگردانی را دشوار میکند. راهحل، پیادهسازی یک فیلتر وضعیت در لایه authenticate است که اجازه میدهد Application Password بهطور موقت غیرفعال شود، بدون حذف آن.
add_filter( 'authenticate', function( $user, $username, $password ) {
if ( is_wp_error( $user ) || ! $user instanceof WP_User ) {
return $user;
}
$uuid = wk_get_app_password_uuid();
if ( ! $uuid ) {
return $user;
}
$paused = get_option( 'wk_paused_app_passwords', [] );
if ( in_array( $uuid, (array) $paused, true ) ) {
return new WP_Error(
'app_password_paused',
'این Application Password بهطور موقت غیرفعال شده است'
);
}
return $user;
}, 40, 3 );
در سطح پیادهسازی پیشرفته، توصیه میشود Application Password را بهعنوان یک ماژول مستقل از لایه احراز هویت طراحی کنید که سه جزء دارد: لایه تولید و توزیع (Creation & Provisioning)، لایه اعتبارسنجی و محدودسازی (Validation & Restriction) و لایه چرخه عمر (Lifecycle Management). این تفکیک، آزمونپذیری را بالا میبرد و امکان جایگزینی هر جزء را بدون بازنویسی سیستم فراهم میکند. برای تکمیل این تصویر، مطلب Zero Trust برای وردپرس چرا آینده امنیت است؟ چارچوب کامل این رویکرد را توضیح میدهد.
اگر پروژه شما در سطح سازمانی است و چند سرویس مختلف به وردپرس متصل میشوند، توصیه میشود از یک لایه API Gateway استفاده کنید که همه درخواستها از آن عبور کنند. در این معماری، وردپرس فقط با Gateway صحبت میکند و Gateway مسئول مدیریت Application Passwordها، Rate Limiting و Audit است. این رویکرد، مقیاسپذیری و امنیت را چند برابر میکند. برای درک عمیقتر این معماری، مطلب معماری وب چیست؟ چارچوب کاملی ارائه میدهد. همچنین اگر با اصول امنیت REST API آشنایی ندارید، مطلب چگونه REST API امن بسازیم؟ راهنمای عملی خوبی است.
Application Password یک راهحل میانی هوشمندانه است که نه به پیچیدگی OAuth دچار میشود و نه به ضعف رمز مشترک؛ اما تنها در ترکیب با مدیریت چرخه عمر، محدودسازی دامنه و پایش مداوم به یک راهحل سازمانی تبدیل میشود.
بستن این مسیر
Application Password در وردپرس یک مکانیزم ساده در ظاهر و پیچیده در پیادهسازی است. برای توسعهدهندهای که بهتازگی با REST API وردپرس آشنا میشود، ساخت یک Application Password با چند کلیک کافی به نظر میرسد. اما برای توسعهدهندهای که پروژههای سازمانی را میسازد، Application Password بخشی از یک معماری امنیتی چندلایه است که در آن هر جزء نقش مشخصی ایفا میکند. مهمترین درس این مسیر ساده است: هر Application Password باید یک هدف مشخص داشته باشد، به یک برنامه مشخص تعلق داشته باشد، در بازههای منظم بازبینی شود و در صورت عدم استفاده، ابطال گردد. اگر امروز تنها یک گام بردارید، بگذارید آن گام بازبینی فهرست Application Passwordهای سایت و حذف موارد غیرفعال یا ناشناس باشد؛ چون این کوچکترین گام، بزرگترین اثر را در جلوگیری از نفوذ از طریق مسیرهای فراموششده دارد. 🔑
اگر Application Password را در یک پروژه واقعی پیادهسازی کردهاید، برایم جالب است بدانم کدام بخش بیشترین چالش را ایجاد کرد: محدودسازی دامنه دسترسی، پیادهسازی چرخه عمر خودکار، یا یکپارچهسازی با سیستم مدیریت Secrets. تجربه خودتان را در دیدگاهها بنویسید؛ بهخصوص اگر راهکار جایگزینی پیدا کردهاید که میتواند برای خواننده بعدی مفید باشد.