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

Familiarity with the Structure of Forms and Prevention of CSRF Attacks

25 بهمن 1399
Laravel 7.0: آشنایی با ساختار فرم ها و جلوگیری از حملات CSRF (قسمت 11)

آشنایی با حملات CSRF

ما در جلسه قبل ظاهر Instagram را برای خودمان کپی کردیم و حالا نوبت به پیاده سازی منطق آن رسیده است. مشکل اصلی ما این است که بنای اصلی اینستاگرام بر اساس username است اما ما در فرم ثبت نام خود و در برنامه هیچ username ای نداریم که باید تصحیح شود. در قدم اول به فایل register.blade.php در پوشه views و سپس auth مراجعه کرده و به فرم موجود در آن نگاهی بیندازید. در ابتدای این فرم دستور csrf@ را داریم:

<div class="card-body">
    <form method="POST" action="{{ route('register') }}">
        @csrf
// بقیه کد ها //

فعلا با دستور route کاری نداشته باشید چرا که هنوز به مبحث route ها نرسیده ایم. دستور csrf@ که یک blade directive است باعث ایجاد یک فیلد hidden در فرم ما می شود:

فیلد مخفی CSRF که به فایل HTML پیوست می شود.
فیلد مخفی CSRF که به فایل HTML پیوست می شود.

csrf مخفف cross-site request forgery (به فارسی: جعل درخواست میان‌وب‌گاهی) است که یک نوع حمله توسط هکرها می باشد. من نمی خواهم بیش از حد وارد مباحث امنیتی بشوم اما به صورت خلاصه بدانید که این حملات نوعی از حملات هستند که از طریق تزریق یک اسکریپت خاص انجام می شوند و معمولا از کوکی ها برای پیاده سازی آن استفاده می شود. این نوع حملات می تونند در قالب های مختلفی ظاهر شوند اما معمولا دو مرحله دارند:

  • ایجاد یک URL یا اسکریپت مخرب
  • گول زدن افراد برای کلیک و اجرای URL یا اسکریپت

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

GET http://bank.com/transfer.do?acct=BOB&amount=100 HTTP/1.1

در اینجا یک سایت بانک فرضی را داریم که از درخواست های GET برای انتقال پول استفاده می کند. در این درخواست کاربری که login شده است می خواهد مقدار 100 دلار را به حساب BOB بپردازد اما هکر URL زیر را می سازد:

http://bank.com/transfer.do?acct=MARIA&amount=100000

در اینجا مقدار 100 هزار دلار به حساب MARIA منتقل خواهد شد اما Maria نمی تواند این دستور را اجرا کند چرا که در این حساب بانکی login نیست. بنابراین چه کار می کند؟ با استفاده از روش های مختلفی دارنده حساب را گول می زند که باید روی این لینک کلیک کند (مثلا برای او ایمیلی می فرستد که باید رمز عبور بانکی خود را تغییر دهد - خود را به جای بانک جا می زند) سپس دارنده حساب نیز روی لینک کلیک کرده و از آنجایی که اجازه این انتقال را دارد (لاگین شده است) عملیات انجام شده و پول به حساب Maria منتقل می شود.

توجه داشته باشید که این یک مثال بسیار ساده و خلاصه بود و عملیات های csrf کمی پیچیده تر از این حرف ها هستند اما برای درک مفهوم آن بهتر است. خوشبختانه فریم ورک لاراول در فرم های خود از سیستمی استفاده می کند که جلوی حملات CSRF را می گیرد. آیا فیلد hidden ای که بالاتر به شما نشان دادم را یادتان است؟ مقدار این فیلد که یک رشته تصادفی است (در تصویر بالا HbsDip8jtg7BgR3h5obEl6B9nLWyawCuMfO8Vkou) در سمت سرور چک می شود و فردی که از سایت دیگری درخواست را ارسال کند (مثلا از ایمیل فرد) چنین رشته ای را نخواهد داشت بنابراین حمله انجام نخواهد شد چرا که لاراول درخواست را رد می کند. لاراول شما را مجبور می کند که هر جایی که فرم داشتید حتما از دستور csrf@ استفاده کنید و گرنه فرم شما را رد خواهد کرد و به خطا برمی خورید.

اضافه کردن فیلد username به فرم ثبت نام

حالا به فایل register.blade.php برمی گردیم. یکی از فیلد های موجود در فرم را کپی کرده و سپس آن را برای username تصحیح کنید. مثلا من فیلد ایمیل را کپی می کنم و سپس آن را روی username قرار می دهم:

<div class="form-group row">
    <label for="email"
        class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

    <div class="col-md-6">
        <input id="email" type="email" class="form-control @error('email') is-invalid @enderror"
            name="email" value="{{ old('email') }}" required autocomplete="email">

        @error('email')
        <span class="invalid-feedback" role="alert">
            <strong>{{ $message }}</strong>
        </span>
        @enderror
    </div>
</div>

<div class="form-group row">
    <label for="username" class="col-md-4 col-form-label text-md-right">Username</label>

    <div class="col-md-6">
        <input id="username" type="username"
            class="form-control @error('username') is-invalid @enderror" name="username"
            value="{{ old('username') }}" required autocomplete="username">

        @error('username')
        <span class="invalid-feedback" role="alert">
            <strong>{{ $message }}</strong>
        </span>
        @enderror
    </div>
</div>

همانطور که می بینید من یک فیلد دیگر را برای username اضافه کرده ام. در این فرم چند مسئله وجود دارد. اولا متدی ()__ را می بینید که جدید است. این متد به شما اجازه می دهد سایت خود را به صورت چند زبانه بنویسید تا مثلا هم برای انگلیسی و هم برای فارسی باشد. برای این کار می توانید به پوشه resources و سپس lang بروید. در آنجا فایل های سایت خود را می بینید که می توانید ویرایش کنید و زبان های مختلفی را به آن ها اضافه کنید. پس از آن می توانید به این زبان ها دسترسی داشته باشید:

{{ __('messages.welcome') }}

مثلا این دستور به دنبال کلید welcome در فایل resources/lang/messages.php می گردد. من نمی خواهم در این دوره از آن استفاده کنم به همین خاطر به جای ()__ از همان رشته ساده username استفاده کرده ام.

سپس در قسمت کلاس های input از دستور error@ استفاده کرده ایم و username را به عنوان آرگومان به آن پاس داده ایم. این دستور به دنبال خطا های موجود در فیلد username می گردد و اگر خطایی پیدا کند کلاس بوت استرپ is-invalid را به آن اضافه می کند که باعث قرمز شدن فیلد می شود. این همان سیستم اعتبارسنجی فرم ها در لاراول است اما من نمی توانم فعلا بیشتر از این توضیح بدهم که این خطاها از کجا می آیند (در جلسات آینده به شما نشان خواهم داد). سپس متد ()old را داریم که برای راحتی کاربران شما ساخته شده است. اگر دقت کرده باشید پس از ثبت یک فرم صفحه refresh شده و سرور فرم را اعتبارسنجی می کند. اگر فرم مشکلی داشته باشد دوباره صفحه فرم به کاربر نمایش داده می شو تا آن را پر کند. مشکل اینجاست که پر کردن دوباره همه چیز خسته کننده است اما متد old مقدار فیلدهای فرم در ثبت اول را نگه داشته و در دفعه بعد درون فرم قرار می دهد (به شرطی که مثل کد های بالا آن را در Value صدا بزنید) تا کاربر راحت تر باشد.

قسمت error@ را نیز داریم که جداگانه و با enderror@ تمام می شود. در این قسمت متغیر message$ در دسترس است که پیام خطای تولید شده توسط لاراول می باشد و بعدا بیشتر با آن آشنا می شویم. در حال حاضر با مراجعه به مرورگر فیلد جدید را می بینیم اما مشکل بزرگی دارد. آیا می دانید چه مشکلی؟

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری ساخت اینستاگرام با Laravel 7 توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

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