سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

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

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
CSRF در PHP چیست؟ آموزش جلوگیری از حملات CSRF

CSRF در PHP چیست؟ آموزش جلوگیری از حملات CSRF

ممدیر کل سیستم ۲۶ شهریور ۱۴۰۵ ۱۹ دقیقه مطالعه ۱ بازدید
CSRF در PHP چیست؟ آموزش جلوگیری از حملات CSRF

CSRF یکی از آسیب‌پذیری‌های مهم برنامه‌های تحت وب است که می‌تواند باعث شود یک درخواست ناخواسته با استفاده از اعتبار احراز هویت کاربر ارسال شود.

نام CSRF مخفف:

Cross-Site Request Forgery

است.

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

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

در این مقاله یاد می‌گیریم CSRF چیست، چرا Session و Cookie در این حمله اهمیت دارند، CSRF Token چگونه کار می‌کند و چگونه در PHP یک سیستم محافظت در برابر CSRF طراحی کنیم.


# CSRF چیست؟

CSRF یا Cross-Site Request Forgery به حمله‌ای گفته می‌شود که در آن مهاجم تلاش می‌کند مرورگر یک کاربر احراز هویت‌شده را وادار به ارسال یک درخواست ناخواسته به یک سایت دیگر کند.

برای درک بهتر، این سناریو را در نظر بگیرید:


کاربر وارد سایت می‌شود

        ↓

Session ایجاد می‌شود

        ↓

کاربر احراز هویت شده است

        ↓

یک سایت یا صفحه دیگر درخواست خاصی ایجاد می‌کند

        ↓

مرورگر درخواست را به سایت اصلی ارسال می‌کند

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


# یک مثال ساده از CSRF

فرض کنید در یک پنل مدیریت، حذف کاربر با چنین URLای انجام می‌شود:


/admin/delete-user.php?id=15

و PHP نیز چیزی شبیه این دارد:


$id = $_GET['id'];



$stmt = $pdo->prepare(

    "DELETE FROM users WHERE id = :id"

);



$stmt->execute([

    'id' => $id

]);

فرض کنیم کاربر مدیر قبلاً وارد پنل شده و Session معتبر دارد.

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

مشکل SQL Injection در اینجا با Prepared Statement برطرف شده است، اما مسئله دیگری وجود دارد:

آیا این درخواست واقعاً از داخل پنل مدیریت ارسال شده است؟

این دقیقاً جایی است که CSRF اهمیت پیدا می‌کند.


# چرا Session در CSRF مهم است؟

بسیاری از برنامه‌های PHP از Session برای احراز هویت استفاده می‌کنند.

مثلاً:


session_start();



$_SESSION['user_id'] = 15;

بعد در درخواست‌های بعدی، مرورگر Cookie مربوط به Session را ارسال می‌کند.

بنابراین سرور می‌تواند تشخیص دهد درخواست مربوط به کدام کاربر است.

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

این تفاوت مهم است:


Authentication

=

چه کسی کاربر است؟



CSRF Protection

=

آیا این درخواست واقعاً از مسیر مورد انتظار برنامه ایجاد شده است؟


# CSRF چگونه اتفاق می‌افتد؟

یک سناریوی ساده را در نظر بگیرید.

کاربر در سایت:


example.com

وارد حساب خود شده است.

Session او فعال است.

سپس در یک صفحه دیگر قرار می‌گیرد که باعث ایجاد درخواست به example.com می‌شود.

اگر endpoint حساس سایت فقط Session را بررسی کند:


if (!isset($_SESSION['user_id'])) {

    exit('Unauthorized');

}

درخواست ممکن است همچنان از دید برنامه متعلق به همان کاربر باشد.

بنابراین احراز هویت به‌تنهایی دفاع کامل در برابر CSRF نیست.


# تفاوت CSRF و XSS چیست؟

CSRF و XSS هر دو از آسیب‌پذیری‌های مهم برنامه‌های وب هستند، اما یکسان نیستند.

XSS

تمرکز XSS روی اجرای محتوای ناخواسته در Context مرورگر است.

مثلاً:


Input

 ↓

HTML/DOM

 ↓

Browser

CSRF

تمرکز CSRF روی ارسال درخواست ناخواسته با اعتبار کاربر است.


Attacker-controlled page

        ↓

Browser

        ↓

Authenticated Request

        ↓

Target Website

به همین دلیل روش‌های دفاعی این دو نیز متفاوت هستند.


# آیا استفاده از HTTPS جلوی CSRF را می‌گیرد؟

خیر.

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

ممکن است یک درخواست CSRF نیز از طریق HTTPS ارسال شود.

بنابراین:


HTTPS

+

CSRF Protection

دو موضوع متفاوت هستند.


# CSRF Token چیست؟

یکی از روش‌های رایج جلوگیری از CSRF استفاده از CSRF Token است.

CSRF Token یک مقدار تصادفی و غیرقابل پیش‌بینی است که سرور ایجاد می‌کند و در Session کاربر نگه می‌دارد.

سپس هنگام ارسال فرم، Token نیز همراه درخواست ارسال می‌شود.

سرور مقدار دریافت‌شده را با مقدار ذخیره‌شده در Session مقایسه می‌کند.

اگر Token صحیح نباشد، درخواست رد می‌شود.

ساختار کلی:


Server

   ↓

Generate Token

   ↓

Store in Session

   ↓

Send Token to Form

   ↓

User submits form

   ↓

Token sent to Server

   ↓

Compare Tokens

   ↓

Accept / Reject


# ساخت CSRF Token در PHP

ابتدا Session را فعال می‌کنیم:


session_start();

سپس اگر Token وجود نداشت، آن را ایجاد می‌کنیم:


if (empty($_SESSION['csrf_token'])) {

    $_SESSION['csrf_token'] = bin2hex(

        random_bytes(32)

    );

}

در اینجا:


random_bytes(32)

یک مقدار تصادفی رمزنگاری‌شده تولید می‌کند.

سپس:


bin2hex()

آن را به یک رشته hexadecimal تبدیل می‌کند.


# نمایش CSRF Token در فرم

فرض کنید فرم حذف یا تغییر اطلاعات داریم.

می‌توانیم Token را به شکل Hidden Input قرار دهیم:


<form method="POST" action="/profile/update.php">



    <input

        type="hidden"

        name="csrf_token"

        value="<?= htmlspecialchars(

            $_SESSION['csrf_token'],

            ENT_QUOTES,

            'UTF-8'

        ) ?>"

    >



    <input

        type="text"

        name="name"

    >



    <button type="submit">

        ذخیره

    </button>



</form>

کاربر معمولاً Token را نمی‌بیند، اما هنگام Submit شدن فرم همراه درخواست ارسال می‌شود.


# بررسی CSRF Token در PHP

در فایل پردازش فرم:


session_start();



$token = $_POST['csrf_token'] ?? '';



if (

    empty($_SESSION['csrf_token']) ||

    !hash_equals($_SESSION['csrf_token'], $token)

) {

    http_response_code(403);

    exit('درخواست نامعتبر است.');

}

تابع مهم اینجا:


hash_equals()

است.

این تابع برای مقایسه امن رشته‌ها طراحی شده و برای مقایسه Token انتخاب مناسبی است.


# چرا از == استفاده نکنیم؟

ممکن است کدهایی مانند این ببینید:


if ($_SESSION['csrf_token'] == $_POST['csrf_token']) {

    // ...

}

برای مقایسه Token بهتر است از:


hash_equals()

استفاده کنیم:


if (

    hash_equals(

        $_SESSION['csrf_token'],

        $_POST['csrf_token']

    )

) {

    // Token valid

}

به این ترتیب مقایسه Token به شکل مناسب‌تری انجام می‌شود.


# ساخت یک CSRF Helper در PHP

اگر در پروژه چندین فرم داریم، بهتر است منطق CSRF را در یک Helper قرار دهیم.

مثلاً:


function csrf_token(): string

{

    if (empty($_SESSION['csrf_token'])) {

        $_SESSION['csrf_token'] = bin2hex(

            random_bytes(32)

        );

    }



    return $_SESSION['csrf_token'];

}

سپس:


function verify_csrf_token(?string $token): bool

{

    if (

        empty($_SESSION['csrf_token']) ||

        empty($token)

    ) {

        return false;

    }



    return hash_equals(

        $_SESSION['csrf_token'],

        $token

    );

}

حالا در فرم:


<input

    type="hidden"

    name="csrf_token"

    value="<?= htmlspecialchars(

        csrf_token(),

        ENT_QUOTES,

        'UTF-8'

    ) ?>"

>

و در Endpoint:


if (!verify_csrf_token($_POST['csrf_token'] ?? null)) {

    http_response_code(403);

    exit('درخواست نامعتبر است.');

}


# یک مثال کامل از فرم امن

فایل:


profile.php

می‌تواند شامل این باشد:


<?php



session_start();



if (empty($_SESSION['csrf_token'])) {

    $_SESSION['csrf_token'] = bin2hex(

        random_bytes(32)

    );

}



?>



<form method="POST" action="update-profile.php">



    <input

        type="hidden"

        name="csrf_token"

        value="<?= htmlspecialchars(

            $_SESSION['csrf_token'],

            ENT_QUOTES,

            'UTF-8'

        ) ?>"

    >



    <label>

        نام:

    </label>



    <input

        type="text"

        name="name"

    >



    <button type="submit">

        ذخیره

    </button>



</form>

در فایل:


update-profile.php

می‌توانیم Token را بررسی کنیم:


<?php



session_start();



$token = $_POST['csrf_token'] ?? '';



if (

    empty($_SESSION['csrf_token']) ||

    !hash_equals(

        $_SESSION['csrf_token'],

        $token

    )

) {

    http_response_code(403);

    exit('درخواست نامعتبر است.');

}



$name = $_POST['name'] ?? '';



// ادامه پردازش اطلاعات...


# آیا CSRF Token باید در دیتابیس ذخیره شود؟

معمولاً نیازی نیست.

در یک پیاده‌سازی ساده می‌توان Token را در Session ذخیره کرد:


$_SESSION['csrf_token']

و هنگام دریافت درخواست آن را با Token ارسال‌شده مقایسه کرد.

در نتیجه:


CSRF Token

      ↓

Session

      ↓

Form

      ↓

POST Request

      ↓

Comparison

کافی است.


# آیا برای هر فرم باید Token متفاوت داشته باشیم؟

الزاماً نه.

یک روش رایج این است که برای هر Session یک Token ایجاد کنیم و فرم‌های مختلف همان Token را ارسال کنند.

اما بسته به معماری و سطح امنیت موردنیاز، می‌توان Tokenهای متفاوت یا مکانیزم‌های دقیق‌تر نیز طراحی کرد.

برای اکثر پروژه‌های معمول PHP، یک Token امن در سطح Session می‌تواند الگوی ساده و قابل استفاده‌ای باشد.


# آیا بعد از هر درخواست باید CSRF Token را تغییر دهیم؟

الزاماً خیر.

می‌توان یک Token را در طول عمر Session نگه داشت.

مثلاً:


if (empty($_SESSION['csrf_token'])) {

    $_SESSION['csrf_token'] = bin2hex(

        random_bytes(32)

    );

}

این Token تا زمانی که Session ادامه دارد استفاده می‌شود.

در بعضی معماری‌ها Tokenها به‌صورت چرخشی یا وابسته به درخواست مدیریت می‌شوند، اما پیچیده‌تر شدن مکانیزم همیشه به معنی امنیت بهتر یا طراحی مناسب‌تر نیست.


# CSRF در درخواست‌های AJAX

اگر Frontend شما با JavaScript به PHP درخواست می‌فرستد، می‌توانید Token را در Header ارسال کنید.

مثلاً:


fetch('/api/profile/update.php', {

    method: 'POST',

    headers: {

        'Content-Type': 'application/json',

        'X-CSRF-Token': csrfToken

    },

    body: JSON.stringify({

        name: 'Saeed'

    })

});

در PHP:


$token = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';

سپس:


if (

    empty($_SESSION['csrf_token']) ||

    !hash_equals(

        $_SESSION['csrf_token'],

        $token

    )

) {

    http_response_code(403);

    exit('Invalid CSRF token.');

}

این الگو در برنامه‌های SPA و APIهای مبتنی بر Session کاربرد دارد.


# CSRF در REST API

اینجا باید یک نکته مهم را در نظر بگیریم.

CSRF بیشتر زمانی اهمیت پیدا می‌کند که احراز هویت مرورگر به‌صورت خودکار مانند Cookie یا Session همراه درخواست ارسال شود.

اگر API از یک Token در:


Authorization: Bearer ...

استفاده کند و این Token به‌صورت خودکار توسط مرورگر برای درخواست‌های Cross-Site اضافه نشود، مدل تهدید متفاوت است.

بنابراین نمی‌توان گفت:

> هر API حتماً به CSRF Token نیاز دارد.

باید نوع Authentication و نحوه ارسال Credentialها را بررسی کرد.


# SameSite Cookie چیست؟

یکی دیگر از مکانیزم‌های مهم در کاهش ریسک CSRF، ویژگی:


SameSite

در Cookie است.

در PHP می‌توان هنگام ایجاد Cookie آن را تنظیم کرد:


setcookie(

    'session_token',

    $token,

    [

        'expires' => time() + 3600,

        'path' => '/',

        'secure' => true,

        'httponly' => true,

        'samesite' => 'Lax'

    ]

);

مقادیر رایج SameSite عبارت‌اند از:


Strict

Lax

None


# SameSite=Strict

در حالت Strict مرورگر محدودیت بیشتری برای ارسال Cookie در درخواست‌های Cross-Site اعمال می‌کند.

مثلاً:


'samesite' => 'Strict'

این گزینه می‌تواند محافظت بیشتری در برابر برخی سناریوهای Cross-Site ایجاد کند، اما ممکن است روی بعضی جریان‌های طبیعی ورود یا Navigation سایت اثر بگذارد.


# SameSite=Lax

در بسیاری از سناریوهای وب، Lax انتخاب متداولی است:


'samesite' => 'Lax'

این حالت تعادل متفاوتی میان امنیت و سازگاری ایجاد می‌کند.

اما باید معماری واقعی سایت و نحوه استفاده از Cookie را در نظر گرفت.


# SameSite=None

برای:


'samesite' => 'None'

Cookie می‌تواند در Contextهای Cross-Site ارسال شود.

در مرورگرهای مدرن، استفاده از SameSite=None نیازمند:


'secure' => true

است.

بنابراین:


[

    'secure' => true,

    'samesite' => 'None'

]

معمولاً فقط در سناریوهایی استفاده می‌شود که واقعاً به Cross-Site Cookie نیاز دارید.


# آیا SameSite جایگزین CSRF Token است؟

بهتر است آن را یک لایه دفاعی در نظر بگیریم، نه اینکه بدون بررسی معماری، همیشه آن را جایگزین Token کنیم.

برای عملیات حساس می‌توان از ترکیب چند مکانیزم استفاده کرد:


CSRF Token

+

SameSite Cookie

+

Origin/Referer Validation

+

Authentication

البته انتخاب دقیق این موارد به معماری برنامه بستگی دارد.


# بررسی Origin و Referer

برای بعضی درخواست‌های حساس می‌توان Headerهایی مانند:


Origin

و:


Referer

را نیز بررسی کرد.

مثلاً:


$origin = $_SERVER['HTTP_ORIGIN'] ?? '';



if ($origin !== 'https://example.com') {

    http_response_code(403);

    exit('Invalid origin.');

}

اما نباید بدون درنظر گرفتن Proxy، CDN، معماری چنددامنه‌ای و رفتار مرورگرها، یک مقدار ثابت را کورکورانه اعمال کنیم.

این روش بهتر است به‌عنوان یک لایه دفاعی تکمیلی استفاده شود.


# CSRF برای چه درخواست‌هایی اهمیت بیشتری دارد؟

CSRF بیشتر برای عملیات‌هایی اهمیت دارد که باعث تغییر وضعیت یا ایجاد اثر جانبی می‌شوند.

مثلاً:


تغییر رمز عبور

تغییر ایمیل

حذف حساب

حذف کاربر

تغییر تنظیمات

ثبت سفارش

انتقال وجه

تغییر سطح دسترسی

ایجاد یا حذف اطلاعات

در مقابل، درخواست‌های صرفاً خواندنی معمولاً مدل تهدید متفاوتی دارند.

به‌طور کلی، عملیات تغییر وضعیت بهتر است با روش‌های مناسب محافظت شوند.


# آیا GET برای حذف اطلاعات مناسب است؟

بهتر است عملیات تغییر وضعیت مانند:


DELETE

UPDATE

CREATE

را با GET انجام ندهیم.

مثلاً این طراحی:


/delete-user.php?id=15

برای حذف کاربر مناسب نیست.

بهتر است عملیات تغییر وضعیت با روش HTTP مناسب و کنترل CSRF انجام شود.

مثلاً:


<form method="POST" action="/delete-user.php">

و داخل فرم:


<input

    type="hidden"

    name="csrf_token"

    value="..."

>

سپس سمت سرور Token بررسی شود.


# چرا استفاده از GET برای عملیات حساس مشکل‌ساز است؟

GET باید تا حد امکان برای دریافت منابع و اطلاعات استفاده شود.

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

برای مثال ممکن است URL توسط:

  • مرورگر
  • لینک
  • Crawler
  • Prefetch
  • سیستم‌های مختلف

دیده یا درخواست شود.

به همین دلیل عملیات تغییر وضعیت را نباید صرفاً به بازدید از یک URL GET وابسته کنیم.


# CSRF و Session Regeneration

در سیستم‌های احراز هویت بهتر است هنگام Login موفق، Session ID نیز طبق طراحی امن برنامه Regenerate شود.

مثلاً:


session_regenerate_id(true);

سپس اطلاعات کاربر را در Session قرار می‌دهیم:


$_SESSION['user_id'] = $user['id'];

این موضوع بیشتر به امنیت Session مربوط است، اما در طراحی کلی Authentication و CSRF باید در کنار یکدیگر در نظر گرفته شوند.


# CSRF و Logout

حتی عملیات Logout نیز می‌تواند نیازمند بررسی طراحی باشد.

اگر Logout صرفاً یک عملیات ساده و کم‌خطر باشد، معماری‌های مختلفی وجود دارد؛ اما اگر endpointهای تغییر وضعیت شما به CSRF Token نیاز دارند، Logout نیز باید بر اساس همان سیاست امنیتی بررسی شود.

اصل مهم این است که برای تمام Endpointهای تغییر وضعیت، یک سیاست مشخص داشته باشیم.


# CSRF در Laravel

اگر از PHP خام به Laravel بروید، معمولاً نیازی نیست سیستم CSRF را از ابتدا خودتان پیاده‌سازی کنید.

Laravel برای فرم‌های Web خود مکانیزم CSRF دارد.

مثلاً در Blade:


<form method="POST" action="/profile">

    @csrf



    <input

        type="text"

        name="name"

    >



    <button type="submit">

        ذخیره

    </button>

</form>

دستور:


@csrf

Token موردنیاز را در فرم قرار می‌دهد.

در نتیجه لازم نیست در هر پروژه Laravel دوباره Helper مربوط به تولید و بررسی Token را از صفر بنویسید.


# CSRF در WordPress

WordPress نیز برای بسیاری از عملیات مدیریتی و فرم‌ها از مکانیزمی به نام Nonce استفاده می‌کند.

Nonce در WordPress بخشی از سیستم امنیتی درخواست‌ها است و برای بررسی Intent و جلوگیری از درخواست‌های ناخواسته در بسیاری از سناریوها استفاده می‌شود.

اما نباید آن را دقیقاً معادل یک Session CSRF Token سفارشی در PHP خام بدانیم؛ پیاده‌سازی و چرخه عمر آن در WordPress متفاوت است.

برای توسعه افزونه WordPress باید از APIهای امنیتی خود WordPress استفاده کرد، نه اینکه سیستم امنیتی جداگانه‌ای بدون دلیل ایجاد کنیم.


# اشتباهات رایج در جلوگیری از CSRF

اشتباه اول: اعتماد به Session به‌تنهایی

این:


if (isset($_SESSION['user_id'])) {

    // Request accepted

}

به‌تنهایی محافظت CSRF نیست.

Session فقط نشان می‌دهد کاربر احراز هویت شده است.


اشتباه دوم: استفاده از یک Token ثابت برای تمام کاربران

نباید یک Token ثابت را در سورس پروژه قرار دهیم:


$csrf_token = '123456';

Token باید غیرقابل پیش‌بینی و مرتبط با Session یا درخواست باشد.


اشتباه سوم: تولید Token با rand()

استفاده از روش‌های ضعیف برای تولید Token مناسب نیست.

برای Token امنیتی از:


random_bytes()

استفاده کنید.


اشتباه چهارم: مقایسه Token با روش نامناسب

به‌جای:


if ($token == $_SESSION['csrf_token']) {

از:


hash_equals(

    $_SESSION['csrf_token'],

    $token

);

استفاده کنید.


اشتباه پنجم: استفاده از CSRF Token در URL

بهتر است Tokenهای CSRF را در Query String قرار ندهیم:


?action=delete&csrf_token=...

برای فرم‌ها استفاده از POST و قرار دادن Token در Body معمولاً مناسب‌تر است.


اشتباه ششم: تصور اینکه HTTPS به‌تنهایی کافی است

HTTPS بسیار مهم است، اما جایگزین CSRF Protection نیست.


# یک ساختار پیشنهادی برای پروژه PHP

اگر پروژه PHP شما چندین فرم دارد، می‌توانید ساختار ساده‌ای مانند این داشته باشید:


project/

│

├── public/

│   ├── index.php

│   ├── login.php

│   └── profile.php

│

├── app/

│   ├── helpers/

│   │   └── csrf.php

│   │

│   └── controllers/

│

└── config/

در:


app/helpers/csrf.php

منطق مربوط به Token قرار بگیرد:


<?php



function csrf_token(): string

{

    if (empty($_SESSION['csrf_token'])) {

        $_SESSION['csrf_token'] = bin2hex(

            random_bytes(32)

        );

    }



    return $_SESSION['csrf_token'];

}



function verify_csrf_token(?string $token): bool

{

    if (

        empty($_SESSION['csrf_token']) ||

        empty($token)

    ) {

        return false;

    }



    return hash_equals(

        $_SESSION['csrf_token'],

        $token

    );

}

و در فرم:


<input

    type="hidden"

    name="csrf_token"

    value="<?= htmlspecialchars(

        csrf_token(),

        ENT_QUOTES,

        'UTF-8'

    ) ?>"

>

و در Controller:


if (

    !verify_csrf_token(

        $_POST['csrf_token'] ?? null

    )

) {

    http_response_code(403);

    exit('درخواست نامعتبر است.');

}

این ساختار را می‌توان بعداً با Middleware و معماری حرفه‌ای‌تر توسعه داد.


# چک‌لیست جلوگیری از CSRF در PHP

قبل از انتشار یک پروژه PHP این موارد را بررسی کنید:

  • [ ] تمام عملیات حساس شناسایی شده‌اند.
  • [ ] عملیات تغییر وضعیت با GET انجام نمی‌شوند.
  • [ ] برای فرم‌های حساس CSRF Token استفاده شده است.
  • [ ] Token با random_bytes() تولید می‌شود.
  • [ ] Token در Session نگهداری می‌شود.
  • [ ] Token هنگام دریافت درخواست با hash_equals() بررسی می‌شود.
  • [ ] درخواست نامعتبر با HTTP Status مناسب مانند 403 رد می‌شود.
  • [ ] Cookieهای Session تنظیمات امنیتی مناسبی دارند.
  • [ ] ویژگی SameSite متناسب با معماری سایت تنظیم شده است.
  • [ ] Cookieهای حساس در HTTPS با Secure استفاده می‌شوند.
  • [ ] Cookieهای حساس در صورت نیاز HttpOnly هستند.
  • [ ] برای APIها نوع Authentication بررسی شده است.
  • [ ] در صورت نیاز Origin یا Referer نیز به‌عنوان لایه دفاعی بررسی شده است.
  • [ ] Session ID هنگام Login به شکل مناسب Regenerate می‌شود.
  • [ ] سیستم CSRF در Frameworkهایی مانند Laravel و WordPress به‌صورت صحیح استفاده می‌شود.

# جمع‌بندی

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

نکته مهم این است که:

Authentication با CSRF Protection یکسان نیست.

Session می‌تواند مشخص کند کاربر چه کسی است، اما CSRF Token می‌تواند کمک کند سرور بررسی کند درخواست از مسیر مورد انتظار برنامه ایجاد شده است.

یکی از روش‌های رایج در PHP استفاده از Token تصادفی است:


$_SESSION['csrf_token'] = bin2hex(

    random_bytes(32)

);

سپس Token در فرم قرار می‌گیرد:


<input

    type="hidden"

    name="csrf_token"

    value="<?= htmlspecialchars(

        $_SESSION['csrf_token'],

        ENT_QUOTES,

        'UTF-8'

    ) ?>"

>

و هنگام دریافت درخواست:


if (

    !hash_equals(

        $_SESSION['csrf_token'],

        $_POST['csrf_token'] ?? ''

    )

) {

    http_response_code(403);

    exit('درخواست نامعتبر است.');

}

در کنار CSRF Token می‌توان از مکانیزم‌هایی مانند SameSite Cookie، Secure Cookie، HttpOnly، بررسی Origin و طراحی صحیح HTTP Methodها نیز استفاده کرد.

در Frameworkهایی مانند Laravel نیز بهتر است از مکانیزم داخلی Framework استفاده کنیم و سیستم CSRF را بدون نیاز دوباره پیاده‌سازی نکنیم.

در نهایت، CSRF تنها یکی از بخش‌های امنیت برنامه است و باید در کنار موضوعاتی مانند XSS، SQL Injection، امنیت Session، احراز هویت، مدیریت رمز عبور و امنیت آپلود فایل بررسی شود.


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

CSRF در PHP چیست؟

CSRF نوعی حمله است که در آن مهاجم تلاش می‌کند مرورگر یک کاربر احراز هویت‌شده را وادار به ارسال درخواست ناخواسته به یک سایت کند.

CSRF Token چیست؟

CSRF Token یک مقدار تصادفی و غیرقابل پیش‌بینی است که معمولاً به Session کاربر مرتبط می‌شود و همراه درخواست ارسال و در سمت سرور بررسی می‌شود.

آیا Session به‌تنهایی از CSRF جلوگیری می‌کند؟

خیر. Session برای احراز هویت کاربر استفاده می‌شود، اما به‌تنهایی ثابت نمی‌کند که درخواست توسط خود کاربر و از مسیر مورد انتظار برنامه ایجاد شده است.

آیا HTTPS جلوی CSRF را می‌گیرد؟

خیر. HTTPS امنیت ارتباط را افزایش می‌دهد، اما جایگزین مکانیزم‌های CSRF نیست.

آیا SameSite جایگزین CSRF Token است؟

SameSite می‌تواند ریسک برخی حملات Cross-Site را کاهش دهد، اما اینکه آیا به‌تنهایی کافی است یا خیر به معماری و نحوه Authentication برنامه بستگی دارد.

آیا GET می‌تواند باعث CSRF شود؟

بله، اگر یک عملیات حساس و تغییر‌دهنده وضعیت به GET متصل شده باشد، طراحی می‌تواند در برابر درخواست‌های ناخواسته آسیب‌پذیرتر شود. عملیات تغییر وضعیت بهتر است با HTTP Method مناسب و محافظت CSRF انجام شوند.

آیا REST API به CSRF Token نیاز دارد؟

بستگی به روش Authentication دارد. APIهایی که Credential آنها به‌صورت خودکار توسط مرورگر مانند Cookie ارسال می‌شود، مدل تهدید متفاوتی نسبت به APIهایی دارند که از Bearer Token در Authorization Header استفاده می‌کنند.

آیا Laravel از CSRF پشتیبانی می‌کند؟

بله. Laravel برای درخواست‌های Web مکانیزم داخلی CSRF دارد و در فرم‌های Blade می‌توان از @csrf استفاده کرد.

تفاوت CSRF و XSS چیست؟

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

آیا CSRF Token باید در دیتابیس ذخیره شود؟

در یک پیاده‌سازی معمول PHP، می‌توان Token را در Session نگه داشت و نیازی به ذخیره آن در دیتابیس نیست.

نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه