پرش به محتوا
هاست سی‌پنل

رفع خطای دسترسی وردپرس با اسکریپت PHP در هاست

۱۳ دقیقه مطالعه به‌روزرسانی

فایل ابزار را در محل نصب وردپرس قرار دهید، آدرس آن را باز کنید و گزارش را ببینید. ابزار با حساب مدیر وردپرس کار می‌کند و پس از تأیید، مجوزهای پیشنهادی را مرحله‌به‌مرحله اصلاح می‌کند.

درخت پوشه‌های وردپرس با مجوزهای ۷۵۵ و ۶۴۴ و پوشه‌ای با مجوز ناامن ۷۷۷ که با یک اسکریپت PHP اصلاح می‌شود.

وردپرس موقع آپلود تصویر نمی‌تواند پوشه بسازد؟ نصب افزونه در مرحله کپی فایل‌ها متوقف می‌شود؟ اگر دلیل این خطاها مجوز نامناسب فایل‌ها و پوشه‌ها باشد، می‌توان آن‌ها را بررسی و اصلاح کرد؛ بدون اینکه تک‌تک پوشه‌های هاست را باز کنید و عدد دسترسی را تغییر دهید.

در این آموزش، ابزار بررسی دسترسی وردپرس را در یک فایل PHP قرار می‌دهیم و از طریق دامنه باز می‌کنیم. نیازی به ساخت کلید، اجرای دستور در کنسول مرورگر یا وارد کردن اطلاعات پایگاه داده نیست. ابزار از ورود مدیر وردپرس استفاده می‌کند، گزارش می‌دهد و تنها پس از تأیید شما تغییرات را اعمال می‌کند.

البته رفع خطای دسترسی وردپرس همیشه با تغییر یک عدد تمام نمی‌شود. اگر مشکل از مالکیت فایل، محدودیت هاست یا تنظیمات وب‌سرور باشد، باید همان علت بررسی شود. این ابزار کمک می‌کند تفاوت‌ها را ببینید و موارد قابل‌اصلاح را از موارد نیازمند بررسی جدا کنید.

راه‌اندازی سریع؛ یک فایل و چند کلیک

برای شروع این مراحل کافی است:

  1. در File Manager هاست، کنار فایل wp-load.php یک فایل با نام wp-permissions-repair.php بسازید.
  2. تمام کد بخش بعد را داخل آن قرار دهید و ذخیره کنید؛ تنظیم دستی داخل کد لازم نیست.
  3. آدرس فایل را از طریق دامنه باز کنید و دکمه «شروع بررسی» را بزنید.

اگر وردپرس مستقیم داخل public_html نصب شده باشد، آدرس ابزار به این شکل است:

https://example.com/wp-permissions-repair.php

نام دامنه خودتان را جایگزین کنید. اگر وردپرس در پوشه blog نصب شده و همان مسیر از وب در دسترس است، آدرس فایل هم شامل /blog/ خواهد بود. فایل باید واقعاً پسوند .php داشته باشد؛ ذخیره کردن آن با پسوند .php.txt باعث اجرای ابزار نمی‌شود.

اگر قبلاً با حساب مدیر وارد سایت شده‌اید، صفحه ابزار باز می‌شود. در غیر این صورت به صفحه ورود وردپرس هدایت می‌شوید و پس از ورود به ابزار برمی‌گردید. تنها حساب دارای مجوز مدیریت تنظیمات و به‌روزرسانی هسته اجازه استفاده دارد؛ در وردپرس شبکه، دسترسی مدیر شبکه هم لازم است.

این ورود همان ورود معمول سایت است. رمز جداگانه‌ای برای ابزار تعریف نمی‌کنید و اطلاعات حساب را در کد قرار نمی‌دهید. بازدیدکننده عادی هم با دانستن آدرس فایل نمی‌تواند گزارش را ببیند یا مجوزها را تغییر دهد.

خطای Permission چه نشانه‌هایی دارد؟

پیام‌هایی مثل «Unable to create directory»، «Could not create directory» یا «Permission denied» می‌توانند به محدودیت دسترسی مربوط باشند. گاهی هم به‌روزرسانی در مرحله جایگزینی فایل‌ها متوقف می‌شود یا تصویرهای منتقل‌شده باز نمی‌شوند.

اما متن خطا را کامل بخوانید. پر شدن فضای حساب، تمام شدن سهمیه تعداد فایل‌ها یا اشتباه بودن مسیر آپلود هم می‌تواند نتیجه مشابهی داشته باشد. قبل از اجرای ابزار، نام مسیر مشکل‌دار و عملی را که انجام نمی‌شود یادداشت کنید؛ این اطلاعات بعداً برای مقایسه نتیجه لازم است.

اگر فقط بارگذاری تصویر مشکل دارد، محدوده «فقط رسانه‌ها» انتخاب مناسبی برای شروع است. برای بررسی عمومی، «مسیرهای استاندارد وردپرس» را انتخاب کنید. هرچه محدوده به مسئله نزدیک‌تر باشد، خواندن گزارش و ارزیابی نتیجه راحت‌تر می‌شود.

ابزار چه چیزهایی را بررسی می‌کند؟

بررسی به چند درخواست کوتاه تقسیم می‌شود تا لازم نباشد همه مسیرها در یک اجرای طولانی پردازش شوند. صفحه تعداد مسیرهای بررسی‌شده، موارد پیشنهادی و مشکلات نیازمند بررسی دستی را نشان می‌دهد. هنگام کار، صفحه را باز نگه دارید.

برای مسیرهای قابل بررسی، نوع فایل، عدد مجوز، شناسه مالک و گروه و دسترسی قابل مشاهده برای PHP بررسی می‌شود. در صورت وجود امکان خواندن شناسه کاربر اجرای PHP، مالک فایل با آن مقایسه می‌شود. اختلاف این دو الزاماً خطا نیست؛ ممکن است بخشی از طراحی درست هاست باشد، بنابراین ابزار آن را کورکورانه تغییر نمی‌دهد.

بررسی قبل از اعمال تکرار می‌شود. اگر مجوز، مالکیت یا شناسه فایل نسبت به گزارش عوض شده باشد، عملیات روی آن مسیر متوقف می‌شود. این کنترل احتمال تغییر ناخواسته را کمتر می‌کند، ولی عملیات فایل‌سیستم تراکنش کامل نیست؛ هنگام اجرا، به‌روزرسانی و جابه‌جایی فایل‌ها را متوقف کنید.

گزارش محیط نیز وضعیت قابل خواندن و نوشتن بودن چند مسیر مهم را نشان می‌دهد. عدد فضای آزاد، فضای فایل‌سیستم است؛ آن را با سهمیه واقعی حساب هاست یا تعداد inode باقی‌مانده اشتباه نگیرید. ابزار از داخل PHP به همه محدودیت‌های اختصاصی میزبان دسترسی ندارد.

گزارش ابزار را چطور بخوانیم؟

در گزارش، هر مسیر یک وضعیت و توضیح دارد. دانستن تفاوت این وضعیت‌ها کمک می‌کند پیام «بررسی تمام شد» را با «همه مشکلات سایت حل شد» اشتباه نگیرید.

وضعیتمعنی
همخوان با الگوعدد مجوز با الگوی بررسی برابر است؛ سلامت محتوا یا کل سایت تأیید نشده است.
پیشنهاد اصلاحمجوز با الگو تفاوت دارد و می‌تواند پس از تأیید شما تغییر کند.
بررسی دستیمثلاً مالکیت متفاوت یا مسیر غیرمعمول وجود دارد و تغییر خودکار مناسب نیست.
دسترسی محدودعدد مجوز همخوان است، اما خواندن یا نوشتن برای PHP محدود به نظر می‌رسد.
مستثنامسیر به دلیل حساسیت یا ساختار خاص، عمداً از عملیات کنار گذاشته شده است.
اصلاح تأیید شدتغییر انجام شده و عدد مجوز دوباره خوانده شده است.
خطا یا نتیجه نامشخصادامه خودکار متوقف شده و بررسی تازه یا کمک پشتیبانی لازم است.

گزارش کامل را می‌توانید دریافت کنید. این فایل شامل مسیرها، وضعیت‌ها و مقادیر قبل و بعد است؛ رمز وردپرس یا محتوای فایل‌های تنظیمات در آن قرار نمی‌گیرد. بااین‌حال، نام مسیرهای داخلی سایت اطلاعات عمومی نیست؛ گزارش را روی دستگاه خود نگه دارید.

تتفاوت مجوز فایل با مالکیت آن در یک حساب میزبانی

چرا ۷۵۵ برای پوشه و ۶۴۴ برای فایل؟

در یک الگوی رایج لینوکسی، پوشه معمول روی 755 و فایل معمول روی 644 قرار می‌گیرد. در این حالت مالک اجازه نوشتن دارد و دسترسی گروه و سایر کاربران سیستم محدودتر است. این عددها به کاربران سیستم‌عامل مربوط‌اند؛ به‌تنهایی مشخص نمی‌کنند بازدیدکننده اینترنت چه چیزی را ببیند.

این الگو برای همه هاست‌ها مناسب نیست. سروری که از گروه مشترک، ACL یا محدودیت‌های اختصاصی استفاده می‌کند ممکن است به تنظیم دیگری نیاز داشته باشد. تفاوت یک مسیر با 755/644 به معنی اثبات خرابی آن نیست. اگر مقدار درست را نمی‌دانید، قبل از اصلاح از میزبان بپرسید.

همچنین chmod مالک فایل را عوض نمی‌کند. ممکن است پوشه روی ۷۵۵ باشد، اما PHP با کاربری اجرا شود که اجازه نوشتن در آن ندارد. در این حالت باید مالکیت یا گروه بررسی شود؛ باز کردن دسترسی تا ۷۷۷ راه‌حل عمومی مناسبی نیست.

قبل از زدن دکمه اصلاح

از سایت بکاپ معتبر داشته باشید و گزارش اولیه را دریافت کنید. اگر بازگشت دقیق Permissionها برایتان مهم است، از میزبان بپرسید نسخه پشتیبان آن‌ها را هم حفظ می‌کند یا نه. هر آرشیو ZIP لزوماً امکان بازیابی دقیق مجوزها را فراهم نمی‌کند.

فهرست پیشنهادی را بخوانید، مناسب بودن الگوی ۷۵۵/۶۴۴ را تأیید کنید و سپس «اصلاح موارد پیشنهادی» را بزنید. اصلاح مرحله‌ای انجام می‌شود؛ لازم نیست برای هر پوشه تصمیم جداگانه بگیرید. ابزار با اولین خطای اصلاح متوقف می‌شود تا مشکل زیر انبوه پیام‌های موفقیت پنهان نشود.

این ابزار موتور تشخیص مبتنی بر قواعد فنی دارد. مدل هوش مصنوعی یا اسکنر بدافزار نیست و نمی‌تواند علت همه خطاهای هاست را با قطعیت پیدا کند. ارزش گزارش در مشخص کردن شواهد و قدم بعدی است، نه دادن اطمینان بی‌دلیل درباره سلامت سایت.

کد اسکریپت رفع خطای دسترسی وردپرس

کد کامل زیر را در فایل قرار دهید. ابزار به PHP 8.1 یا بالاتر روی لینوکس، HTTPS، نشست PHP و امکان بارگذاری وردپرس نیاز دارد. برای استفاده لازم نیست کلید بسازید یا بخشی از کد را تغییر دهید.

<?php
/** Permission Inspector 2.0 — copyright attribution is checked, not DRM. */
declare(strict_types=1);
const WBR_VERSION = '2.0.1';
const WBR_LIMIT = 40000;
const WBR_BATCH = 180;
const WBR_SECONDS = 2.0;
const WBR_SCOPES = [
    'uploads' => ['wp-content/uploads'], 'plugins' => ['wp-content/plugins'],
    'themes' => ['wp-content/themes'],
    'all' => ['wp-admin', 'wp-includes', 'wp-content'],
];
function wbr_e(string $s): string { return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function wbr_brand(): string {
    $name = base64_decode('d2J3ZWIuaXI=', true);
    if (!is_string($name) || !hash_equals('63cc8f2aea0b04a333673dd06b1f1063b315389964c47af1104c87a840e3980c', hash('sha256', $name))) {
        http_response_code(503); exit('Attribution integrity check failed.');
    }
    return $name;
}
function wbr_fail(string $message, int $code = 400): never {
    http_response_code($code);
    header('Content-Type: text/html; charset=UTF-8');
    exit('<!doctype html><meta charset="utf-8"><div dir="rtl" style="font:16px/2 sans-serif;padding:30px">'
        . wbr_e($message) . '<hr>© ' . wbr_e(wbr_brand()) . '</div>');
}
function wbr_json(array $data, int $code = 200): never {
    http_response_code($code); header('Content-Type: application/json; charset=UTF-8');
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE); exit;
}
function wbr_post(string $key): string { return isset($_POST[$key]) && is_string($_POST[$key]) ? $_POST[$key] : ''; }
function wbr_headers(): void {
    header('Cache-Control: private, no-store, max-age=0');
    header('X-Robots-Tag: noindex, nofollow, noarchive');
    header('X-Content-Type-Options: nosniff'); header('Referrer-Policy: no-referrer');
    header('X-Frame-Options: DENY');
}
ini_set('display_errors', '0'); wbr_headers();
$brand = wbr_brand();
if (PHP_VERSION_ID < 80100 || PHP_OS_FAMILY !== 'Linux') wbr_fail('PHP 8.1 یا بالاتر روی هاست لینوکسی لازم است.');
$root = realpath(__DIR__);
if (!$root || !is_readable($root . '/wp-load.php')) wbr_fail('فایل را کنار wp-load.php قرار دهید. اگر این فایل قابل خواندن نیست، ابتدا از File Manager یا پشتیبانی کمک بگیرید.');
foreach (['session_start', 'random_bytes', 'lstat', 'realpath', 'opendir', 'flock', 'chmod'] as $fn) {
    if (!function_exists($fn)) wbr_fail('تابع لازم در هاست در دسترس نیست: ' . $fn, 503);
}
// Normal WordPress authentication: no shared password, no first-visitor claim.
$booting = true;
register_shutdown_function(static function () use (&$booting): void {
    $e = error_get_last();
    if ($booting && $e && in_array($e['type'], [E_ERROR, E_PARSE, E_COMPILE_ERROR, E_CORE_ERROR], true)) {
        wbr_fail('بارگذاری وردپرس ممکن نشد. این ابزار برای شناسایی مدیر به وردپرس سالم نیاز دارد؛ دسترسی اولیه را از File Manager یا پشتیبانی اصلاح کنید.', 503);
    }
});
define('WP_USE_THEMES', false);
require_once $root . '/wp-load.php';
$booting = false;
wbr_headers();
if (!is_ssl()) wbr_fail('ابزار را با HTTPS باز کنید. اگر SSL پشت پراکسی شناسایی نمی‌شود، تنظیم آن باید در هاست اصلاح شود.', 403);
$toolUrl = site_url('/' . rawurlencode(basename(__FILE__)), 'https');
$isPost = ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST';
if (!is_user_logged_in()) {
    if ($isPost) wbr_json(['error' => 'ورود مدیر منقضی شده است؛ صفحه را دوباره باز کنید.'], 401);
    wp_safe_redirect(wp_login_url($toolUrl)); exit;
}
if (!current_user_can('manage_options') || !current_user_can('update_core') || (is_multisite() && !is_super_admin())) {
    wbr_fail('فقط مدیر دارای مجوز مدیریت و به‌روزرسانی هسته می‌تواند از ابزار استفاده کند.', 403);
}
if (realpath(ABSPATH) !== $root || !defined('WP_CONTENT_DIR') || realpath(WP_CONTENT_DIR) !== $root . '/wp-content') {
    wbr_fail('ساختار این نصب سفارشی است؛ نسخه عمومی ابزار برای آن عملیات اصلاح انجام نمی‌دهد.');
}
$scriptStamp = @filemtime(__FILE__);
if (!$scriptStamp || time() - $scriptStamp > 86400 || $scriptStamp > time() + 60) {
    wbr_fail('مهلت ۲۴ساعته ابزار تمام شده است. فایل را حذف کنید؛ برای استفاده تازه، دوباره آن را در ویرایشگر هاست ذخیره کنید.', 403);
}
$nonceAction = 'wbr-v2-' . hash('sha256', __FILE__);
if ($isPost && !wp_verify_nonce(wbr_post('nonce'), $nonceAction)) wbr_json(['error' => 'اعتبار درخواست تمام شده؛ صفحه را تازه کنید.'], 403);
if (session_status() !== PHP_SESSION_ACTIVE) {
    session_name('WBR' . substr(hash('sha256', $root), 0, 12));
    ini_set('session.use_strict_mode', '1'); ini_set('session.use_only_cookies', '1'); ini_set('session.use_trans_sid', '0');
    session_set_cookie_params(['lifetime' => 0, 'path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict']);
    if (!@session_start()) wbr_fail('ذخیره نشست در هاست ممکن نیست؛ با پشتیبانی بررسی کنید.', 503);
}
$sessionKey = 'wbr2_' . hash('sha256', $root . '|' . get_current_user_id() . '|' . wp_get_session_token());
$euid = function_exists('posix_geteuid') ? posix_geteuid() : null;
function wbr_row(string $path, string $status, string $reason, ?array $s = null, ?int $target = null): array {
    return ['path' => $path, 'status' => $status, 'reason' => $reason,
        'before' => $s ? sprintf('%04o', $s['mode'] & 07777) : null,
        'target' => $target === null ? null : sprintf('%04o', $target), 'after' => null,
        'uid' => $s['uid'] ?? null, 'gid' => $s['gid'] ?? null,
        'dev' => $s['dev'] ?? null, 'ino' => $s['ino'] ?? null,
        'ctime' => $s['ctime'] ?? null, 'mtime' => $s['mtime'] ?? null, 'size' => $s['size'] ?? null];
}
function wbr_stat(string $path, string $root): array {
    clearstatcache(true);
    $s = @lstat($path);
    if (!$s) throw new RuntimeException('خواندن مشخصات مسیر ممکن نیست.');
    $kind = $s['mode'] & 0170000;
    if ($kind === 0120000) throw new RuntimeException('لینک نمادین؛ هدف لینک بررسی یا تغییر نمی‌کند.');
    if (!in_array($kind, [0040000, 0100000], true)) throw new RuntimeException('مسیر از نوع فایل یا پوشه معمول نیست.');
    if (!str_starts_with($path, $root . '/') || @realpath($path) !== $path) throw new RuntimeException('مسیر واقعی متفاوت یا غیرقابل دسترس است.');
    if ($kind === 0100000 && $s['nlink'] > 1) throw new RuntimeException('چند پیوند سخت؛ تغییر خودکار انجام نمی‌شود.');
    if (($s['mode'] & 07000) !== 0) throw new RuntimeException('بیت مجوز ویژه دارد؛ بررسی مدیر سرور لازم است.');
    $rootStat = @lstat($root);
    if (!$rootStat || $s['dev'] !== $rootStat['dev']) throw new RuntimeException('فایل‌سیستم متفاوت؛ بررسی جداگانه لازم است.');
    return $s;
}
function wbr_excluded(string $rel): bool {
    $name = strtolower(basename($rel));
    return str_starts_with($name, '.') || in_array($name,
        ['wp-config.php', 'php.ini', 'web.config', 'backup', 'backups', 'updraft', 'ai1wm-backups', 'wpvividbackups', 'cache'], true)
        || (bool) preg_match('/\.(?:sql|sqlite|db|zip|gz|bz2|7z|tar|bak|log|key|pem|p12|pfx|ini|env|wpress)$/i', $name);
}
function wbr_env(string $root, ?int $euid): array {
    clearstatcache(true);
    // Disk capacity is optional; unavailable host functions must not block scanning.
    $free = false;
    if (is_callable('disk_free_space')) {
        try {
            $value = @disk_free_space($root);
            if ((is_int($value) || is_float($value)) && $value >= 0) $free = $value;
        } catch (Throwable $e) {
            // Keep the measurement unknown; permissions can still be inspected.
        }
    }
    $checks = [['title' => 'کاربر اجرای PHP', 'value' => $euid === null ? 'شناسه در دسترس نیست؛ مالکیت قابل تطبیق قطعی نیست.' : (string) $euid],
        ['title' => 'فضای آزاد فایل‌سیستم', 'value' => $free === false ? 'نامشخص؛ اطلاعات فضای دیسک در این هاست در دسترس نیست. بررسی مجوزها ادامه دارد.' : round($free / 1048576) . ' MB — این عدد سهمیه حساب یا inode نیست.'],
        ['title' => 'دامنه تشخیص', 'value' => 'مجوزهای معمول، مالکیت قابل مشاهده و دسترسی مؤثر؛ ACL، SELinux، سهمیه حساب و تنظیم وب‌سرور تشخیص قطعی داده نمی‌شوند.']];
    foreach (['.' => 'ریشه نصب', 'wp-config.php' => 'تنظیمات وردپرس', '.htaccess' => 'تنظیمات وب‌سرور', 'wp-content' => 'محتوا', 'wp-content/uploads' => 'رسانه‌ها'] as $rel => $label) {
        $path = $rel === '.' ? $root : $root . '/' . $rel; $s = @lstat($path);
        $value = !$s ? 'وجود ندارد یا قابل بررسی نیست' : sprintf('%04o', $s['mode'] & 07777)
            . ' | خواندن: ' . (is_readable($path) ? 'بله' : 'خیر') . ' | نوشتن برای PHP: ' . (is_writable($path) ? 'بله' : 'خیر');
        $checks[] = ['title' => $label, 'value' => $value];
    }
    return $checks;
}
function wbr_new(string $scope, string $root, ?int $euid): array {
    $queue = WBR_SCOPES[$scope];
    if ($scope === 'all') $queue = array_merge($queue, ['index.php', 'wp-activate.php', 'wp-blog-header.php', 'wp-comments-post.php',
        'wp-cron.php', 'wp-links-opml.php', 'wp-load.php', 'wp-login.php', 'wp-mail.php', 'wp-settings.php', 'wp-signup.php', 'wp-trackback.php', 'xmlrpc.php']);
    return ['id' => bin2hex(random_bytes(16)), 'phase' => 'scan', 'scope' => $scope, 'started' => time(),
        'updated' => time(), 'queue' => $queue, 'seen' => [], 'rows' => [], 'plan' => [], 'cursor' => 0,
        'pending' => [], 'complete' => false, 'environment' => wbr_env($root, $euid), 'message' => 'بررسی در حال انجام است.'];
}
function wbr_scan(array &$job, string $root, ?int $euid): void {
    $start = microtime(true); $n = 0;
    while ($job['queue'] && $n++ < WBR_BATCH && microtime(true) - $start < WBR_SECONDS) {
        $rel = array_pop($job['queue']);
        if (isset($job['seen'][$rel])) continue;
        if (count($job['seen']) >= WBR_LIMIT) throw new RuntimeException('سقف بررسی رسید؛ محدوده کوچک‌تر انتخاب کنید. هیچ مجوزی تغییر نکرده است.');
        $job['seen'][$rel] = true; $path = $root . '/' . $rel;
        if (wbr_excluded($rel)) { $job['rows'][] = wbr_row($rel, 'excluded', 'مسیر حساس یا مستثنا؛ اگر پوشه باشد همه زیرشاخه‌هایش کنار گذاشته می‌شوند.'); continue; }
        try { $s = wbr_stat($path, $root); }
        catch (RuntimeException $e) { $job['rows'][] = wbr_row($rel, 'manual', $e->getMessage()); continue; }
        $dir = ($s['mode'] & 0170000) === 0040000;
        if ($dir && (@lstat($path . '/wp-config.php') || @lstat($path . '/wp-load.php'))) {
            $job['rows'][] = wbr_row($rel, 'excluded', 'نشانه نصب جداگانه؛ پوشه و زیرشاخه‌ها کنار گذاشته شدند.', $s); continue;
        }
        $target = $dir ? 0755 : 0644; $old = $s['mode'] & 0777;
        $wrongOwner = $euid !== null && $s['uid'] !== $euid;
        if ($wrongOwner && ($old !== $target || !is_readable($path) || !is_writable($path))) {
            $row = wbr_row($rel, 'manual', 'مالک با کاربر PHP متفاوت است؛ ممکن است عمدی باشد. مالکیت/گروه باید با میزبان بررسی شود.', $s, $target);
        } elseif ($old !== $target) {
            $reason = (($old & 0002) !== 0 ? 'نوشتن برای سایر کاربران سیستم فعال است. ' : '')
                . 'با الگوی 755/644 تفاوت دارد؛ فقط با تأیید این الگو اصلاح شود.'
                . ($euid === null ? ' شناسه کاربر PHP معلوم نیست؛ موفقیت chmod تضمین نمی‌شود.' : '');
            $row = wbr_row($rel, 'candidate', $reason, $s, $target);
        } elseif (!is_readable($path) || !is_writable($path)) {
            $row = wbr_row($rel, 'review', 'عدد مجوز با الگو برابر است، اما دسترسی مؤثر محدود است؛ برای مسیر نیازمند نوشتن، مالکیت/ACL/هاست بررسی شود.', $s, $target);
        } else { $row = wbr_row($rel, 'same', 'عدد مجوز با الگو برابر است؛ این نتیجه تضمین سلامت فایل یا سایت نیست.', $s, $target); }
        $index = count($job['rows']); $job['rows'][] = $row;
        if ($row['status'] === 'candidate') $job['plan'][] = $index;
        if ($dir) {
            $h = @opendir($path);
            if ($h === false) { $job['rows'][$index]['reason'] .= ' زیرشاخه‌ها قابل پیمایش نیستند؛ پس از اصلاح محدود، دوباره اسکن کنید.'; continue; }
            try {
                while (($name = readdir($h)) !== false) {
                    if ($name === '.' || $name === '..') continue;
                    if (count($job['seen']) + count($job['queue']) >= WBR_LIMIT || microtime(true) - $start > WBR_SECONDS + 1) {
                        throw new RuntimeException('فهرست این محدوده بزرگ است؛ اسکن ناقص ماند و اصلاح فعال نشد. محدوده کوچک‌تر یا پشتیبانی لازم است.');
                    }
                    $job['queue'][] = $rel . '/' . $name;
                }
            } finally { closedir($h); }
        }
    }
    if (!$job['queue']) {
        $job['phase'] = 'ready'; $job['complete'] = true;
        $job['message'] = 'بررسی مسیرهای قابل دسترس تمام شد. موارد دستی و مستثنا را هم ببینید؛ همه تفاوت‌ها الزاماً خطا نیستند.';
    }
}
function wbr_apply(array &$job, string $root): void {
    $start = microtime(true); $n = 0;
    // Pending entries were saved to the session BEFORE this request changes files.
    foreach ($job['pending'] as $idx) {
        if ($n++ >= 80 || microtime(true) - $start > WBR_SECONDS) break;
        $row =& $job['rows'][$idx]; $path = $root . '/' . $row['path'];
        try {
            $s = wbr_stat($path, $root);
            if ($s['dev'] !== $row['dev'] || $s['ino'] !== $row['ino'] || $s['uid'] !== $row['uid'] || $s['gid'] !== $row['gid']
                || $s['ctime'] !== $row['ctime'] || $s['mtime'] !== $row['mtime'] || $s['size'] !== $row['size']
                || sprintf('%04o', $s['mode'] & 07777) !== $row['before']) {
                throw new RuntimeException('مسیر یا مالکیت یا مجوز از زمان بررسی تغییر کرده؛ اسکن تازه لازم است.');
            }
            $target = octdec($row['target']);
            if (!@chmod($path, $target)) throw new RuntimeException('chmod ناموفق؛ مالکیت، فایل‌سیستم فقط‌خواندنی یا محدودیت هاست بررسی شود.');
            clearstatcache(true, $path); $after = @lstat($path);
            if (!$after || ($after['mode'] & 07777) !== $target || $after['ino'] !== $row['ino'] || $after['dev'] !== $row['dev']) {
                throw new RuntimeException('نتیجه تغییر قابل تأیید نیست؛ بررسی دستی لازم است.');
            }
            $row['status'] = 'fixed'; $row['after'] = sprintf('%04o', $after['mode'] & 07777); $row['reason'] = 'تغییر انجام و مجوز دوباره خوانده شد.';
        } catch (RuntimeException $e) {
            $row['status'] = 'failed'; $row['reason'] = $e->getMessage();
            $job['phase'] = 'paused'; $job['message'] = 'با اولین خطا متوقف شد؛ تغییرات تأییدشده قبلی باقی می‌مانند.';
        }
        ++$job['cursor']; unset($row);
        if ($job['phase'] === 'paused') break;
    }
    $job['pending'] = [];
    if ($job['phase'] === 'apply' && $job['cursor'] >= count($job['plan'])) {
        $job['phase'] = 'done'; $job['message'] = 'مرحله اصلاح تمام شد. عملکرد سایت را آزمایش و سپس ابزار را حذف کنید.';
    }
}
function wbr_public(array $job, int $offset = 0): array {
    $counts = [];
    foreach ($job['rows'] as $row) $counts[$row['status']] = ($counts[$row['status']] ?? 0) + 1;
    return ['id' => $job['id'], 'phase' => $job['phase'], 'scope' => $job['scope'], 'message' => $job['message'],
        'visited' => count($job['seen']), 'remaining' => count($job['queue']), 'counts' => $counts,
        'environment' => $job['environment'], 'rows' => array_slice($job['rows'], $offset, 150),
        'totalRows' => count($job['rows']), 'cursor' => $job['cursor'], 'planned' => count($job['plan'])];
}
if ($isPost) {
    // One lock across all browser sessions for this script; no concurrent repairs.
    $lock = @fopen(__FILE__, 'rb');
    if (!$lock || !@flock($lock, LOCK_EX | LOCK_NB)) wbr_json(['error' => 'ابزار در درخواست دیگری مشغول است؛ چند لحظه بعد تلاش کنید.'], 409);
    $action = wbr_post('action');
    $job = $_SESSION[$sessionKey] ?? null;
    if (is_array($job) && time() - $job['updated'] > 3600) { unset($_SESSION[$sessionKey]); $job = null; }
    try {
        if ($action === 'start') {
            if (is_array($job) && in_array($job['phase'], ['scan', 'apply'], true)) throw new RuntimeException('عملیات قبلی هنوز فعال است؛ آن را ادامه دهید یا توقف کنید.');
            $scope = wbr_post('scope'); if (!isset(WBR_SCOPES[$scope])) throw new RuntimeException('محدوده معتبر نیست.');
            $job = wbr_new($scope, $root, $euid);
        } elseif ($action === 'remove') {
            if (is_array($job) && in_array($job['phase'], ['scan', 'apply'], true)) throw new RuntimeException('ابتدا عملیات را متوقف کنید.');
            if (wbr_post('confirm') !== 'yes') throw new RuntimeException('تأیید حذف لازم است.');
            $self = @lstat(__FILE__);
            if (!$self || ($self['mode'] & 0170000) !== 0100000 || !@unlink(__FILE__)) throw new RuntimeException('حذف خودکار نشد؛ فایل را از File Manager حذف کنید.');
            unset($_SESSION[$sessionKey]); session_write_close();
            wbr_json(['removed' => true]);
        } elseif (!is_array($job) || !hash_equals($job['id'], wbr_post('job'))) {
            throw new RuntimeException('گزارش معتبر در این مرورگر پیدا نشد؛ بررسی تازه لازم است.');
        } elseif ($action === 'apply') {
            if ($job['phase'] !== 'ready' || !$job['complete'] || !$job['plan'] || wbr_post('confirm') !== 'yes') throw new RuntimeException('ابتدا بررسی کامل و تأیید اصلاح لازم است.');
            if (time() - $job['started'] > 3600) throw new RuntimeException('گزارش قدیمی است؛ بررسی تازه انجام دهید.');
            $job['phase'] = 'apply'; $job['message'] = 'اصلاح مرحله‌ای شروع شد؛ این صفحه را باز نگه دارید.';
        } elseif ($action === 'stop') {
            $job['phase'] = 'paused'; $job['message'] = 'با درخواست شما متوقف شد؛ تغییرات قبلی باقی می‌مانند.';
        } elseif ($action === 'report') {
            $report = ['tool' => $brand, 'version' => WBR_VERSION, 'generated_at' => gmdate('c'),
                'scope' => $job['scope'], 'phase' => $job['phase'], 'environment' => $job['environment'], 'rows' => $job['rows']];
            wbr_json(['report' => $report]);
        } elseif ($action !== 'step' && $action !== 'status') throw new RuntimeException('درخواست معتبر نیست.');
        if ($action === 'step') {
            if ($job['pending']) {
                foreach ($job['pending'] as $idx) if ($job['rows'][$idx]['status'] === 'candidate') {
                    $job['rows'][$idx]['status'] = 'uncertain'; $job['rows'][$idx]['reason'] = 'پاسخ اجرای قبلی ثبت نشده؛ نتیجه ممکن است اعمال شده باشد. اسکن تازه لازم است.';
                }
                $job['pending'] = []; $job['phase'] = 'paused'; $job['message'] = 'درخواست قبلی کامل ثبت نشده است؛ گزارش را بگیرید و دوباره بررسی کنید.';
            } elseif ($job['phase'] === 'scan') wbr_scan($job, $root, $euid);
            elseif ($job['phase'] === 'apply') {
                $job['pending'] = array_slice($job['plan'], $job['cursor'], 80);
                $job['updated'] = time(); $_SESSION[$sessionKey] = $job;
                // Preserve original modes and in-flight paths before chmod.
                if (!session_write_close()) throw new RuntimeException('ذخیره گزارش قبل از اصلاح ناموفق بود؛ تغییری انجام نشد.');
                wbr_apply($job, $root);
                if (!@session_start()) wbr_json(['error' => 'ثبت نتیجه در نشست ناموفق بود. اصلاح را تکرار نکنید؛ صفحه را تازه و اسکن جدید انجام دهید.'], 503);
            }
        }
        $job['updated'] = time(); $_SESSION[$sessionKey] = $job; session_write_close();
        wbr_json(wbr_public($job, max(0, (int) wbr_post('offset'))));
    } catch (Throwable $e) {
        if (is_array($job)) {
            if ($job['phase'] === 'scan') { $job['phase'] = 'paused'; $job['complete'] = false; }
            $job['message'] = $e->getMessage(); $_SESSION[$sessionKey] = $job;
        }
        session_write_close(); wbr_json(['error' => $e->getMessage()], 400);
    }
}
$existing = $_SESSION[$sessionKey] ?? null;
if (is_array($existing) && time() - $existing['updated'] > 3600) { unset($_SESSION[$sessionKey]); $existing = null; }
$initial = is_array($existing) ? wbr_public($existing) : null;
session_write_close();
$cspNonce = base64_encode(random_bytes(18));
header("Content-Security-Policy: default-src 'none'; script-src 'nonce-$cspNonce'; style-src 'unsafe-inline'; connect-src 'self'; img-src 'none'; form-action 'self'; frame-ancestors 'none'; base-uri 'none'");
header('Content-Type: text/html; charset=UTF-8');
?>
<!doctype html><html lang="fa" dir="rtl"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>بررسی و اصلاح دسترسی وردپرس</title>
<style>
*{box-sizing:border-box}body{margin:0;background:#f5f6f7;color:#222;font:15px/1.9 Tahoma,Arial,sans-serif}main{max-width:1080px;margin:36px auto;padding:0 18px}header,.panel{background:white;border:1px solid #e4e7e9;border-radius:16px;padding:24px;margin-bottom:18px}h1{font-size:25px;margin:4px 0 10px}h2{font-size:18px}p{margin:8px 0}button,select{font:inherit;border:1px solid #d6dadd;border-radius:9px;padding:9px 15px;background:white;color:#222}button{cursor:pointer}button.primary{background:#1a9c58;color:white;border-color:#1a9c58}button:disabled{opacity:.45;cursor:default}.actions{display:flex;gap:10px;flex-wrap:wrap;align-items:center}.muted,footer{color:#646c72;font-size:13px}.bad{color:#a32121;white-space:pre-wrap}.cards{display:flex;gap:10px;flex-wrap:wrap;margin:15px 0}.card{background:#f5f6f7;border-radius:10px;padding:10px 16px}.scroll{overflow:auto}table{border-collapse:collapse;width:100%;font-size:13px}td,th{border-bottom:1px solid #eee;padding:10px;text-align:right;vertical-align:top}td.path{direction:ltr;text-align:left;word-break:break-all;min-width:190px}footer{text-align:center;padding:20px}a{color:#1a9c58}label{display:block;margin:12px 0}progress{width:100%;height:10px}#message{font-weight:bold}details{margin:14px 0}#environment p{border-bottom:1px solid #eee;padding:6px}noscript{color:#a32121}
</style></head><body><main>
<header><div class="muted">ابزار نگهداری وردپرس · نسخه <?= wbr_e(WBR_VERSION) ?></div><h1>بررسی و اصلاح دسترسی فایل‌ها</h1>
<p>ابتدا گزارش را ببینید؛ سپس فقط در صورت مناسب بودن الگوی ۷۵۵ برای پوشه و ۶۴۴ برای فایل، اصلاح را تأیید کنید.</p>
<p class="muted">ورود شما با حساب مدیر وردپرس تأیید شده است. نیازی به ساخت کلید یا ویرایش تنظیمات کد نیست.</p></header>
<section class="panel"><div class="actions"><select id="scope" aria-label="محدوده بررسی"><option value="all">مسیرهای استاندارد وردپرس</option><option value="uploads">فقط رسانه‌ها</option><option value="plugins">فقط افزونه‌ها</option><option value="themes">فقط قالب‌ها</option></select><button class="primary" id="scan">شروع بررسی</button><button id="stop" disabled>توقف</button></div>
<p id="message">با شروع بررسی، هیچ مجوزی تغییر نمی‌کند.</p><progress id="progress" hidden></progress><p id="error" class="bad" role="alert"></p><div id="cards" class="cards"></div>
<details><summary>چه چیزهایی جدا بررسی می‌شوند؟</summary><p>ریشه نصب، wp-config.php، فایل‌های مخفی، بکاپ‌ها و موارد حساس خودکار تغییر نمی‌کنند. لینک‌ها و نصب‌های دیگر کنار گذاشته می‌شوند. مشکل مالکیت یا محدودیت سرور ممکن است به پشتیبانی نیاز داشته باشد.</p><p>این ابزار بر پایه قواعد فنی کار می‌کند؛ مدل هوش مصنوعی، اسکنر بدافزار یا تضمین رفع همه خطاها نیست. هنگام اجرا، به‌روزرسانی و جابه‌جایی فایل‌ها را متوقف کنید.</p></details>
<noscript>JavaScript مرورگر باید فعال باشد؛ اجرای دستور دستی لازم نیست.</noscript>
</section>
<section class="panel"><h2>گزارش بررسی</h2><div id="environment"></div><div class="scroll"><table><thead><tr><th>مسیر</th><th>وضعیت</th><th>قبل ← پیشنهاد ← بعد</th><th>توضیح</th></tr></thead><tbody id="rows"></tbody></table></div><div class="actions"><button id="prev" disabled>قبلی</button><span id="page"></span><button id="next" disabled>بعدی</button><button id="download" disabled>دریافت گزارش کامل</button></div></section>
<section class="panel"><label><input type="checkbox" id="confirm"> بکاپ دارم، گزارش را خوانده‌ام و الگوی ۷۵۵/۶۴۴ برای این هاست و مسیرها مناسب است.</label><div class="actions"><button class="primary" id="apply" disabled>اصلاح موارد پیشنهادی</button><button id="remove">پایان کار و حذف ابزار</button></div><p class="muted">اصلاح بازگشت خودکار ندارد؛ در صورت توقف، تغییرات قبلی باقی می‌مانند. گزارش شامل مجوزهای قبلی را قبل از اصلاح دریافت کنید.</p></section>
<footer>© <?= date('Y') ?> <a href="https://<?= wbr_e($brand) ?>" target="_blank" rel="noopener noreferrer"><?= wbr_e($brand) ?></a> — طراحی و توسعه ابزار بررسی دسترسی وردپرس</footer>
</main><script nonce="<?= wbr_e($cspNonce) ?>">
'use strict';
const nonce=<?= json_encode(wp_create_nonce($nonceAction)) ?>;
let state=<?= json_encode($initial, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_INVALID_UTF8_SUBSTITUTE) ?>,busy=false,offset=0,halt=false;
const el=id=>document.getElementById(id),labels={same:'همخوان با الگو',candidate:'پیشنهاد اصلاح',manual:'بررسی دستی',review:'دسترسی محدود',excluded:'مستثنا',fixed:'اصلاح تأیید شد',failed:'خطا',uncertain:'نتیجه نامشخص'};
async function call(action,extra={}){
 const res=await fetch(location.pathname,{method:'POST',credentials:'same-origin',cache:'no-store',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({nonce,action,job:state?.id||'',offset:String(offset),...extra})});
 let d;try{d=await res.json()}catch{throw Error('پاسخ کامل دریافت نشد. صفحه را تازه کنید و وضعیت را بررسی کنید؛ درخواست اصلاح را کورکورانه تکرار نکنید.')}
 if(!res.ok||d.error)throw Error(d.error||'درخواست انجام نشد.');return d;
}
function render(){
 const active=state&&['scan','apply'].includes(state.phase);
 el('scan').disabled=busy||active;el('scope').disabled=busy||active;el('stop').disabled=!active;
 el('apply').disabled=busy||!state||state.phase!=='ready'||!state.counts.candidate||!el('confirm').checked;
 el('remove').disabled=busy||active;el('download').disabled=busy||!state;el('progress').hidden=!active;
 if(!state)return;
 el('message').textContent=state.message;el('cards').replaceChildren();
 for(const [text,value] of [['بررسی‌شده',state.visited],['پیشنهاد اصلاح',state.counts.candidate||0],['اصلاح تأییدشده',state.counts.fixed||0],['بررسی دستی / خطا',(state.counts.manual||0)+(state.counts.failed||0)+(state.counts.uncertain||0)]]){const x=document.createElement('div');x.className='card';x.textContent=text+': '+value;el('cards').append(x)}
 el('environment').replaceChildren();for(const c of state.environment){const p=document.createElement('p');p.textContent=c.title+': '+c.value;el('environment').append(p)}
 el('rows').replaceChildren();for(const row of state.rows){const tr=document.createElement('tr');[row.path,labels[row.status]||row.status,[row.before||'—',row.target||'—',row.after||'—'].join(' ← '),row.reason].forEach((v,i)=>{const td=document.createElement('td');td.textContent=v;if(i===0)td.className='path';tr.append(td)});el('rows').append(tr)}
 el('page').textContent=(offset+1)+' تا '+Math.min(offset+150,state.totalRows)+' از '+state.totalRows;
 el('prev').disabled=busy||offset===0;el('next').disabled=busy||offset+150>=state.totalRows;
}
async function run(action,extra={},pump=false){
 if(busy)return;busy=true;halt=false;el('error').textContent='';render();
 try{state=await call(action,extra);render();while(pump&&!halt&&['scan','apply'].includes(state.phase)){await new Promise(r=>setTimeout(r,120));state=await call('step');render()}if(halt&&['scan','apply'].includes(state.phase)){state=await call('stop');render()}}
 catch(e){el('error').textContent=e.message}finally{busy=false;render()}
}
el('scan').onclick=()=>{offset=0;el('confirm').checked=false;run('start',{scope:el('scope').value},true)};
el('apply').onclick=()=>run('apply',{confirm:'yes'},true);
el('confirm').onchange=render;
el('stop').onclick=()=>{if(busy)halt=true;else run('stop')};
el('prev').onclick=()=>{offset=Math.max(0,offset-150);run('status')};el('next').onclick=()=>{offset+=150;run('status')};
el('download').onclick=async()=>{try{const d=await call('report');const u=URL.createObjectURL(new Blob([JSON.stringify(d.report,null,2)],{type:'application/json;charset=utf-8'}));const a=document.createElement('a');a.href=u;a.download='wordpress-permissions-report.json';a.click();setTimeout(()=>URL.revokeObjectURL(u),1000)}catch(e){el('error').textContent=e.message}};
el('remove').onclick=async()=>{if(!confirm('فقط همین فایل ابزار حذف شود؟ گزارش مورد نیاز را دریافت کرده‌اید؟'))return;try{await call('remove',{confirm:'yes'});document.querySelector('main').replaceChildren();const p=document.createElement('p');p.textContent='ابزار حذف شد. عملکرد سایت را بررسی کنید.';document.querySelector('main').append(p)}catch(e){el('error').textContent=e.message}};
render();if(state&&['scan','apply'].includes(state.phase))run('step',{},true);
</script></body></html>

کدام مسیرها عمداً تغییر نمی‌کنند؟

ابزار فقط محدوده انتخاب‌شده را بررسی می‌کند. در حالت عمومی، سه پوشه استاندارد وردپرس و فهرستی مشخص از فایل‌های اصلی ریشه بررسی می‌شوند. پوشه‌ای که کنار وردپرس برای یک سایت دیگر ساخته‌اید وارد عملیات نمی‌شود.

خود ریشه نصب، از جمله public_html، خودکار تغییر نمی‌کند. فایل wp-config.php، فایل‌های مخفی مانند .htaccess و .user.ini و نام‌ها یا پسوندهای حساس مشخص‌شده در کد نیز مستثنا هستند. کنار گذاشتن یک پوشه به معنی کنار گذاشتن تمام زیرشاخه‌های آن است.

لینک نمادین، فایل دارای چند پیوند سخت، مسیر دارای بیت مجوز ویژه و مسیر روی فایل‌سیستم متفاوت هم به بررسی دستی سپرده می‌شوند. ابزار از پوشه‌ای که نشانه نصب جداگانه وردپرس داشته باشد عبور می‌کند. این کنترل‌ها جای شناخت ساختار سایت را نمی‌گیرند؛ داده خصوصی با نام سفارشی ممکن است با فهرست استثناها شناخته نشود.

برای نصب‌هایی مثل Bedrock، مسیر محتوای سفارشی یا ساختار غیرمعمول، نسخه عمومی ابزار مناسب نیست. اگر ابزار تفاوت مسیر محتوا را تشخیص دهد، اصلاح را فعال نمی‌کند. هدف این است که رفع خطای دسترسی وردپرس باعث بازتر شدن بی‌دلیل دسترسی داده‌های خصوصی نشود.

اگر خود public_html اجازه اجرای فایل را ندهد

اسکریپت باید بتواند اجرا شود تا مسیرها را بررسی کند. اگر وب‌سرور اجازه عبور از public_html یا خواندن فایل ابزار را نداشته باشد، از داخل همان فایل نمی‌توان مانع اجرای آن را برطرف کرد.

در این حالت، مجوز اولیه پوشه ریشه و فایل ابزار را در File Manager بررسی کنید و مقدار مناسب را از میزبان بگیرید. تنظیم تأییدشده برای خود public_html را به‌صورت بازگشتی روی همه فایل‌ها اعمال نکنید. پوشه‌های والد خارج از محل نصب نیز ممکن است در مشکل نقش داشته باشند.

نسخه حاضر برای شناسایی مدیر، وردپرس را بارگذاری می‌کند. اگر خرابی مجوزها باعث شود خود وردپرس یا صفحه ورود بالا نیاید، ابزار هم نمی‌تواند از این روش مدیر را شناسایی کند. راه‌حل این وضعیت، اصلاح دسترسی اولیه از پنل هاست یا پشتیبانی است؛ ورود بدون احراز هویت به ابزار تعمیر مناسب نیست.

اگر مرورگر کد PHP را نشان داد یا فایل را دانلود کرد، آن را از هاست حذف کنید و تنظیم اجرای PHP را با میزبان بررسی کنید. اگر HTTPS پشت پراکسی درست شناخته نمی‌شود، تنظیم تشخیص اتصال امن باید در سرور و وردپرس اصلاح شود.

اگر مرورگر بسته شد یا عملیات قطع شد

هر مرحله در یک درخواست کوتاه اجرا می‌شود. برنامه اصلی و مقادیر اولیه در نشست نگه داشته می‌شوند و فهرست مرحله اصلاح پیش از تغییر ذخیره می‌شود. اگر پاسخ مرحله قبلی کامل ثبت نشده باشد، ابزار آن را نتیجه قطعی موفق تلقی نمی‌کند؛ توقف می‌کند و بررسی دوباره می‌خواهد.

برای ادامه، همان مرورگر و حساب مدیر را استفاده کنید. پس از باز کردن صفحه، اگر نشست و عملیات هنوز معتبر باشند، بررسی یا اصلاح مرحله‌ای ادامه پیدا می‌کند. قطع اینترنت، از بین رفتن نشست یا محدودیت منابع سرور ممکن است مانع این ادامه شود؛ این قابلیت تضمین ادامه تحت هر شرایطی نیست.

ابزار برای هر بررسی سقف چهل هزار مسیر دارد. اگر یک محدوده بیش از سقف یا ظرفیت زمان پیمایش باشد، بررسی ناقص اعلام می‌شود و تأیید اصلاح آن گزارش فعال نمی‌شود. برای سایت بزرگ، محدوده کوچک‌تر انتخاب کنید یا عملیات را با پشتیبانی از مسیر مناسب سرور انجام دهید.

مجوزهای اصلاح‌شده قبل از توقف خودکار برنمی‌گردند. ابتدا گزارش را بگیرید، وضعیت فعلی را بررسی کنید و از اجرای پشت‌سرهم بدون خواندن نتیجه خودداری کنید. گزارش مقدار قبلی را نگه می‌دارد، اما جای بکاپ و برنامه بازیابی را نمی‌گیرد.

بررسی گزارش مجوزها پیش از تأیید تغییر فایل‌های سایت

مجوزها اصلاح شده‌اند، اما خطا باقی است

اگر گزارش اصلاح موفق است ولی کار قبلی هنوز انجام نمی‌شود، لازم است علت دیگری بررسی شود. تکرار همان تغییر عدد، اطلاعات تازه‌ای به شما نمی‌دهد. مسیر درگیر و نتیجه ابزار را کنار پیام اولیه بگذارید.

وضعیتقدم بعدی
مجوز همخوان است ولی نوشتن ممکن نیستبررسی کاربر PHP، گروه، ACL و محدودیت امنیتی سرور
مالک با کاربر PHP متفاوت استبررسی عمدی بودن این تنظیم و اصلاح احتمالی توسط میزبان
ساخت فایل همچنان ناموفق استبررسی سهمیه فضای حساب و تعداد فایل‌های مجاز
فقط نصب یا به‌روزرسانی مشکل داردبررسی تنظیمات وردپرس و روش دسترسی به فایل‌ها
خطای ۴۰۳ باقی مانده استبررسی قوانین وب‌سرور، فایروال و افزونه امنیتی
مسیر از بررسی کنار گذاشته شده استخواندن علت استثنا و بررسی جداگانه همان مسیر

برای پشتیبانی بنویسید دقیقاً چه کاری انجام نمی‌شود و کدام مسیر درگیر است. مثلاً: «ساخت پوشه در wp-content/uploads ناموفق است. ابزار مجوز پوشه را ۷۵۵ نشان می‌دهد؛ لطفاً مالکیت، کاربر اجرای PHP و سهمیه حساب را بررسی کنید.» چنین گزارشی از عبارت کلی «دسترسی سایت خراب است» قابل پیگیری‌تر است.

اگر مجوزها بعد از مدتی دوباره تغییر می‌کنند، دلیل تغییر را پیدا کنید. فرایند انتشار، بازیابی بکاپ، افزونه یا اقدام فرد دیگری ممکن است تنظیم تازه را برگرداند. در صورت وجود نشانه نفوذ، صرف تغییر Permissionها کافی نیست و باید علت امنیتی بررسی شود.

پایان کار؛ آزمایش سایت و حذف ابزار

همان کاری را که خطا می‌داد دوباره انجام دهید. برای مشکل رسانه، یک تصویر کوچک بارگذاری کنید و باز شدن آدرس آن را هم ببینید. برای به‌روزرسانی، پس از اطمینان از بکاپ همان عملیات را دوباره امتحان کنید. ورود مدیریت و چند صفحه اصلی سایت نیز باید درست کار کنند.

وقتی گزارش لازم را دریافت کردید، «پایان کار و حذف ابزار» را بزنید. پس از تأیید، فقط فایل خود ابزار حذف می‌شود. اگر هاست اجازه حذف نداد، از File Manager آن را پاک کنید. ابزار پس از ۲۴ ساعت از آخرین تغییر فایل منقضی می‌شود، اما انقضا جای حذف را نمی‌گیرد.

رفع خطای دسترسی وردپرس زمانی تمام شده که کار مشکل‌دار دوباره درست انجام شود و علت آن روشن باشد. همخوان شدن عددها قدمی در این مسیر است؛ معیار نهایی، نتیجه واقعی در سایت شماست.

مطالعه بیشتر و منابع

برای آشنایی با مدیریت فایل‌ها، مطالب هاست سی‌پنل و برای پیگیری تغییرات مشکوک، بخش امنیت سایت را ببینید.

توضیح مجوزها بر پایه راهنمای رسمی دسترسی فایل‌های وردپرس است. کنترل مجوز مدیر نیز مطابق مستندات current_user_can در وردپرس انجام می‌شود. این ابزار محصول رسمی وردپرس نیست؛ طراحی و توسعه آن برای وب‌وب انجام شده است.

سوالات متداول

برای اجرای ابزار باید کلید یا رمز جداگانه بسازم؟

خیر. فایل را در محل نصب وردپرس قرار می‌دهید و آدرسش را باز می‌کنید. اگر با حساب مدیر وارد سایت نشده باشید، به صفحه ورود معمول وردپرس هدایت می‌شوید. رمز یا کلید داخل کد وارد نمی‌کنید.

آیا به‌محض باز کردن فایل، مجوزها تغییر می‌کنند؟

خیر. ابتدا «شروع بررسی» را می‌زنید و گزارش می‌گیرید. تغییر مجوز فقط بعد از کامل شدن بررسی، تأیید شما و زدن دکمه اصلاح انجام می‌شود. بازدید عادی از آدرس، Permissionها را عوض نمی‌کند.

اگر وردپرس یا صفحه ورود باز نشود، ابزار کار می‌کند؟

این نسخه برای شناسایی مدیر به بارگذاری وردپرس نیاز دارد. اگر خرابی مانع آن باشد، باید ابتدا دسترسی اولیه از File Manager یا با کمک پشتیبانی اصلاح شود. ابزار راه ورود بدون احراز هویت ایجاد نمی‌کند.

آیا تمام مشکلات مالکیت و دسترسی را خودکار حل می‌کند؟

خیر. مجوز معمول با مالکیت، گروه و سیاست امنیتی هاست متفاوت است. ابزار بعضی نشانه‌ها را گزارش می‌کند، اما تغییر مالکیت یا اصلاح ACL و تنظیمات وب‌سرور را انجام نمی‌دهد. این موارد ممکن است به مدیر سرور نیاز داشته باشند.

چرا بهتر است فایل ابزار بعد از پایان حذف شود؟

این ابزار برای نگهداری موقت سایت است و قابلیت تغییر مجوز دارد. پس از دریافت گزارش و آزمایش عملکرد سایت، آن را با دکمه حذف یا File Manager پاک کنید. برای استفاده بعدی، دوباره نسخه اصلی را بارگذاری کنید.

  • دسترسی وردپرس
  • public_html
  • مجوز فایل
  • اسکریپت PHP
  • خطای آپلود

مقاله‌های مرتبط

ادامه همین موضوع در وبلاگ.

همه مقاله‌ها
کمک فنی

برای اجرای این کار کمک می‌خواهید؟

تیم فنی Wbweb از طراحی سایت تا هاست و سرور کنار شماست؛ درخواستتان را ثبت کنید یا تیکت بزنید.