اهمیت تأیید امضای وب هوک

Webhooks یک تکنیک ادغام پاسخ به تماس برای ارسال و دریافت اطلاعات، مانند اعلان رویدادها، در زمان واقعی است. Webhook ها می توانند توسط رویدادهای برنامه فعال شوند و داده ها را از طریق HTTP به برنامه دیگر یا API شخص ثالث منتقل کنند. میتوانید URL وبی هوک را پیکربندی کنید و شرکتکنندگان خارجی را برای سفارشیسازی، گسترش یا تغییر گردشهای کاری متصل کنید.
وب هوک ها ممکن است امضا شوند یا نباشند. با این حال، برای اهداف امنیتی، بهتر است یک امضای قابل تأیید اضافه کنید تا شنونده بتواند تأیید کند که درخواست از منبع مورد انتظار وب هوک آمده است.
در اینجا چند نمونه از نحوه استفاده از وبکهک ها امروزه آورده شده است:
- اعلان ها در یک پلت فرم کنترل نسخه مبتنی بر git زمانی که یک توسعه دهنده کد را به مخزن فشار داده است.
- اعلانهایی برای زمانی که کاربر به یک رشته پیام در یک پلت فرم پیامرسانی پاسخ میدهد.
- اعلانهای یک سرویس پرداخت که تأیید میکند یک روش پرداخت برای خرید خردهفروشی مجاز است.
بر اساس گزارش Verizon، حملات زنجیره تامین عامل 62 درصد از کل حوادث نفوذ به سیستم در سال 2022 بوده است. در طول چرخه عمر توسعه نرم افزار، حفظ امنیت همه اجزا و اقدامات درگیر در توسعه و استقرار بسیار مهم است. اگر آسیبپذیریها مورد سوء استفاده قرار گیرند، میتوانند شرکای درگیر را به خطر بیندازند و کل زنجیره تامین نرمافزار را مختل کنند.
برای مقابله با این تهدیدها و حفظ امنیت زنجیره تامین، فروشندگان زنجیره تامین، امضای وب هوک را اجرا می کنند. برای مثال، با CircleCI میتوانید از امضای وبهوک برای تأیید یک راز استفاده کنید و مطمئن شوید که فقط CircleCI – و نه یک بازیگر مخرب – با وبهوک شما تماس میگیرد. Travis CI همچنین یک هدر HTTP امضا را در فرآیند تأیید وب هوک خود ارائه می دهد.
در این راهنما، یک وب هوک GitHub را در Node.js پیادهسازی میکنیم که تشخیص میدهد کاربران چه زمانی کد را به یک مخزن فشار میدهند.
پیش نیازها
برای پیگیری، باید موارد زیر را انجام دهید:
- Node.js و npm را دانلود و نصب کنید.
- ngrok را نصب کنید، یک ابزار پروکسی معکوس برای باز کردن یک تونل امن از یک URL عمومی به برنامه محلی Node.js.
- Postman را نصب کنید، پلتفرمی که به شما امکان می دهد درخواست های HTTP ایجاد کنید و نتایج را به راحتی بررسی کنید.
تأیید امضای وب هوک با استفاده از Node.js
یک پوشه جدید در ماشین محلی خود ایجاد کنید و یک فایل جدید به نام webhook.js
با کد زیر به یاد داشته باشید که مقدار را جایگزین کنید repo
ثابت با مسیر مخزن محلی شما:
const http = require('http');
const exec = require('child_process').exec;
const repo = "C:\\Users\\your-user-name\\Documents\\GitHub\\webhook-test";
http
.createServer((req, res) => {
req.on('data', chunk => {
const body = JSON.parse(chunk);
console.log(body);
const isMain = body?.ref === 'refs/heads/main';
if (isMain) {
try {
console.log('Push event detected. Pulling repository updates...');
exec(`cd ${repo} && git fetch && git pull`);
console.log('Git repository pulled successfully.');
} catch (error) {
console.log(error);
}
}
res.end();
});
})
.listen(8080);
اکنون خط زیر را در ترمینال خود اجرا کنید:
node webhook.js
در اینجا چیزی است که ما در حال تلاش برای انجام آن هستیم webhook.js
فایل:
- را
repo
konstant مسیر پوشه حاوی مخزن مورد نظر ما را ذخیره می کند. - را
createServer
تابع یک سرور HTTP را در دستگاه ما در پورت 8080 راه اندازی می کند. برنامه webhook اکنون در حال اجرا است و آماده دریافت درخواست ها در نقطه پایانی http://localhost:8080 است. - پس از رسیدگی به درخواست ورودی، بدنه ثابت بخش عمده ای از درخواست را ذخیره می کند. درخواست به نقطه پایانی ما به این معنی است که ما یک اعلان از وب هوک GitHub به آدرس اینترنتی بارگیری محلی خود در http://localhost:8080 دریافت میکنیم که به ما اطلاع میدهد که برخی از کاربران کد را به مخزنی که در حال تماشای آن هستیم ارسال کرده است.
- ما می خواهیم اطمینان حاصل کنیم که فشار به طور خاص به شعبه اصلی انجام شده است. برای تأیید آن، دادههای JSON درخواستی را در یک شی بدنه تجزیه میکنیم و از آن استفاده میکنیم
isMain
ثابت برای نشان دادن اینکه آیا مرجع با شاخه اصلی مطابقت دارد یا خیر. - اگر درخواست پذیرفته شد، دستوری را برای تغییر به مخزن محلی اجرا می کنیم. در نهایت، ما را اجرا می کنیم
git fetch
وgit pull
دستور به روز رسانی آن را می دهد.
شبیه سازی یک درخواست مخرب با Postman
ما در حال طراحی برنامه Node.js برای کار با GitHub هستیم. با این حال، قبل از استفاده از GitHub، بیایید یک درخواست مخرب را با Postman شبیه سازی کنیم تا نشان دهیم برنامه ما چقدر آسیب پذیر است. این به شما کمک می کند تا اهمیت امضا را درک کنید.
برنامه Postman را باز کنید و یک درخواست HTTP POST جدید را در URL بارگیری محلی ما http://localhost:8080 پیکربندی کنید.
در بدن تب، کد زیر را اضافه کنید:
{
"ref": "refs/heads/main",
"comment": "hi, I'm a malicious request..."
}
اصابت ارسال. ترمینال خود را در جایی که Node.js در حال اجرا است باز کنید و توجه داشته باشید که چه اتفاقی افتاده است:
همانطور که می بینید، برنامه Node.js درخواستی را از منبعی غیر از GitHub می پذیرد که خطرناک است. ما باید امضاهای وب هوک را در کد خود پیاده سازی کنیم تا بتوانیم تأیید کنیم که آیا درخواست کننده قانونی است یا خیر.
پیکربندی ngrok
در این سناریو، ما برنامه Node.js را به صورت محلی اجرا می کنیم. با این حال، GitHub نمیتواند برای ارسال درخواست webhook به نقطه پایانی http://localhost:8080 در دستگاه شما برسد. ما می توانیم با انتشار برنامه Node.js در یک سرور عمومی این مشکل را حل کنیم. با این حال، اجرا و اشکال زدایی Node.js در ماشین توسعه آسان تر از استقرار آن در وب سرور یا ابر است، بنابراین ما از ngrok استفاده خواهیم کرد. ngrok یک ابزار پروکسی معکوس است که یک تونل امن را از یک URL عمومی تصادفی به نقطه پایانی وب هوک محلی شما باز می کند.
یک پنجره ترمینال در دستگاه خود باز کنید و دستور ngrok زیر را برای نمایش پورت محلی 8080 اجرا کنید:
ngrok http 8080
ngrok تأیید می کند که پورت 8080 محلی شما را از طریق یک تونل امن با یک URL عمومی تصادفی در معرض دید قرار داده است:
Forwarding https://<>.sa.ngrok.io -> http://localhost:8080
در مرحله بعد، از زیر دامنه عمومی ngrok در بالا برای پیکربندی یک وب هوک GitHub استفاده می کنیم.
پیکربندی یک وب هوک GitHub
از GitHub بازدید کنید تا یک صفحه مخزن جدید ایجاد کنید و یک مخزن جدید برای پیکربندی وب هوک اضافه کنید:
کلیک تنظیمات در نوار بالایی سپس، را انتخاب کنید وب هوک ها در لیست سمت چپ عمل کرده و کلیک کنید اضافه کردن وب هوک:
این باز می شود اضافه کردن وب هوک فرم، که در آن یک وب هوک را برای رویدادی که در آن یک توسعه دهنده کد را به مخزن فشار داده است، پیکربندی می کنیم.
اکنون فرم را به صورت زیر پر کنید:
- در URL بارگیری آدرس ngrok را نسبت به نقطه پایانی برنامه محلی Node.js خود وارد کنید.
- در نوع محتوا در فیلد، “application/json” را انتخاب کنید تا نشان دهید که این نوع محتوایی است که GitHub به نقطه پایانی ارسال می کند.
- در راز فیلد، “some-webhook-secret” را تایپ کنید. به یاد داشته باشید که در تولید، به گذرواژه ها یا عبارات قوی تری برای اعمال امنیت نیاز دارید.
- زیر کدام رویدادها میخواهید این وبقلاب را فعال کنند، انتخاب کنید فقط رویداد فشار. با این کار، وبکهاب GitHub برای یک عمل فشار مخزن تنظیم میشود که میتوانید برای آزمایش تأیید آن را فعال کنید.
توجه داشته باشید: همیشه توصیه می شود که تأیید لایه سوکت ایمن (SSL) را در پیکربندی وب هوک فعال کنید. با فعال کردن تأیید SSL، سازمان ها اطمینان حاصل می کنند که داده های منتقل شده بین سیستم ها ایمن هستند و نمی توانند توسط اشخاص غیرمجاز رهگیری شوند.
پس از پر کردن فرم، کلیک کنید اضافه کردن وب هوک.
ایمن سازی وب هوک
ما باید ماژول رمزنگاری Node.js مورد نیاز را برای مدیریت داده های رمزگذاری شده اضافه کنیم. این خط را در بلوک اول اضافه کنید webhook.js
فایل:
const crypto = require('crypto');
در مرحله بعد، یک ثابت را برای نگه داشتن همان رشته مخفی که در وب هوک GitHub خود پیکربندی کرده اید، اعلام کنید:
const secret="some-webhook-secret";
توجه داشته باشید: هرگز توصیه نمی شود که اسرار خود را به صورت سخت کدگذاری کنید. این روش فقط برای اهداف نمایشی است.
در ابتدای req.on
تابع، برای حفظ مقدار امضای محاسبهشده SHA256 ایجاد شده با HMAC، یک ثابت اعلام میکنیم. امضای HMAC-SHA256 را با اجرای یک تابع هش رمزنگاری SHA256 روی کلید مخفی مشترک و مقدار ارسال شده توسط درخواست GitHub (در اینجا توسط chunk
پارامتر):
req.on('data', chunk => {
const hashAlgorithm = 'sha256'
const signature = Buffer.from(req.headers['x-hub-signature-256'] || '', 'utf8')
const hmac = crypto.createHmac(hashAlgorithm, secret)
const digest = Buffer.from(hashAlgorithm + '=' + hmac.update(chunk).digest('hex'), 'utf8')
بعد، ما اعلام می کنیم isAllowed
ثابت در داخل req.on
رویداد برای نشان دادن اینکه آیا هدر x-hub-signature-256 با امضای محاسبه شده مطابقت دارد یا خیر. استفاده کنید crypto.timingSafeEqual
تابع، که دو متغیر را بدون افشای اطلاعات زمان بندی مقایسه می کند که می تواند به مهاجم کمک کند تا یکی از مقادیر را حدس بزند:
const isAllowed = (signature.length === digest.length && crypto.timingSafeEqual(digest, signature));
if (!isAllowed) {
const msg = 'Webhook signature does not match the hash of the payload';
console.log(msg);
res.writeHead(401)
res.end(msg)
return;
}
در کد بالا، تطابق به این معنی است که میتوانیم مطمئن باشیم که درخواست از سوی GitHub میآید و نه یک بازیگر مخرب. اگر درخواست از GitHub دریافت نشود، یک پاسخ غیرمجاز HTTP 401 برمیگرداند.
در اینجا برخی از بهترین شیوه های اعمال شده در این کد وجود دارد که باید هنگام توسعه وب هوک های خود از آنها استفاده کنید:
- هنگام انجام عملیات رشته برای محاسبه امضاهای وب هوک، همیشه محموله را به صورت UTF-8 در نظر بگیرید، زیرا محموله وب هوک ممکن است حاوی کاراکترهای یونیکد باشد. علاوه بر این، اگر انتظاراتی از نحوه نمایش داده های خود دارید، می توانید یک روش اعتبار سنجی را برای اطمینان از اینکه داده های دریافتی شما همانطور که انتظار می رود ظاهر می شوند، پیاده سازی کنید.
- برخی از ارائه دهندگان وب هوک (مانند GitHub) ممکن است شامل یک
x-hub-signature
سربرگ در درخواست ها راx-hub-signature
هدر با استفاده از SHA-1 ایجاد میشود، که یک الگوریتم هش ضعیف در نظر گرفته میشود، اما فقط برای سازگاری با عقب در نظر گرفته شده است. در صورت امکان، ازx-hub-signature-256
هدر، که با استفاده از الگوریتم قوی SHA-256 تولید می شود. - امضای هش را با
"sha256="
با استفاده از راز بدنه بارگذاری درخواست. - به جای استفاده از عملگر مساوی ساده (
"=="
یا"==="
، از یک روش مقایسه ایمن استفاده کنید که احتمال حملات زمان بندی را کاهش می دهد. همه زبان ها و پلتفرم های مدرن حداقل یکی از این روش های ایمن را دارند. در این پروژه Node.js، ما ازcrypto.timingSafeEqual
تابع.
میتوانید کد webhook.js کامل را از اینجا دریافت کنید.
برای اطلاعات دقیق تر، به مستندات GitHub در مورد ایمن سازی وبکهوک ها مراجعه کنید.
تست نقطه پایانی وب هوک ایمن
از آنجایی که کد را تغییر دادید، باید سرور Node محلی خود را مجددا راه اندازی کنید. در کنسول خود، سرور گره را با Ctrl+C (یا Command+C در مک) متوقف کنید و اجرا کنید node webhook.js
دوباره دستور دهید
سپس، به برنامه Postman برگردید و درخواست را دوباره ارسال کنید. اکنون، برنامه Node.js با یک کد غیرمجاز HTTP 401 پاسخ می دهد:
GitHub را باز کنید و یک فایل README.md اضافه کنید اگر قبلا این کار را نکرده اید، یا اگر دارید آن را باز کنید. سپس متن را به صورت زیر اضافه یا تغییر دهید:
در نهایت کلیک کنید تغییرات را متعهد شوید برای فشار دادن تغییرات به شاخه اصلی مخزن:
اکنون به ترمینال Node.js خود برگردید تا تأیید کنید که مخزن با موفقیت واکشی شده و کشیده شده است:
نتیجه
وب هوک ها امکان برقراری ارتباط بین ابزارها را فراهم می کند و در زمان وقوع رویدادها، داده های بلادرنگ را فراهم می کند. میتوانید آنها را در سیستمها ادغام کنید تا گردشهای کاری مشترک انعطافپذیر و گسترده ایجاد کنید، که در هنگام استفاده از API با پارامترهای از پیش تعریفشده امکانپذیر نیست.
از آنجایی که وب هوک ها از نظر طراحی باز هستند، در برابر تهدیدات امنیتی آسیب پذیر هستند، بنابراین اتخاذ تدابیر مناسب برای جلوگیری از حملات زنجیره تامین ضروری است.
امضای وب هوک راه حل پیشنهادی است زیرا ارائه دهندگان وب هوک می توانند اسرار را پیکربندی کنند و با استفاده از الگوریتم های هش قوی امضا تولید کنند. امضاها روشی ساده و ایمن برای اطمینان از اینکه ارتباط دادهای بین وبقلابها فقط بین شرکای تأیید شده و مورد اعتماد انجام میشود، هستند.