۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

با رمزگذاری قابل پرس و جو MongoDB و Node.js عمل کنید

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

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

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

بنابراین رمزگذاری قابل استعلام MongoDB ذخیره داده و زیرساخت آن را به عنوان اهداف حمله حذف می کند. این قابلیت شبه جادویی نیاز به پیکربندی اضافی برای برنامه ها دارد. این مقاله به شما نشان می دهد که چگونه یک محیط توسعه برای کار با رمزگذاری قابل پرس و جو MongoDB در یک برنامه Node.js راه اندازی کنید.

کلیدهای اصلی و رمزگذاری داده

دو نوع کلید در رمزگذاری قابل استعلام MongoDB استفاده می‌شود: کلید اصلی مشتری (CMK) و کلیدهای رمزگذاری داده (DEK). اینها با هم کار می کنند تا داده های شما را ایمن کنند. DEK برای رمزگذاری داده ها در پایگاه داده استفاده می شود، در حالی که CMK برای رمزگذاری و رمزگشایی DEK استفاده می شود و یک لایه حفاظتی اضافه می کند. CMK کلید حساس تر است. اگر CMK به خطر بیفتد، داده های شما در معرض خطر قرار می گیرند. اگر CMK یا DEK گم شود یا غیرقابل دسترسی باشد، برنامه مشتری قادر به رمزگشایی داده ها نخواهد بود.

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

یک CMK ایجاد کنید

اولین قدم این است که CMK خود را تولید کنید. می توانید این کار را با ابزار خط فرمان openssl انجام دهید، همانطور که در لیست ۱ نشان داده شده است.

فهرست ۱. یک کلید محلی ایجاد کنید

openssl rand 96 > cmk.txt

یک DEK ایجاد کنید

ما یک برنامه Node.js ساده برای مدیریت CMK خود ایجاد می کنیم، یک DEK ایجاد می کنیم و DEK را در یک مجموعه رمزگذاری شده ویژه در MongoDB به نام Key vault وارد می کنیم. رمزگذاری قابل پرسش، DEK رمزگذاری شده با CMK را در این مجموعه نگه می دارد. هنگام برقراری تماس با داده های برنامه رمزگذاری شده، برنامه مشتری شما DEK را از صندوق کلید بازیابی می کند، DEK را با CMK رمزگشایی می کند و سپس از DEK رمزگشایی شده برای تعامل با نمونه پایگاه داده استفاده می کند.

این رمزگذاری زیادی در حال انجام است، اما مجدداً، ایده این است که DEK را به واسطه CMK ایمن نگه دارید.

اسناد MongoDB نسخه کامل‌تری از برنامه مورد استفاده برای ایجاد جدول خزانه کلید DEK را ارائه می‌دهد اینجا. ما حداقل حداقل را ایجاد خواهیم کرد تا بتوانیم جنگل را از میان درختان ببینیم.

JetBrains از سرویس CI/CD برای تیم های کوچکتر رونمایی می کند

با تایپ npm init یک برنامه NPM جدید ایجاد کنید. شما می توانید تمام پیش فرض ها را بپذیرید. اکنون دو فایل js جدید به نام‌های make-dek.js و insert.js بسازید و خطوط را در فایل package.json مانند Listing 2 اضافه کنید.

فهرست ۲. اسکریپت makeDEK

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "makeDEK": "node ./make-dek.js",
    "insert": "node ./insert.js"
}

اکنون می‌توانید makeDEK.js را با وارد کردن npm run makeDEK و npm run insert در خط فرمان اجرا کنید. (اما این دستورات هنوز کاری انجام نمی دهند.)

افزودن وابستگی ها

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

فهرست ۳. وابستگی های MongoDB را اضافه کنید

npm install mongodb
npm install mongodb-client-encryption

MongoDB Atlas را راه اندازی کنید

ما از Atlas، سرویس مدیریت شده MongoDB، برای این آموزش استفاده خواهیم کرد. از زمان نگارش این مقاله، برای ایجاد یک خوشه MongoDB 6 در اطلس، به یک کلاستر اختصاصی سطح پولی نیاز دارید. (توجه داشته باشید که امکان استفاده از رمزگذاری قابل پرس و جو در حالت دستی با سرور انجمن MongoDB وجود دارد.)

می‌توانید یک حساب Atlas رایگان در اینجا ایجاد کنید. از آنجا به راحتی می توان خوشه را راه اندازی کرد (نام را به عنوان Cluster0 گذاشت) و یک کاربر با احراز هویت رمز عبور ایجاد کرد. فقط مطمئن شوید که “Dedicated Cluster” را هنگامی که آن انتخاب را دریافت کردید، انتخاب کنید.

توجه داشته باشید که کاربر برای انجام این مراحل باید نقش AtlasAdmin را داشته باشد. می‌توانید با رفتن به «دسترسی به پایگاه داده» در کنسول MongoDB و کلیک کردن روی «ویرایش» در کنار کاربر، نقش را بر روی کاربر تنظیم کنید. سپس در منوی کشویی Built-in Role، AtlasAdmin را انتخاب کنید.

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

لطفاً توجه داشته باشید (هشدار امنیتی):

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

افزودن کتابخانه رمز مشترک

MongoDB از دو سبک رمزگذاری قابل پرس و جو پشتیبانی می کند: خودکار و دستی. Auto ساده تر است و به مشتری MongoDB اجازه می دهد تا رمزگذاری را برای شما مذاکره کند. برای استفاده از خودکار، به کتابخانه رمزگذاری مشترک MongoDB در اینجا نیاز دارید. در منوی کشویی سمت راست، crypt_shared را انتخاب کنید، سیستم عامل خود را مشخص کنید و از آخرین نسخه مانند عکس صفحه ۱ استفاده کنید. (همچنین یک آدرس ایمیل برای پذیرش مجوز وارد خواهید کرد.)

عکس صفحه ۱. بسته crypt_shared را دانلود کنید

دخمه مشترک mongodb

اکنون آن فایل را در یک مکان مناسب قرار داده و آن را از حالت فشرده خارج کنید. در دایرکتوری ایجاد شده توسط استخراج، یک فایل /lib/mongo_crypt_v1.so را خواهید یافت. این همان چیزی است که ما به آن نیاز داریم. به مسیر توجه کنید زیرا بعداً وقتی را در فهرست ۴ و فهرست ۵ تنظیم کردیم، به آن نیاز خواهید داشت.

React 18 با رندر همزمان، دسته‌بندی خودکار وارد می‌شود

کد make-dek.js

اکنون آماده نوشتن کد فایل make-dek.js هستیم. این یک برنامه کوچک خواهد بود که مجموعه طاق کلید و خود مجموعه رمزگذاری شده را تنظیم می کند. این دو مجموعه به صورت پشت سر هم کار می کنند تا داده های پایدار، پرس و جو و بازیابی از مجموعه رمزگذاری شده را مدیریت کنند. (این موضوع در اسناد MongoDB با عمق بیشتری پوشش داده شده است.) محتویات make-dek.js در فهرست ۴ نشان داده شده است.

فهرست ۴. make-dek.js

const { MongoClient, Binary } = require("mongodb");
const { ClientEncryption } = require("mongodb-client-encryption");

const keyVaultNamespace = "encryption.__keyvault";
const secretDB = "secretDB";
const secretCollection = "secretCollection";
const uri = "mongodb+srv://<ATLAS_USERNAME>:<ATLAS_PASSWORD>@cluster0.444xyz.mongodb.net/?retryWrites=true&w=majority";

async function run() {
   const keyVaultClient = new MongoClient(uri);
   await keyVaultClient.connect();
   const keyVaultDB = keyVaultClient.db("encryption");
   await keyVaultDB.dropDatabase();
   const keyVaultColl = keyVaultDB.collection("__keyvault");
   await keyVaultColl.createIndex(
      { keyAltNames: 1 },
      { unique: true, partialFilterExpression: { keyAltNames: { $exists: true } } }
   );
   const localMasterKey = require("fs").readFileSync("./cmk.txt");
   const kmsProviders = { local: { key: localMasterKey } };
   const clientEnc = new ClientEncryption(keyVaultClient, {
      keyVaultNamespace: keyVaultNamespace,
      kmsProviders: kmsProviders
   });
   const dek = await clientEnc.createDataKey("local", { keyAltNames: ["dek"] });
   const encryptedFieldsMap = {
      ["secretDB.secretCollection"]: {
         fields: [
            {
               keyId: dek,
               path: "secretField",
               bsonType: "int",
               queries: { queryType: "equality" },
            }
         ]
      }
   };
   const extraOptions = { cryptSharedLibPath: "<MONGO_CRYPT_LIB_PATH>" };
   const encClient = new MongoClient(uri, {
      autoEncryption: {
         keyVaultNamespace,
         kmsProviders,
         extraOptions,
         encryptedFieldsMap
      }
   });

   await encClient.connect();
   const newEncDB = encClient.db(secretDB);
   await newEncDB.dropDatabase();
   await newEncDB.createCollection(secretCollection);
   await keyVaultClient.close();
   await encClient.close();
   console.log("Successfully created DEK and encrypted collection.");
}

run().catch(console.dir);

فهرست ۴ داستان دو مجموعه را بیان می کند: encryption.__keyvault و secretDB.secretCollection. این دو مجموعه با هم برای پشتیبانی از رمزگذاری قابل پرس و جو استفاده می شوند.

secretDB.secretCollection داده‌های کسب‌وکار واقعی را هتل می‌کند. مجموعه encryption.__keyvault کلیدهای رمزگذاری داده های رمزگذاری شده مورد استفاده در secretDB.secretCollection را نگه می دارد. دو MongoClient در حال استفاده است. سرویس گیرنده رمزگذاری شده (encClient) با DEK ایجاد شده توسط keyVaultClient رمزگذاری نشده پیکربندی می شود. DEK روی فیلد encryptedFieldsMap.keyId تنظیم شده است که برای پیکربندی encClient استفاده می‌شود.

encryptedFieldsMap اطلاعات بیشتری را برای سرویس گیرنده رمزگذاری شده encClient نگهداری می کند، که یک MongoClient استاندارد با کد autoEncrypted است. > میدان پر شده است. encryptedFieldsMap به مشتری می گوید که کدام فیلدها (با ویژگی path) رمزگذاری شده اند، در این مورد secretField. اگر ویژگی queries تنظیم نشده باشد، فیلد رمزگذاری می شود اما قابل پرس و جو نیست. از زمان نوشتن این مقاله، فقط برابری به عنوان queryType پشتیبانی می‌شود.

توجه کنید که یک شی ClientEncryption (clientEnc) برای تولید DEK استفاده می شود. شی clientEnc از keyVaultClient به همراه keyVaultNameSpace (encryption.__keyvault) و kmsProvider< استفاده می کند. /code>.

kmsProvider یک ارائه دهنده کلید محلی است که به عدد تصادفی که ما در خط فرمان تولید کرده ایم اشاره می کند. همچنین توسط autoEncryption که در سرویس گیرنده encClient تنظیم کرده ایم، استفاده می شود. (یادآوری: از kmsProvider محلی در تولید استفاده نکنید.)

درج و درخواست داده

اکنون زیرساختی برای درج و پرس و جو داده ها در secretDB.secretCollection.secretField در اختیار داریم. این کار با استفاده از کلیدهای encryption.__keyvault انجام می شود. فهرست ۵ یک مثال ساده از انجام این کار با دو فیلد ارائه می دهد، یک رشته رمزگذاری نشده در nonsecretField و یک int رمزگذاری شده در secretField< /code>.

فهرست ۵. درج و پرس و جو در زمینه های رمزگذاری شده و رمزگذاری نشده

const { MongoClient, Binary } = require("mongodb");

const localMasterKey = require("fs").readFileSync("./cmk.txt");
const kmsProviders = { local: { key: localMasterKey } };
const uri = "mongodb+srv://<ATLAS_USERNAME>:
<ATLAS_PASSWORD>@cluster0.444xyz.mongodb.net/?retryWrites=true&w=majority"

async function run() {
const unencryptedClient = new MongoClient(uri);
await unencryptedClient.connect();
const keyVaultClient = unencryptedClient.db("encryption").collection("__keyvault");
const dek = await keyVaultClient.findOne({ "keyAltNames": "dek" });

const encryptedFieldsMap = {
["secretDB.secretCollection"]: {
fields: [
{
keyId: dek._id,
path: "secretField",
bsonType: "int",
queries: { queryType: "equality" }
}
]
}
};
const extraOptions = { cryptSharedLibPath: "<MONGO_CRYPT_LIB_PATH>" };
const encryptedClient = new MongoClient(uri, {
autoEncryption: {
keyVaultNamespace: "encryption.__keyvault",
kmsProviders: kmsProviders,
extraOptions: extraOptions,
encryptedFieldsMap: encryptedFieldsMap
}
});
await encryptedClient.connect();
try {
const unencryptedColl = unencryptedClient.db("secretDB").collection("secretCollection");
const encryptedColl = encryptedClient.db("secretDB").collection("secretCollection");
await encryptedColl.insertOne({
secretField: 42,
nonsecretField: "What is the secret to life, the universe and everything?"
});
console.log(await unencryptedColl.findOne({ nonsecretField: /universe/ }));
console.log(await encryptedColl.findOne({ "secretField":42 })
);
} finally {
await unencryptedClient.close();
await encryptedClient.close();
}
}

run().catch(console.dir);

در فهرست ۵ ما دو کلاینت MongoDB ایجاد می کنیم، یک کلاینت رمزگذاری نشده و یک کلاینت رمزگذاری شده. با unencryptedClient به keyvault encryption.__keyvault که با make-dek.js در فهرست ۴ ایجاد کردیم دسترسی پیدا می کنیم و DEK را که در آنجا ذخیره کرده ایم بازیابی می کنیم. سپس از DEK برای ساختن encryptedFieldsMap استفاده می‌کنیم که تنظیمات مسیر، نوع و پرس‌وجوها را برای فیلد مخفی نیز در خود نگه می‌دارد.

در مرحله بعد، کلاینت رمزگذاری شده را ایجاد می کنیم و فضای نام صندوق پست (encryption.__keyvaultkmsProvider (که دوباره فایل محلی در cmk.txt است) را مشخص می کنیم. extraOptions به کتابخانه رمز مشترکی که ما از MongoDB دانلود کردیم و encryptedFieldsMap اشاره می کند.

سپس از encryptedClient برای درج در مجموعه secretDB.secretCollection با مجموعه secretField و nonsecretField استفاده می کنیم. به ترتیب به یک int و یک رشته.

در نهایت، داده ها را پرس و جو می کنیم. ابتدا از کلاینت رمزگذاری نشده استفاده می کنیم - این بار با اشاره به secretDB.secretCollection - برای پرس و جو با استفاده از nonsecretField و خروجی نتیجه. نتیجه نشان خواهد داد که secretField متن رمزی است، در حالی که nonsecretField متن ساده است. نکته اینجاست که کلاینت رمزگذاری نشده می تواند طبق معمول از فیلدهای معمولی پرس و جو کرده و استفاده کند.

سپس از کلاینت رمزگذاری شده برای پرس و جو در برابر secretField استفاده می شود، و هنگامی که آن نتیجه خروجی می شود، همه فیلدها از جمله secretField قابل مشاهده هستند. این نشان می دهد که encryptedClient به همه فیلدها دسترسی کامل دارد.

توجه داشته باشید که secretDB.secretCollection همچنین متادیتا را در فیلد __safeContent__ نگه می‌دارد. مطمئن شوید که این یا مجموعه طاق کلید را تغییر ندهید، در غیر این صورت ممکن است چیزها آنطور که انتظار می رود کار نکنند.

رمزگذاری که می توانید پرس و جو کنید

رمزگذاری قابل پرس و جو MongoDB نسبت به داده های رمزگذاری نشده یا حتی داده های رمزگذاری شده معمولی به تلاش بیشتری برای توسعه نیاز دارد، اما همچنین قابلیتی را امکان پذیر می کند که از طریق هیچ وسیله دیگری قابل دستیابی نیست: داده های پرس و جو که جدا از کلیدهای آن رمزگذاری شده اند. این یک سطح بالایی از امنیت را برای داده های حساس ارائه می دهد. برای شرکت هایی که هم به حداکثر امنیت داده و هم به قابلیت پرس و جو نیاز دارند، رمزگذاری قابل پرس و جو MongoDB ممکن است یک ویژگی ضروری باشد.