آسیب‌پذیری‌های رایج در اپلیکیشن‌های Laravel

آسیب‌پذیری‌های رایج در اپلیکیشن‌های Laravel اغلب مشابه با سایر اپلیکیشن‌های مبتنی بر وب هستند، اما با توجه به ویژگی‌ها و ساختار لاراول، راهکارهای مدیریت این آسیب‌پذیری‌ها نیز تسهیل شده است. در زیر به این آسیب‌پذیری‌ها و راه‌های مقابله با آن‌ها اشاره می‌کنیم:

1. SQL Injection (تزریق SQL)

  • شرح: این آسیب‌پذیری زمانی رخ می‌دهد که داده‌های ورودی کاربر مستقیماً به کوئری‌های دیتابیس ارسال شوند.
  • راهکار در لاراول:
    • استفاده از Query Builder یا Eloquent ORM که به صورت پیش‌فرض داده‌ها را پاکسازی می‌کنند.
    • نمونه کد امن:
      $users = DB::table('users')->where('email', $email)->get();

       

    • استفاده از Binding:
      $users = DB::select('SELECT * FROM users WHERE email = ?', [$email]);

       

2. Cross-Site Scripting (XSS)

  • شرح: تزریق کدهای مخرب جاوااسکریپت به صفحات وب.
  • راهکار در لاراول:
    • استفاده از فیلترهای داخلی لاراول برای خروجی داده‌ها:
      {{ $variable }}

       

    • در موارد خاص که نیاز به خروجی خام دارید:
      {!! $variable !!}

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

3. Cross-Site Request Forgery (CSRF)

  • شرح: مهاجم سعی می‌کند درخواست‌های ناخواسته‌ای را از سمت کاربران معتبر ارسال کند.
  • راهکار در لاراول:
    • لاراول به صورت پیش‌فرض دارای CSRF Protection است.
    • اطمینان از استفاده از @csrf در فرم‌ها:
      <form method="POST" action="/example"> @csrf <input type="text" name="data"> <button type="submit">ارسال</button> </form>

       

4. Mass Assignment (تخصیص انبوه)

  • شرح: مهاجم می‌تواند فیلدهای حساس را به‌صورت غیرمجاز تغییر دهد.
  • راهکار در لاراول:
    • استفاده از ویژگی‌های fillable یا guarded در مدل‌ها:
      protected $fillable = ['name', 'email']; // یا protected $guarded = ['is_admin'];

       

5. Insecure File Upload (آپلود فایل ناامن)

  • شرح: مهاجم می‌تواند فایل‌های مخرب (مانند اسکریپت‌های PHP) آپلود کند.
  • راهکار در لاراول:
    • اعتبارسنجی نوع فایل در هنگام آپلود:
      $request->validate([ 'file' => 'required|mimes:jpg,jpeg,png,pdf|max:2048', ]);

       

    • ذخیره فایل‌ها در خارج از مسیر اصلی پروژه (storage):
      $path = $request->file('file')->store('uploads');

       

6. Authentication Vulnerabilities (آسیب‌پذیری در احراز هویت)

  • شرح: سوءاستفاده از فرآیند احراز هویت یا ضعف در آن.
  • راهکار در لاراول:
    • استفاده از سیستم احراز هویت داخلی لاراول (Laravel Breeze یا Laravel Sanctum).
    • فعال کردن hashed password با استفاده از bcrypt:
      $user->password = bcrypt($request->password);

       

7. Directory Traversal (گشت‌وگذار در دایرکتوری‌ها)

  • شرح: دسترسی غیرمجاز به فایل‌های حساس سیستم.
  • راهکار در لاراول:
    • محدود کردن دسترسی به مسیرها و فایل‌ها.
    • استفاده از فانکشن‌هایی مانند storage_path() یا base_path() برای مدیریت مسیرها به‌جای استفاده از مقادیر ورودی.

8. Sensitive Data Exposure (افشای داده‌های حساس)

  • شرح: ذخیره‌سازی یا افشای داده‌های حساس مانند رمزهای عبور یا کلیدهای API.
  • راهکار در لاراول:
    • استفاده از فایل .env برای ذخیره داده‌های حساس و اطمینان از عدم نمایش آن‌ها در محیط عمومی.
    • رمزنگاری داده‌ها با استفاده از:
      $encrypted = encrypt('your_data'); $decrypted = decrypt($encrypted);

       

نکات کلی برای افزایش امنیت در لاراول:

  1. بروزرسانی مداوم: همیشه از آخرین نسخه لاراول استفاده کنید.
  2. فعال کردن HTTPS: ارتباطات را رمزنگاری کنید.
  3. پیکربندی مناسب سرور: دسترسی به فایل‌های حساس مانند .env یا دایرکتوری‌های storage را مسدود کنید.
  4. مانیتورینگ: ابزارهای نظارتی مانند Sentry یا Laravel Telescope را برای مانیتورینگ خطاها و فعالیت‌های مشکوک پیاده‌سازی کنید.
  5. استفاده از فایروال: از WAF برای جلوگیری از حملات متداول استفاده کنید.

قراردادهای لاراول برای نامگذاری

بخش مربوطه قاعده اسم گذاری ✔️ روش قابل قبول ❌ روش اشتباه
Controller اسامی مفرد ArticleController ArticlesController
Route اسامی جمع articles/1 article/1
Route name روش snake_case همراه با نقاط اتصال users.show_active users.show-active, show-active-users
Model اسامی مفرد User Users
hasOne or belongsTo relationship اسامی مفرد articleComment articleComments, article_comment
All other relationships اسامی جمع articleComments articleComment, article_comments
Table اسامی جمع article_comments article_comment, articleComments
Pivot table نام مدل ها با اسامی مفرد و ترتیب الفبایی article_user user_article, articles_users
Table column روش snake_case بدون اسم مدل meta_title MetaTitle; article_meta_title
Model property روش snake_case $model->created_at $model->createdAt
Foreign key اسامی مفرد model name with _id suffix article_id ArticleId, id_article, articles_id
Primary key id custom_id
Migration 2017_01_01_000000_create_articles_table 2017_01_01_000000_articles
Method روش camelCase getAll get_all
Method in resource controller table store saveArticle
Method in test class روش camelCase testGuestCannotSeeArticle test_guest_cannot_see_article
Variable روش camelCase $articlesWithAuthor $articles_with_author
Collection توصیفی و اسامی جمع $activeUsers = User::active()->get() $active, $data
Object توصیفی و اسامی مفرد $activeUser = User::active()->first() $users, $obj
Config and language files index snake_case articles_enabled ArticlesEnabled; articles-enabled
View kebab-case show-filtered.blade.php showFiltered.blade.php, show_filtered.blade.php
Config snake_case google_calendar.php googleCalendar.php, google-calendar.php
Contract (interface) صفت یا اسم AuthenticationInterface Authenticatable, IAuthentication
Trait صفت Notifiable NotificationTrait
Trait (PSR) adjective NotifiableTrait Notification
Enum singular UserType UserTypesUserTypeEnum
FormRequest singular UpdateUserRequest UpdateUserFormRequestUserFormRequestUserRequest
Seeder singular UserSeeder UsersSeeder

اصول برنامه نویسی بهینه: اصل DRY (خودت را تکرار نکن)

اصل DRY (Don’t Repeat Yourself) یکی از اصول مهم در برنامه‌نویسی است که بر جلوگیری از تکرار کد و منطق تأکید دارد. هدف این اصل، کاهش تکرار کد و افزایش قابلیت نگهداری و خوانایی آن است. با رعایت اصل DRY، تغییرات در یک بخش از کد به‌طور خودکار در تمام بخش‌هایی که از آن کد استفاده می‌کنند، اعمال می‌شود و این باعث کاهش خطاها و افزایش بهره‌وری می‌شود.

در چارچوب لاراول، می‌توان اصل DRY را به چندین روش مختلف پیاده‌سازی کرد:

1. استفاده از کلاس‌های سرویس (Service Classes)

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

namespace App\Services;

class UserService
{
    public function register(array $data)
    {
        // منطق ثبت نام کاربر
    }

    public function updateProfile(User $user, array $data)
    {
        // منطق به روزرسانی پروفایل کاربر
    }
}

2. استفاده از کلاس‌های درخواست (Form Request Classes)

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

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class RegisterRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:8|confirmed',
        ];
    }
}

روابط (Models and Relationships)

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

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

4. استفاده از Blade کامپوننت‌ها و بخش‌ها (Blade Components and Sections)

در قالب‌های Blade از کامپوننت‌ها و بخش‌ها استفاده کنید تا از تکرار کدهای HTML جلوگیری شود.

<!-- resources/views/components/alert.blade.php -->
<div class="alert alert-{{ $type }}">
    {{ $slot }}
</div>

<!-- استفاده در یک ویو -->
<x-alert type="error">
    خطایی رخ داده است!
</x-alert>


5. استفاده از توابع و کلاس‌های کمکی (Helper Functions and Classes)

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

// app/Helpers/helpers.php
if (! function_exists('formatDate')) {
    function formatDate($date)
    {
        return \Carbon\Carbon::parse($date)->format('Y-m-d');
    }
}


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

اصول برنامه نویسی بهینه:منطق برنامه باید در service class باشد

هر کنترلر باید یک وظیفه داشته باشد، بنابراین منطق برنامه را در service classes بنویسید. این یک رویکرد خوب برای طراحی برنامه‌های Laravel است. با قرار دادن منطق برنامه در کلاس‌های سرویس، شما مسئولیت‌های مربوط به منطق کسب و کار را از کنترلرها جدا می‌کنید و کدهای کنترلرها را ساده‌تر و قابل نگهداری‌تر می‌کنید.

در Laravel، کلاس‌های سرویس معمولاً در مسیر app/Services قرار می‌گیرند. این کلاس‌ها مسئولیت انجام عملیات مربوط به منطق کسب و کار را دارند. به عنوان مثال، شما می‌توانید یک کلاس سرویس برای مدیریت کاربران با نام UserService ایجاد کنید که شامل عملیات‌هایی مانند ثبت نام، ورود، به روزرسانی اطلاعات کاربری و … باشد.

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

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

روش نامناسب:

public function store(Request $request)
{
    if ($request->hasFile('image')) {
        $request->file('image')->move(public_path('images') . 'temp');
    }
    
    ...
}

روش بهتر:

public function store(Request $request)
{
    $this->articleService->handleUploadedImage($request->file('image'));

    ...
}

class ArticleService
{
    public function handleUploadedImage($image)
    {
        if (!is_null($image)) {
            $image->move(public_path('images') . 'temp');
        }
    }
}

 

اصول برنامه نویسی بهینه:اعتبارسنجی ها را در Request classes انجام دهید

اعتبارسنجی ها را در Request classes انجام دهید نه در controllers.

این یک رویکرد خوب برای پاک‌سازی کد و بهبود قابلیت نگهداری و تست است. با اعتبارسنجی را در کلاس‌های درخواست (Request classes) انجام دادن، مسئولیت‌های مربوط به اعتبارسنجی داده‌ها را از کنترلرها جدا می‌کنید و کدهای کنترلر را ساده‌تر و قابل مدیریت‌تر می‌کنید.

در لاراول، کلاس‌های درخواست معمولاً در مسیر app/Http/Requests قرار می‌گیرند. این کلاس‌ها مسئولیت اعتبارسنجی داده‌های درخواستی که توسط کاربران ارسال می‌شوند را دارند. به عنوان مثال، شما می‌توانید قوانین اعتبارسنجی مربوط به یک فرم ورود را در کلاسی به نام LoginRequest قرار دهید.

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

با این رویکرد، کنترلرها فقط مسئولیت انجام عملیات مربوط به برنامه (مانند استخراج داده‌های لازم از درخواست و انجام عملیات مربوطه بر روی آن) را دارند و اعتبارسنجی داده‌ها را به کلاس‌های درخواست منتقل می‌کنید. این باعث می‌شود کدهای کنترلرها ساده‌تر، قابل تست‌تر و قابل نگهداری‌تر باشند.

روش نادرست:

public function store(Request $request)
{
    $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'publish_at' => 'nullable|date',
    ]);

    ...
}

روش بهتر:

public function store(PostRequest $request)
{
    ...
}

class PostRequest extends Request
{
    public function rules()
    {
        return [
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
            'publish_at' => 'nullable|date',
        ];
    }
}

 

اصول برنامه نویسی بهینه: مدل های بزرگ،‌ کنترلرهای کوچک!

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

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

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

استفاده از الگوهای طراحی مانند Repository Pattern و Service Layer هم می‌تواند به شما کمک کند تا مدل‌ها را از کنترلرها جدا کرده و کد را سازماندهی و مدیریت بهتری دهید.

به طور کلی، رویکرد “مدل‌های بزرگ، کنترلرهای کوچک” بهبود قابلیت خوانایی، تست و توسعه سیستم‌های شما را تضمین می‌کند.

اگر از Query Builder یا raw SQL queries استفاده میکنید، تمام منطق پایگاه داده را در model ها یا Repository classes قرار بدهید.

روش اشتباه:

public function index()
{
    $clients = Client::verified()
        ->with(['orders' => function ($q) {
            $q->where('created_at', '>', Carbon::today()->subWeek());
        }])
        ->get();

    return view('index', ['clients' => $clients]);
}

روش بهتر:

public function index()
{
    return view('index', ['clients' => $this->client->getWithNewOrders()]);
}

class Client extends Model
{
    public function getWithNewOrders()
    {
        return $this->verified()
            ->with(['orders' => function ($q) {
                $q->where('created_at', '>', Carbon::today()->subWeek());
            }])
            ->get();
    }
}

 

اصول برنامه نویسی بهینه: اصل تک وظیفه ای بودن

اصل تک وظیفه‌ای بودن (Single Responsibility Principle یا SRP) از اصول اصلی برنامه‌نویسی شیءگراست که در فریم‌ورک لاراول نیز توصیه می‌شود. این اصل می‌گوید که هر کلاس یا قسمت از برنامه باید مسئولیت یک وظیفه خاص را داشته باشد و فقط در انجام آن وظیفه خاص تخصص داشته باشد.

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

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

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

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

روش اشتباه:

public function getFullNameAttribute(): string
{
    if (auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified()) {
        return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
    } else {
        return $this->first_name[0] . '. ' . $this->last_name;
    }
}

روش بهتر:

public function getFullNameAttribute(): string
{
    return $this->isVerifiedClient() ? $this->getFullNameLong() : $this->getFullNameShort();
}

public function isVerifiedClient(): bool
{
    return auth()->user() && auth()->user()->hasRole('client') && auth()->user()->isVerified();
}

public function getFullNameLong(): string
{
    return 'Mr. ' . $this->first_name . ' ' . $this->middle_name . ' ' . $this->last_name;
}

public function getFullNameShort(): string
{
    return $this->first_name[0] . '. ' . $this->last_name;
}