في هذا البرنامج التعليمي ، سنلقي نظرة على واجهة برمجة تطبيقات تشفير الويب : واجهة تشفير بيانات من جانب العميل. يعتمد هذا البرنامج التعليمي على هذه المقالة . من المفترض أنك معتاد إلى حد ما على التشفير.
ماذا سنفعل بالضبط؟ سنكتب خادمًا بسيطًا يقبل البيانات المشفرة من العميل ويعيدها عند الطلب. ستتم معالجة البيانات نفسها من جانب العميل.
سيتم تنفيذ الخادم في Node.js باستخدام Express ، العميل في JavaScript. سيتم استخدام Bootstrap للتصميم.
كود المشروع هنا .
إذا كنت مهتمًا ، من فضلك اتبعني.
تدريب
قم بإنشاء دليل
crypto-tut:
mkdir crypto-tut
ندخل فيه ونبدأ المشروع:
cd crypto-tut
npm init -y
التثبيت
express:
npm i express
التثبيت
nodemon:
npm i -D nodemon
التحرير
package.json:
"main": "server.js",
"scripts": {
"start": "nodemon"
},
هيكل المشروع:
crypto-tut
--node_modules
--src
--client.js
--index.html
--style.css
--package-lock.json
--package.json
--server.js
المحتويات
index.html:
<head>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<script src="client.js" defer></source>
</head>
<body>
<div class="container">
<h3>Web Cryptography API Tutorial</h3>
<input type="text" value="Hello, World!" class="form-control">
<div class="btn-box">
<button class="btn btn-primary btn-send">Send message</button>
<button class="btn btn-success btn-get" disabled>Get message</button>
</div>
<output></output>
</div>
</body>
المحتويات
style.css:
h3,
.btn-box {
margin: .5em;
text-align: center;
}
input,
output {
display: block;
margin: 1em auto;
text-align: center;
}
output span {
color: green;
}
الخادم
لنبدأ في إنشاء خادم.
نفتح
server.js.
نقوم بتوصيل سريع وإنشاء مثيلات للتطبيق وجهاز التوجيه:
const express = require('express')
const app = express()
const router = express.Router()
نربط البرمجيات الوسيطة (الطبقة المتوسطة بين الطلب والاستجابة):
//
app.use(express.json({
type: ['application/json', 'text/plain']
}))
//
app.use(router)
//
app.use(express.static('src'))
نقوم بإنشاء متغير لتخزين البيانات:
let data
نقوم بمعالجة استلام البيانات من العميل:
router.post('/secure-api', (req, res) => {
//
data = req.body
//
console.log(data)
//
res.end()
})
نقوم بمعالجة إرسال البيانات إلى العميل:
router.get('/secure-api', (req, res) => {
// JSON,
//
res.json(data)
})
نبدأ الخادم:
app.listen(3000, () => console.log('Server ready'))
نحن ننفذ الأمر
npm start. تعرض المحطة الرسالة "Server ready". الافتتاح http://localhost:3000:
هذا هو المكان الذي ننتهي فيه من الخادم ، انتقل إلى جانب العميل في التطبيق.
زبون
هنا يبدا المرح.
افتح الملف
client.js.
سيتم استخدام الخوارزمية المتماثلة AES-GCM لتشفير البيانات. تسمح هذه الخوارزميات باستخدام نفس المفتاح للتشفير وفك التشفير.
إنشاء وظيفة توليد المفاتيح المتماثلة:
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/generateKey
const generateKey = async () =>
window.crypto.subtle.generateKey({
name: 'AES-GCM',
length: 256,
}, true, ['encrypt', 'decrypt'])
يجب تشفير البيانات في دفق بايت قبل التشفير. يتم القيام بذلك بسهولة باستخدام فئة TextEncoder:
// https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
const encode = data => {
const encoder = new TextEncoder()
return encoder.encode(data)
}
بعد ذلك ، نحتاج إلى متجه تنفيذ (متجه التهيئة ، IV) ، وهو عبارة عن تسلسل عشوائي أو شبه عشوائي من الأحرف يُضاف إلى مفتاح التشفير لزيادة أمانه:
// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
const generateIv = () =>
// https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams
window.crypto.getRandomValues(new Uint8Array(12))
بعد إنشاء الوظائف المساعدة ، يمكننا تنفيذ وظيفة التشفير. يجب أن تعيد هذه الوظيفة تشفيرًا و IV بحيث يمكن فك التشفير لاحقًا:
const encrypt = async (data, key) => {
const encoded = encode(data)
const iv = generateIv()
const cipher = await window.crypto.subtle.encrypt({
name: 'AES-GCM',
iv
}, key, encoded)
return {
cipher,
iv
}
}
بعد تشفير البيانات باستخدام SubtleCrypto ، فهي عبارة عن مخازن مؤقتة للبيانات الثنائية الأولية. هذا ليس أفضل تنسيق للنقل والتخزين. دعونا نصلح هذا.
يتم عادةً إرسال البيانات بتنسيق JSON وتخزينها في قاعدة بيانات. لذلك ، من المنطقي تجميع البيانات في تنسيق محمول. تتمثل إحدى طرق القيام بذلك في تحويل البيانات إلى سلاسل base64:
// https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
const pack = buffer => window.btoa(
String.fromCharCode.apply(null, new Uint8Array(buffer))
)
بعد تلقي البيانات ، تحتاج إلى إجراء العملية العكسية ، أي تحويل سلاسل base64 المشفرة إلى مخازن ثنائية خام:
// https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
const unpack = packed => {
const string = window.atob(packed)
const buffer = new ArrayBuffer(string.length)
const bufferView = new Uint8Array(buffer)
for (let i = 0; i < string.length; i++) {
bufferView[i] = string.charCodeAt(i)
}
return buffer
}
يبقى لفك تشفير البيانات الواردة. ومع ذلك ، بعد فك التشفير ، نحتاج إلى فك تشفير دفق البايت إلى تنسيقه الأصلي. يمكن القيام بذلك باستخدام فئة TextDecoder:
// https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder
const decode = byteStream => {
const decoder = new TextDecoder()
return decoder.decode(byteStream)
}
وظيفة فك التشفير هي معكوس وظيفة التشفير:
// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/decrypt
const decrypt = async (cipher, key, iv) => {
const encoded = await window.crypto.subtle.decrypt({
name: 'AES-GCM',
iv
}, key, cipher)
return decode(encoded)
}
في هذه المرحلة ،
client.jsيبدو المحتوى كما يلي:
const generateKey = async () =>
window.crypto.subtle.generateKey({
name: 'AES-GCM',
length: 256,
}, true, ['encrypt', 'decrypt'])
const encode = data => {
const encoder = new TextEncoder()
return encoder.encode(data)
}
const generateIv = () =>
window.crypto.getRandomValues(new Uint8Array(12))
const encrypt = async (data, key) => {
const encoded = encode(data)
const iv = generateIv()
const cipher = await window.crypto.subtle.encrypt({
name: 'AES-GCM',
iv
}, key, encoded)
return {
cipher,
iv
}
}
const pack = buffer => window.btoa(
String.fromCharCode.apply(null, new Uint8Array(buffer))
)
const unpack = packed => {
const string = window.atob(packed)
const buffer = new ArrayBuffer(string.length)
const bufferView = new Uint8Array(buffer)
for (let i = 0; i < string.length; i++) {
bufferView[i] = string.charCodeAt(i)
}
return buffer
}
const decode = byteStream => {
const decoder = new TextDecoder()
return decoder.decode(byteStream)
}
const decrypt = async (cipher, key, iv) => {
const encoded = await window.crypto.subtle.decrypt({
name: 'AES-GCM',
iv
}, key, cipher)
return decode(encoded)
}
الآن دعونا ننفذ إرسال واستقبال البيانات.
نقوم بإنشاء المتغيرات:
// ,
const input = document.querySelector('input')
//
const output = document.querySelector('output')
//
let key
تشفير البيانات وإرسالها:
const encryptAndSendMsg = async () => {
const msg = input.value
//
key = await generateKey()
const {
cipher,
iv
} = await encrypt(msg, key)
//
await fetch('http://localhost:3000/secure-api', {
method: 'POST',
body: JSON.stringify({
cipher: pack(cipher),
iv: pack(iv)
})
})
output.innerHTML = ` <span>"${msg}"</span> .<br> .`
}
استلام وفك تشفير البيانات:
const getAndDecryptMsg = async () => {
const res = await fetch('http://localhost:3000/secure-api')
const data = await res.json()
//
console.log(data)
//
const msg = await decrypt(unpack(data.cipher), key, unpack(data.iv))
output.innerHTML = ` .<br> <span>"${msg}"</span> .`
}
نقرات زر التعامل:
document.querySelector('.btn-box').addEventListener('click', e => {
if (e.target.classList.contains('btn-send')) {
encryptAndSendMsg()
e.target.nextElementSibling.removeAttribute('disabled')
} else if (e.target.classList.contains('btn-get')) {
getAndDecryptMsg()
}
})
أعد تشغيل الخادم في حالة حدوث ذلك. نفتح
http://localhost:3000. انقر فوق الزر "إرسال رسالة":
نرى البيانات التي يتلقاها الخادم في الجهاز:
{
cipher: 'j8XqWlLIrFxyfA2easXkJTLLIt9x8zLHei/tTKI=',
iv: 'F8doVULJzbEQs3M1'
}
انقر فوق الزر "الحصول على رسالة":
نرى نفس البيانات التي تلقاها العميل في وحدة التحكم:
{
cipher: 'j8XqWlLIrFxyfA2easXkJTLLIt9x8zLHei/tTKI=',
iv: 'F8doVULJzbEQs3M1'
}
تفتح واجهة برمجة تطبيقات تشفير الويب فرصًا مثيرة للاهتمام بالنسبة لنا لحماية المعلومات السرية من جانب العميل. خطوة أخرى نحو تطوير الويب بدون خادم.
تبلغ نسبة دعم هذه التقنية حاليًا 96٪:
أتمنى أن تكون قد استمتعت بالمقال. شكرآ لك على أهتمامك.