تغییرات اخیر

در اینجا اطلاعیه‌ها، نسخه‌ها و تغییرات جدید لیارا فهرست می‌شوند.

افزایش امنیت رمز عبور در جاوااسکریپت با استفاده از BcryptJS


۳ اسفند ۱۴۰۴

خلاصه کنید:

openaigeminiperplexity

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

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

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

آنچه در این مقاله می‌خوانید:

  • پیش نیازها
  • چرا از BcryptJS استفاده کنیم؟
  • هشینگ چگونه کار می‌کند؟
  • نصب BcryptJS و سایر ماژول‌های مورد نیاز
  • راه‌اندازی سرور با Express JS
  • رمزگذاری رمزها و ذخیره آن‌ها در پایگاه داده MongoDB
  • دسترسی به رمز عبور هش‌شده و استفاده از آن
  • جمع بندی

پیش نیازها

قبل از شروع این آموزش، مطمئن شوید که محیط توسعه‌ی شما آماده است و موارد زیر را در اختیار دارید:

  • نصب Node.js: روی سیستم خود باید نسخه‌ای پایدار از Node.js (نسخه‌ی ۱۲ یا بالاتر) نصب کرده باشید.
  • آشنایی با جاوااسکریپت: برای درک بهتر کدها و انجام مراحل این آموزش، لازم است با اصول اولیه‌ی برنامه‌نویسی در زبان JavaScript آشنا باشید.
  • نصب Express JS: برای ساخت سرور، به فریم‌ورک Express نیاز دارید. اگر تاکنون با Express کار نکرده‌اید، پیشنهاد می‌شود ابتدا با استفاده از راهنمای رسمی Express نحوه‌ی راه‌اندازی آن را یاد بگیرید.
  • نصب پایگاه داده MongoDB: در نهایت برای ذخیره‌ی داده‌ها، باید پایگاه داده‌ی MongoDB را نصب کنید.
افزایش امنیت رمز عبور در جاوااسکریپت با استفاده از BcryptJS

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

Bcrypt یک الگوریتم هش (Hashing Algorithm) است که برای تبدیل رمزهای عبور به هش‌های امن طراحی شده تا در صورت بروز رخنه‌ی اطلاعات (Data Breach)، رمزهای اصلی کاربران قابل بازیابی نباشند.

این الگوریتم با استفاده از saltهای تصادفی، امنیت رمزها را بسیار بالا می‌برد و شکستن آن با روش‌هایی مانند Brute Force (تلاش برای حدس‌زدن تمام حالت‌های ممکن) بسیار دشوار می‌کند.

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

برخی دلایل محبوبیت و برتری BcryptJS در زمینه‌ی امنیت رمز عبور عبارتند از:

  • امنیت بالا: BcryptJS از الگوریتم Bcrypt استفاده می‌کند که عمداً سرعت آن پایین طراحی شده است و این ویژگی خوبی برای امنیت است، زیرا اجرای هر فرآیند هش نیازمند توان محاسباتی زیاد است، شکستن آن برای مهاجم‌ها بسیار زمان‌بر و تقریباً غیرممکن می‌شود.
  • استفاده از Salt برای افزایش امنیت: این کتابخانه به‌صورت خودکار برای هر رمز عبور یک salt تصادفی تولید می‌کند. وجود Salt باعث می‌شود حتی رمزهای مشابه، هش‌های متفاوتی داشته باشند. در نتیجه، حتی اگر رمز ضعیفی انتخاب شود، فرآیند شکستن آن بسیار سخت‌تر خواهد بود.
  • سادگی در استفاده: یکی از مزایای بزرگ BcryptJS این است که توسعه‌دهندگان جاوااسکریپت می‌توانند رمزهای عبور را بدون نیاز به دانش عمیق رمزنگاری به‌صورت ایمن هش کنند.

در ادامه‌‍‌ی آموزش، با مفاهیم هش (Hash) و salt بیشتر آشنا می‌شویم تا درک عمیق‌تری از نخوه‌ی عملکرد امنیتی BcryptJS به‌دست بیاوریم.

همین حالا هاست ابری Python رو سفارش بدید و پروژه‌تون رو با سرعت بالا راه‌اندازی کنید!
✅ دامنه رایگان ✅ ترافیک نامحدود ✅ هزینه ساعتی
خرید هاست ابری Python

هشینگ چگونه کار می‌کند؟

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

هشینگ

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

ورودی داد، در مرحله اول، داده‌ای که می‌تواند از هر نوعی (باینری، کاراکتر، اعشاری و غیره) باشد به‌صورت متن ساده یا رشته ذخیره می‌شود.

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

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

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

سالتینگ

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

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

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

Password = ‘sammy’
Hash = £%$^&£!23!3%!!
Salt = 2vqw£4Df$%sdfk
Hash + Salt = £%$^&£!23!3%!!2vqw£4Df$%sdfk

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

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

هاست پایتون چیست؟ + راهنمای کامل خرید هاست Python
خرید هاست پایتون

نصب BcryptJS و سایر ماژول‌های مورد نیاز

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

ابتدا با ایجاد یک پروژه npm شروع می‌کنیم. مراحل زیر را دنبال کنید:

1- ایجاد پوشه و فایل: یک پوشه جدید باز کنید و در آن فایل app.js را ایجاد کنید.

2- باز کردن ترمینال: یک پنجره ترمینال در این پوشه باز کنید و دستور زیر را وارد کنید:

npm init

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

3- ایجاد فایل‌های اضافی: حالا ۳ فایل دیگر ایجاد کنید.

  • auth.js
  • db.js
  • User.js

4- نصب بسته‌های لازم: در همان پنجره ترمینال، دستور زیر را برای نصب بسته‌های مورد نیاز وارد کنید:

npm install express mongoose BcryptJS nodemon

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

راه‌اندازی سرور با Express JS

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

مرحله ۱ – ایجاد ارتباط با پایگاه‌داده MongoDB

برای اتصال به MongoDB، ما از نسخه کامپیوتر شخصی استفاده می‌کنیم. برای سازماندهی بهتر پروژه، کد تنظیم اتصال را در فایل db.js ذخیره خواهیم کرد.

const mongoose = require("mongoose");
const mongoURI = "mongodb://127.0.0.1:27017/bcrypt_database";

const connectMongo = async () => {
  try {
    await mongoose.connect(mongoURI, { useNewUrlParser: true, useUnifiedTopology: true });
    console.log("Connected to MongoDB!");
  } catch (error) {
    console.error("Error connecting to MongoDB: ", error.message);
  }
};

module.exports = connectMongo;

در اینجا، پکیج mongoose را وارد می‌کنیم که API‌ای برای اتصال جاوااسکریپت به MongoDB را فراهم می‌کند. همچنین، URI اتصال به یک نصب محلی از MongoDB است؛ اگر از یک پایگاه‌داده ابری (مانند Atlas) استفاده می‌کنید، فقط کافی است URI را به URI پایگاه‌داده خاص خود تغییر دهید.

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

تابع connectToMongo یک تابع async است زیرا mongoose.connect یک Promise جاوااسکریپت را بازمی‌گرداند. این تابع در صورت موفقیت اتصال، نتیجه موفقیت‌آمیز را برمی‌گرداند و در غیر این صورت با خطا مواجه خواهد شد.

در نهایت، از module.exports برای صادرات این تابع استفاده می‌کنیم تا هر بار که ماژول db.js وارد می‌شود، در دسترس باشد.

مرحله ۲ – ایجاد یک اسکیما برای کاربر

برای ایجاد یا احراز هویت کاربران با پایگاه‌داده MongoDB، به یک اسکیما پایه نیاز خواهید داشت. اگر نمی‌دانید اسکیما چیست، می‌توانید از این راهنمای مفید برای درک و ایجاد اسکیما در MongoDB استفاده کنید. ما تنها از دو فیلد برای اسکیما خود استفاده خواهیم کرد: email و password.

از کد زیر در ماژول User.js استفاده کنید:

const mongoose = require("mongoose");

const UserSchema = new mongoose.Schema({
    email: {
        type: String,
        required: true,
        unique: true
    },
    password: {
        type: String,
        required: true
    }
});

module.exports = mongoose.model('User', UserSchema);

در این اسکیما، دو فیلد به نام‌های email و password ایجاد می‌کنیم. هر دوی این فیلدها الزامی و از نوع رشته هستند. همچنین، فیلد email یک فیلد منحصر به فرد است به این معنی که هر ایمیل تنها یک بار می‌تواند برای ایجاد حساب کاربری استفاده شود.

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

می‌توانید با استفاده از تابع model() از کتابخانه mongoose یک اسکیما را به مدل تبدیل کنید.

مرحله ۳ – راه‌اندازی سرور در app.js

پس از دنبال کردن مراحل قبلی، شما با موفقیت یک مدل و یک ماژول برای برقراری اتصال به پایگاه‌داده MongoDB ایجاد کرده‌اید. حالا، بیایید سرور را راه‌اندازی کنیم. از کد زیر در فایل app.js استفاده کنید:

const connectToMongo = require("./db");
const express = require("express");
const app = express();

connectToMongo();

app.use(express.json());
app.use("/auth", require("./auth"));

const port = 3300;
app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}`);
});

در اینجا، ما ماژول‌های express و db.js را برای اتصال به MongoDB وارد می‌کنیم. سپس از middleware express.json() برای مدیریت پاسخ‌های JSON استفاده می‌کنیم. مسیرها در ماژول جداگانه‌ای به نام auth.js ایجاد می‌شوند تا کد تمیز و سازمان‌یافته باقی بماند. در نهایت، یک نقطه پایانی برای سرور ایجاد می‌کنیم تا بر روی پورت 3300 در localhost گوش دهد.

حالا شما سرور خود را با موفقیت راه‌اندازی کرده‌اید و آماده‌اید تا از BcryptJS برای ذخیره‌سازی و احراز هویت امن رمزهای عبور استفاده کنید!

راهنمای جامع استفاده از جستجوی شبکه‌ای Grid Search در پایتون python
جستجوی شبکه‌ای Grid Search در پایتون python

رمزنگاری رمزهای عبور و ذخیره آن‌ها در پایگاه‌داده MongoDB

در این مرحله، سرور تقریباً آماده است و اکنون شما دو endpoint برای سرور ایجاد خواهید کرد: signup و login. در endpoint signup، ما ایمیل و رمز عبور یک کاربر جدید را دریافت کرده و آن را با استفاده از BcryptJS به‌صورت رمزنگاری‌شده ذخیره می‌کنیم.

کد زیر را در فایل auth.js وارد کنید:

const express = require("express");
const router = express.Router();
const User = require("./User");
const bcrypt = require("bcryptjs");

// ROUTE 1:
router.post("/signup", async (req, res) => {
  try {
    const salt = await bcrypt.genSalt(10);
    const secPass = await bcrypt.hash(req.body.password, salt);
    
    let user = await User.create({
      email: req.body.email,
      password: secPass,
    });
    
    res.json({ user });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

module.exports = router;

تست endpoint signup:

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

cd <path to your project folder>
nodemon ./app.js

این دستور سرور شما را در localhost و پورت 3300 اجرا می‌کند (یا هر پورتی که مشخص کرده‌اید). سپس می‌توانید یک درخواست HTTP به URL http://localhost:3300/auth/signup با بدنه زیر ارسال کنید:

{
  "email": "sammy@digitalocean.com",
  "password": "sammy"
}

پاسخ خروجی به درخواست

این درخواست خروجی/پاسخ زیر را تولید خواهد کرد:

{
  "user": {
    "email": "sammy@digitalocean.com",
    "password": "$2a$10$JBka/WyJD0ohkzyu5Wu.JeCqQm33UIx/1xqIeNJ1AQI9kYZ0Gr0IS",
    "_id": "654510cd8f1edaa59a8bb589",
    "__v": 0
  }
}

نکته: هش رمز عبور و شناسه (ID) همیشه منحصر به فرد هستند و هر بار متفاوت خواهند بود.

در بخش بعدی، خواهید آموخت که چگونه به هش ذخیره شده برای رمز عبور دسترسی پیدا کرده و آن را با رمز عبور ارائه شده در حین ورود/احراز هویت اعتبارسنجی کنید.

برای دانستن نحوه اتصال به سرور مجازی با استفاده از WebSSH در پایتون Python، مقاله زیر را بخوانید.
نحوه اتصال به سرور مجازی

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

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

برای افزودن یک متد احراز هویت، کد زیر را به auth.js خود اضافه کنید، بعد از مسیر /signup:

کد جدید برای auth.js

// ROUTE 2:
router.post("/login", async (req, res) => {
  let user = await User.findOne({ email: req.body.email });
  if (!user) {
    return res.status(400).json({ error: "Login with proper credentials!" });
  }
  
  const passwordCompare = await bcrypt.compare(req.body.password, user.password);
  if (!passwordCompare) {
    return res
      .status(400)
      .json({ error: "Login with proper credentials!" });
  }
  
  res.json({ success: "Authenticated!" });
});

نهایی‌سازی کد auth.js:

شما در نهایت کد auth.js خود را به شکل زیر خواهید داشت:

const express = require("express");
const router = express.Router();
const User = require("./User");
const bcrypt = require("bcryptjs");

// ROUTE 1: Signup
router.post("/signup", async (req, res) => {
  const salt = await bcrypt.genSalt(10);
  const secPass = await bcrypt.hash(req.body.password, salt);
  let user = await User.create({
    email: req.body.email,
    password: secPass,
  });
  res.json({ user });
});

// ROUTE 2: Login
router.post("/login", async (req, res) => {
  let user = await User.findOne({ email: req.body.email });
  if (!user) {
    return res.status(400).json({ error: "Login with proper credentials!" });
  }
  const passwordCompare = await bcrypt.compare(req.body.password, user.password);
  if (!passwordCompare) {
    return res.status(400).json({ error: "Login with proper credentials!" });
  }
  res.json({ success: "Authenticated!" });
});

// Exporting router
module.exports = router;

تست endpoint /login

برای تست این endpoint، می‌توانید یک درخواست HTTP جدید به URL http://localhost:3300/auth/login با بدنه زیر ارسال کنید:

{
  "email": "sammy@digitalocean.com",
  "password": "sammy"
}

پاسخ خروجی به درخواست

این درخواست پاسخ زیر را برمی‌گرداند:

{
  "success": "Authenticated!"
}

با این کار، شما توانسته‌اید یک سیستم احراز هویت ساده با استفاده از BcryptJS برای رمزنگاری و بررسی رمزهای عبور در سرور خود ایجاد کنید!

جمع بندی

در این آموزش یک سرور را ایجاد کردیم تا استفاده از BcryptJS برای ذخیره‌سازی و دسترسی ایمن به رمزهای عبور پایگاه‌داده را توضیح دهیم.

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

به اشتراک بگذارید