نکات امنیتی در رابطه با Express.js برای نجات یک اپلیکیشن
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 12 دقیقه

نکات امنیتی در رابطه با Express.js برای نجات یک اپلیکیشن

7 مرحله زیر را انجام دهید تا برنامه‌تان شکست ناپذیر باشد! آیا تلفن همراه شما قفل دارد؟ آیا پین کد، رمز عبور، اثر انگشت یا FaceID دارید؟ 99 درصد احتمال دارد که شما این کار را می‌کنید و واضح است که چرا، چون به امنیت خود اهمیت می‌دهید. امروزه محافظت از تلفن همراه به اندازه سلامت بدن مهم است. اما برای محافظت از پروژههای Express.js هم می‌توان چنین کاری انجام داد؟

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

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

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

ممکن است بپرسید کاربرد Node JS Express در کسب و کار چیست و چه نوع شرکت‌هایی از آن استفاده می‌کنند؟ برای درک چگونگی عملکرد Node JS Express در واقعیت، Instapage یا Brinker International را در نظر بگیرید. همانطور که آنها نشان می‌دهند، مزیت Express.js که توسط توسعه دهندگان بیشترین ارزش را دارد، سادگی پیکربندی و سفارشی سازی آن است.

اما بیایید به موضوع خود برگردیم. به طور خلاصه 7 اقدام ساده برای امنیت داده‌ها وجود دارد:

  • از نسخه‌های معتبر Express.js بهره بگیرید
  • از اتصال مطمئن استفاده کنید
  • از کوکی‌های خود محافظت کنید
  • وابستگی‌های خود را ایمن کنید
  • ورود کاربران را تأیید کنید
  • از سیستم خود در برابر حملات جستجوی فراگیر (Brute Force) محافظت کنید
  • دسترسی کاربر را کنترل کنید

در ادامه نگاه دقیق‌تری به هرکدام خواهیم داشت.

1 – از نسخه‌های معتبر Express.js بهره بگیرید

موارد زیر را به خاطر داشته باشید. نسخه‌های منسوخ شده Node Express دیگر گزینه‌ای برای استفاده نیستند. هیچ یک از شرکت‌های تولید کننده نرم‌افزار دیگر با فریمورک‌های منسوخ شده کار نمی‌کنند. به عنوان مثال، نسخه‌های 2 و 3 امروزه پشتیبانی نمی‌شوند. این بدان معناست که در این نسخه‌ها مسائل امنیتی یا عملکردی برطرف نشده‌اند.

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

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

2 – از اتصال مطمئن استفاده کنید

برای ایمن سازی هدرهای HTTP، از Helmet.js استفاده کنید (یک ماژول مفید Node.js). این مجموعه از 13 عملکرد میان‌افزار برای تنظیم هدرهای پاسخ HTTP برخوردار است. همچنین توابعی برای تنظیم سیاست امنیت محتوا، رسیدگی به شفافیت گواهی‌ها، جلوگیری از دزدی کلیکی (Click Jacking)، غیرفعال کردن حافظه پنهان سمت کلاینت و افزودن برخی از محافظت‌های XSS را دارد.

npm install helmet --save

حتی اگر نمی‌خواهید از تمام عملکردهای امنیتی استفاده کنید، حداقل کاری که باید انجام دهید غیرفعال کردن هدر X-Powered By است:

app.disable('x-powered-by')

از این هدر می‌توان برای تشخیص اینکه آیا برنامه از Express بهره گرفته یا نه، استفاده کرد که به هکر اجازه می‌دهد حمله‌ای دقیق انجام دهد. مطمئنا هدر X-Powered-By تنها راه برای شناسایی یک برنامه Express-run نیست، اما رایج‌ترین و ساده ترین آن است.

برای محافظت از سیستم خود در برابر حملات HTTP parameter pollution، می‌توانید از HPP استفاده کنید. این میان‌افزار پارامترهایی مانند req.query و req.body را کنار می‌گذارد و به جای آن آخرین مقدار پارامتر را انتخاب می‌کند. دستور نصب به شرح زیر است:

npm install hpp --save

برای رمزنگاری داده‌ها که از کلاینت به سرور ارسال می‌شود، از پروتکل امن لایه انتقال (TLS) استفاده کنید. TLS یک پروتکل رمزنگاری برای ایمن سازی شبکه‌های رایانه‌ای است که از نسل رمزگذاری لایه امن سوکت (SSL) است. TLS را می‌توان با Nginx (یک سرور رایگان و کارآمد HTTP) و Let’s Encrypt (یک مجوز TLS رایگان) مدیریت کرد.

3 – از کوکی‌های خود محافظت کنید

در Express.js 4 دو ماژول کوکی وجود دارد:

  • express-session (در Express.js 3 ، express.session بود)
  • cookie-session (در Express.js 3، express.cookieSession بود)

ماژول express-session شناسه session را در کوکی و داده‌های session را در سرور ذخیره می‌کند. اما cookie-session تمام داده‌های session را در کوکی ذخیره می‌کند.

به طور کلی cookie-session کارآمدتر است. با این وجود، اگر داده‌های session که برای ذخیره سازی نیاز دارید پیچیده است و احتمال دارد بیش از 4096 بایت در هر کوکی باشد، از express-session استفاده کنید. دلیل دیگر استفاده از express-session این است که باید دادههای کوکی را برای کلاینت نامرئی نگه دارید.

علاوه بر این، باید گزینه‌های امنیتی کوکی را نیز تعیین کنید. یعنی:

  • secure
  • httpOnly
  • domain
  • path
  • expires

اگر "secure" روی "true" تنظیم شود، مرورگر فقط کوکی‌ها را از طریق HTTPS ارسال می‌کند. اگر "httpOnly" روی "true" تنظیم شود، کوکی نه از طریق کلاینت JS بلکه از طریقHTTP  ارسال می‌شود. مقدار "domain" دامنه کوکی را نشان می‌دهد. اگر دامنه کوکی با دامنه سرور مطابقت داشته باشد، "path" برای نشان دادن مسیر کوکی استفاده می‌شود. اگر مسیر کوکی با مسیر درخواستی مطابق باشد، کوکی در درخواست ارسال می‌شود. سرانجام همانطور که از نامش پیداست، مقدار "expires" نشان دهنده زمانی است که کوکی‌ها منقضی می‌شوند.

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

4 – وابستگی‌های خود را ایمن کنید

بدون شک npm ابزاری قدرتمند برای توسعه وب است. با این حال برای اطمینان از بالاترین سطح امنیت، فقط از نسخه 6 آن استفاده کنید (npm@6). موارد قدیمی ممکن است دارای برخی آسیب پذیری‌های شدید باشند که کل برنامه شما را به خطر می‌اندازد. همچنین برای تجزیه و تحلیل درخت وابستگی‌ها، از دستور زیر استفاده کنید:

npm audit

npm audit می‌تواند به رفع مشکلات واقعی پروژه شما کمک کند. به طوری که تمام وابستگی‌های شما را در dependency، devDependency، bundledDependency و optionalDependency بررسی می‌کند، اما در peerDependency اینگونه نیست. در اینجا می‌توانید همه آسیب پذیری‌های موجود در هر یک از پکیج‌های npm را بخوانید.

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

مرحله 1. نصب Snyk

npm install -g snyk
cd your-app

مرحله 2. اجرای تست

snyk test

مرحله 3. آشنایی با نحوه حل مشکل

snyk wizard

Wizard یک متد Snyk است که ماهیت آسیب پذیری وابستگی را توضیح می‌دهد و را‌حل‌هایی برای رفع آن ارائه می‌دهد.

5 – ورود کاربران را تایید کنید

کنترل ورود کاربر بخش بسیار مهمی برای توسعه سمت سرور است. این مسئله از اهمیت بیشتری نسبت به درخواست‌های غیر مجاز (در قسمت هفتم این مقاله شرح داده خواهد شد) برخوردار است.

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

به عنوان مثال، نابود کردن متدهای موجود در Loopback.js ORM (فریمورک Node.js) می‌تواند تمام داده‌های جدول پایگاه داده را از بین ببرد (وقتی با هیچ رکوردی مطابقت نداشته باشد، همه موارد را که در اینجا توضیح داده شده پاک می‌کند). تصور کنید فقط به این دلیل که اعتبارسنجی ورودی را نادیده گرفته‌اید، می‌توانید تمام داده‌ها را در یک جدول از دست بدهید.

  • برای بررسی‌های میانی از اعتبار سنجی body / object استفاده کنید

برای شروع می‌توانید از اعتبار سنجی body / object برای بازرسی‌های میانی استفاده کنید. در زیر می‌توانید مثال اعتبار سنجی ajv را در Express.JS ببینید که سریع‌ترین اعتبار سنجJSON Schema برای Node.js است.

const Ajv = require('ajv'); 
const ajv = new Ajv({allErrors: true}); 
const speaker = { 
  'type': 'object', 
  'required': [
    'id', 
    'name'
  ],
  'properties': { 
    'id': {
      'type': 'integer', 
    }, 
    'name': { 
      'type': 'string',
    }, 
  }, 
};
const conversation = { 
  type: 'object', 
  required: [
    'duration', 
    'monologues'
  ], 
  properties: { 
    duration: { 
      type: 'integer',
    }, 
    monologues: { 
      type: 'array', 
      items: monolog, 
    }, 
  }, 
};
const body = { 
  type: 'object', 
  required: [
    'speakers', 
    'conversations'
  ], 
  properties: { 
    speakers: { 
      type: 'array', 
      items: speaker, 
    }, 
    conversations: { 
      type: 'array', 
      items: conversation, 
    }, 
  }, 
}; 
const validate = ajv.compile(body); 
const isValidTranscriptBody = transcriptBody => { 
  const isValid = validate(transcriptBody);
  if (!isValid) { 
    console.error(validate.errors); 
  } 
  return isValid; 
};
  • خطاها را مدیریت کنید

حال تصور کنید فراموش کرده‌اید شی خاصی را بررسی کنید و با ویژگی تعریف نشده برخی عملیات را انجام دهید یا اینکه از کتابخانه خاصی استفاده می‌کنید و با خطا مواجه می‌شوید. این باعث خرابی در سرور می‌شود. سپس مهاجم می‌تواند نقطه پایانی خاصی را که این آسیب پذیری وجود دارد پینگ کند و سرور شما را برای مدت طولانی از کار بیندازد. ساده‌ترین راه برای مدیریت خطا استفاده از ساختار try-catch است:

try { 
  const data = body;
  if (data.length === 0) throw new Error('Client Error'); 
  const beacons = await  this.beaconLogService.filterBeacon(data); 
  if (beacons.length > 0) { 
    const max = beacons.reduce((prev, current) => (prev.rssi > current.rssi) ? prev : current); 
    await this.beaconLogService.save({ 
      ...max,
      userId: headers['x-uuid'] 
    }); 
    return { 
      data: { 
        status: 'Saved', 
        position: max 
      }, 
    }; 
  } 
  return { 
    data: { 
      status: 'Not valid object, 
    }, 
  }; 
} 
catch(err) { 
  this.logger.error(err.message, err.stack); 
  throw new HttpException('Server Error',     HttpStatus.INTERNAL_SERVER_ERROR); 
}

اطمینان حاصل کنید که از Error(‘message’) constructor جدیدی برای مدیریت خطا استفاده کنید یا حتی این کلاس را مطابق با اهداف خود توسعه دهید.

  • از JOI استفاده کنید

درس اصلی در اینجا این است که شما همیشه باید ورود کاربر را تأیید کنید تا قربانی حملات مرد میانی (man-in-the-middle) نشوید. روش دیگر برای انجام این کار کمک گرفتن از hapi/joi@ (بخشی از اکوسیستم hapi و یک کتابخانه تأیید اعتبار داده‌های JS) است.

در اینجا توجه کنید که ماژول joi منسوخ شده است. به همین دلیل استفاده از دستور زیر ممنوع است:

npm install joi

در ازای آن، از این یکی دستور استفاده کنید:

npm install @hapi/joi
  • از express-validator استفاده کنید

یک روش دیگر برای تأیید اعتبار ورودی کاربر، استفاده از express-validator (مجموعه‌ای از میان‌افزارهای Node Express شامل validator.js و تابع sanitizer) است. برای نصب آن دستور زیر را اجرا کنید:

npm install --save express-validator 
  • ورودی کاربر را پاکسازی کنید

به علاوه اقدام مهمی که باید انجام شود، پاکسازی ورودی کاربر برای محافظت از سیستم در برابر تزریق عملگرهای MongoDB است. برای این منظور، شما باید express-mongo-sanitize را نصب و استفاده کنید:

npm install express-mongo-sanitize
  • از برنامه خود در برابر CSRF محافظت کنید

CSRF زمانی انجام می‌شود که دستورات غیرمجاز از کاربر معتبری ارسال شود. بنابراین شما باید از برنامه خود در برابر جعل درخواست از سایت‌های دیگر (CSRF) محافظت کنید. این کار را می‌توانید با کمک csurf انجام دهید. قبل از آن باید مطمئن شوید که میان‌افزار session برای کوکی‌ها همانطور که قبلا در این مقاله توضیح داده شد، پیکربندی شده است. برای نصب این ماژول Node.js، دستور زیر را اجرا کنید:

npm install csurf

6 – از سیستم خود در برابر حملات جستجوی فراگیر (Brute Force) محافظت کنید

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

با کمک پکیج rate-limiter-flexible می‌توان از این نوع حملات جلوگیری کرد. این پکیج سریع، انعطاف‌پذیر و مناسب برای هر فریمورک Node است.

برای نصب، دستور زیر را اجرا کنید:

npm i --save rate-limiter-flexible
yarn add rate-limiter-flexible

این روش یک جایگزین ساده و ابتدایی دارد: express-rate-limit. تنها کاری که انجام می‌دهد محدود کردن درخواست‌های مکرر به API های عمومی یا بازنشانی رمز عبور است.

npm install --save express-rate-limit

7 – دسترسی کاربر را کنترل کنید

در میان روش‌های احراز هویت مواردی مانند توکن‌ها، Auth0 و JTW وجود دارند. بیایید روی مورد سوم تمرکز کنیم. JTW (JSON Web Tokens) برای انتقال داده‌های تأیید اعتبار در برنامه‌های کلاینت - سرور استفاده می‌شود. توکن‌ها توسط سرور ایجاد می‌شوند، با یک کلید مخفی امضا می‌شوند و به کلاینت منتقل می‌شوند. سپس کلاینت از این توکن‌ها برای احراز هویت استفاده می‌کند.

Express-jwt-permissions ابزاری است که همراه با express-jwt برای بررسی مجوزهای یک کد خاص استفاده می‌شود. این مجوزها مجموعه‌ای از رشته‌ها در داخل رمز هستند:

"permissions": [
  "status",
  "user:read",
  "user:write"
]

برای نصب این ابزار، دستور زیر را اجرا کنید:

npm install express-jwt-permissions --save

جمع‌بندی

در این مقاله، اقدامات ضروری امنیتی Node Express و برخی از ابزارهایی را که می‌توان در طول مسیر استفاده کرد را ذکر کردیم.

به یاد داشته باشید امروزه امنیت اطلاعات یکی از ارزش‌های اساسی است. من اکیدا توصیه می‌کنم مطمئن شوید که برنامه‌تان در برابر حملات مخرب مقاوم است. در غیر این صورت ممکن است ضرر و زیان قابل توجهی به کسب و کار و همچنین کاربرانتان وارد شود. امیدوارم که این مقاله مسیرتان را به سمت امنیت اطلاعات کمی ساده‌تر کرده باشد.

منبع

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
5 از 1 رای

/@erfanheshmati
عرفان حشمتی
Full-Stack Web Developer

کارشناس معماری سیستم های کامپیوتری، طراح و توسعه دهنده وب سایت، تولیدکننده محتوا

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید