مقدمه

امنیت داده‌ها یکی از مهم‌ترین دغدغه‌ها در توسعه وب است. Web Cryptography API یک رابط برنامه‌نویسی سطح پایین برای انجام عملیات رمزنگاری پایه در اپلیکیشن‌های وب است. این API به ما اجازه می‌دهد تا کارهایی مانند هش کردن داده‌ها برای تأیید صحت، تولید و مدیریت کلیدهای رمزنگاری، و رمزگذاری و رمزگشایی اطلاعات را مستقیماً در مرورگر و به صورت ایمن انجام دهیم.

این API به دلیل ماهیت حساس خود، تنها در بسترهای امن (HTTPS) قابل استفاده است. همچنین، استفاده صحیح از آن نیازمند درک مفاهیم پایه‌ای امنیت و رمزنگاری است، زیرا استفاده نادرست از الگوریتم‌ها می‌تواند منجر به آسیب‌پذیری‌های جدی شود.

تمام توابع و متدهای این API از طریق شیء crypto.subtle در دسترس هستند. نام subtle (به معنی ظریف و دقیق) به صورت عمدی انتخاب شده تا به توسعه‌دهندگان هشدار دهد که عملیات رمزنگاری دارای جزئیات و ظرافت‌های فراوانی است و اشتباهات کوچک در پیاده‌سازی می‌تواند کل سیستم امنیتی را بی‌اثر کند.

تمام متدهای موجود در crypto.subtle به صورت ناهمزمان (asynchronous) طراحی شده‌اند و یک Promise برمی‌گردانند. این طراحی تضمین می‌کند که عملیات رمزنگاری سنگین، ترِد اصلی مرورگر را مسدود نکرده و رابط کاربری روان باقی بماند.

هش کردن داده‌ها (Hashing)

هش کردن فرآیندی یک‌طرفه است که یک ورودی با هر اندازه‌ای را به یک خروجی با اندازه ثابت و منحصر به فرد (به نام «هش» یا «اثر انگشت») تبدیل می‌کند. از هش برای تأیید صحت و تمامیت داده‌ها استفاده می‌شود، بدون اینکه نیاز به دانستن خود داده اصلی باشد.

برای این کار از متد subtle.digest(algorithm, data) استفاده می‌کنیم.

Copy Icon JAVASCRIPT
async function hashString(text) {
    // 1. Encode the string to a Uint8Array
    const encoder = new TextEncoder();
    const data = encoder.encode(text);
    
    // 2. Hash the data using SHA-256
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    
    // 3. Convert the ArrayBuffer to a hex string for display
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    
    return hashHex;
}

hashString('Hello, Web Crypto!').then(console.log);

در این مثال، ابتدا رشته ورودی را با TextEncoder به داده باینری تبدیل می‌کنیم. سپس با الگوریتم SHA-256 آن را هش می‌کنیم. از آنجایی که خروجی یک ArrayBuffer است، برای نمایش آن را به یک رشته هگزادسیمال تبدیل می‌کنیم.

رمزگذاری و رمزگشایی (Encryption/Decryption)

برخلاف هش، رمزگذاری یک فرآیند دوطرفه است. داده‌ها با یک کلید به یک فرمت غیرقابل خواندن (ciphertext) تبدیل شده و سپس با همان کلید (در رمزنگاری متقارن) یا یک کلید دیگر (در رمزنگاری نامتقارن) به حالت اولیه بازگردانده می‌شوند. در این درس ما بر روی رمزنگاری متقارن با الگوریتم مدرن AES-GCM تمرکز می‌کنیم.

۱. تولید کلید

اولین قدم، تولید یک کلید امن است. این کار با متد subtle.generateKey() انجام می‌شود.

Copy Icon JAVASCRIPT
async function generateAesKey() {
    const key = await crypto.subtle.generateKey(
        { name: 'AES-GCM', length: 256 },
        true, // Whether the key is extractable
        ['encrypt', 'decrypt'] // Key usages
    );
    return key;
}

این تابع یک کلید ۲۵۶ بیتی برای الگوریتم AES-GCM تولید می‌کند که هم برای رمزگذاری و هم رمزگشایی قابل استفاده است.

۲. رمزگذاری و رمزگشایی

پس از داشتن کلید، می‌توانیم با متدهای subtle.encrypt() و subtle.decrypt() کار کنیم. الگوریتم AES-GCM نیازمند یک «بردار مقداردهی اولیه» یا Initialization Vector (IV) است. این مقدار باید برای هر عملیات رمزگذاری منحصر به فرد باشد، اما نیازی به مخفی نگه داشتن آن نیست و می‌توان آن را همراه با داده رمز شده ارسال کرد.

Copy Icon JAVASCRIPT
async function encryptAndDecrypt(plainText, key) {
    const encoder = new TextEncoder();
    const data = encoder.encode(plainText);
    const iv = crypto.getRandomValues(new Uint8Array(12));

    // Encrypt
    const encryptedBuffer = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, data);
    
    // Decrypt
    const decryptedBuffer = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, encryptedBuffer);
    
    const decoder = new TextDecoder();
    return decoder.decode(decryptedBuffer);
}

در این تابع، ابتدا یک iv تصادفی تولید می‌کنیم. سپس داده را با کلید و iv داده شده رمزگذاری می‌کنیم. در مرحله بعد، همان داده رمز شده را با همان کلید و iv رمزگشایی کرده و به رشته اولیه برمی‌گردانیم.

در این درس با مفاهیم پایه‌ای Web Cryptography API آشنا شدیم و دیدیم که چگونه می‌توان عملیات رمزنگاری سطح پایینی مانند هش و رمزگذاری را به صورت ایمن در مرورگر انجام داد. با این درس، فصل «APIهای جاوااسکریپت» به پایان می‌رسد. در این فصل گسترده، با طیف وسیعی از APIهای مدرن مرورگر، از کار با کلیپ‌بورد گرفته تا کامپوننت‌های وب و رمزنگاری، آشنا شدیم. در فصل بعدی، به یکی از جنبه‌های ضروری برنامه‌نویسی یعنی «مدیریت خطا و دیباگ کدها» خواهیم پرداخت.