לא מזמן נתקלתי בתכונה חמודה שנוספה לאובייקט net - המודול המובנה בnode.js לטיפול בתעבורת רשת.
האובייקט שנוסף נקרא BlockList, והוא מאפשר לשמור ולעדכן (באופן חד-כיווני) רשימה "שחורה" של מספרי IP או טווח של מספרים כאלו, בצמוד לאובייקט net.
ומה קורה כשמתקבלת בקשה בשרת ממספר שנמצא ברשימה הרשחורה? - טוב ששאלתם: כלום.. כלומר, קורה בדיוק מה שקורה כשמספר אחר שולח בקשה דומה.
לפי הנוסח המופיע באתר התיעוד, בתרגום חופשי, אפשר לנסח זאת כך: "האובייקט יכול לבוא לשימוש עם מערכות API כלשהן, כדי לבטל את האפשרות של חיבורים נכנסים או יוצאים עבור מספרי IP ספציפיים או טווח של מספרים".
אז איך זה יכול לעזור לנו לממש את החלומות על מערכת מפולטרת ומאובטחת שתמיד חלמנו לפתח?
אז זהו שלא ממש עוזר, אבל נחמד זה כן.
קוד בסיסי לדוגמה עבור המודול HTTP:
const http = require('http');
const net = require('net');
let server = http.createServer((req, res) => {
//console.log(`source ip: ${req.socket.remoteAddress} ,BlockList rules: ${res.socket.server.BlockList.rules} ,ip match rules: ${res.socket.server.BlockList.check(req.socket.remoteAddress)}`);
if (server.BlockList.check(req.socket.remoteAddress)) {
res.statusCode = 403;
return res.end();
}
res.end('hello FILTERED world!')
});
const BlockList = new net.BlockList();
BlockList.addAddress('127.0.0.1', 'ipv4');
server.BlockList = BlockList;
server.listen(8080, '0.0.0.0');
ובשימוש בexpress ניתן להוסיף middleware:
app.use((req, res, next) => {
if (req.socket.server.BlockList.check(req.socket.remoteAddress)) {
return res.status(403).end();
next();
});
כמה נקודות:
- עבור השימוש בתכונה יש צורך לעדכן את Node לגירסה 15.x באמצעות כלי האקרובטיקה האהוב עליכם.
- כדי שזה יעבוד, יש צורך להוסיף לפונקציה listen() מספר של host שמאזין לבקשות הנשלחות. אם לא תעשו את זה, אלא תשאירו את הפרמטר ריק, מה שיקרה הוא שמספרי IPV4 לא יתאימו לכללים שהוספתם, בגלל מנגנון שנקרא dual-stack support, שהופך מספרי IPV4 למשולבי IPV6 (ייתכן שזה באג, אבל זה המצב כעת).
- אם אתם משתמשים בnginx ודרכו הגישה לNode, בדרך כלל הגישה לIP של שולח הבקשה הוא דרך הheader x-forwarded-for.
- כפי שציינתי לפני כן, אין אפשרות להוציא מספר שנחסם מהרשימה. אבל סביר להניח שאפשר לפתור את זה איכשהו.. או אפילו לכתוב modoule חדש שיעשה בדיוק את זה, ולתרום אותו לאנושות.
- את החסימה בפועל תצטרכו לממש בעצמכם.
- בדוגמה שצורפה החסימה היא באמצעות הודעת שגיאה 403. אפשר גם להוסיף קאפצ'ה, ומנגנוני זיהוי לבוטים, ושמירה של המידע בDB לשימוש עתידי, ועוד ועוד כיד הדמיון..
לחסום את אלו שברשימה השחורה באופן "מקצועי", כלומר לחסום את האפשרות ליצור socket עם השרת (בשונה מהקמת סוקט ושליחת תוכן בתוך הסוקט, כשהתוכן כולל הודעת חסימה), באמצעות REJECT או DROP (מונחים שמתייחסים לתעבורת הTCP: שליחת הודעת ניתוק בעת ניסיון ליצור סוקט, או חוסר תגובה לניסיון כזה, בהתאמה) לא ניתן באמצעות הprocess של Node עצמו (וגם לא באמצעות python לדוגמה) בלי התממשקות עם מערכת ההפעלה באופן כלשהו (בlinux התממשקות עם netfilter, ובwin עם winsock), מכיון שמערכת ההפעלה היא זו שמקימה את החיבור, ומעבירה את השליטה בsocket שהוקם לnode לאחר שהחיבור כבר הוקם.
ניתן לקרוא בנושא למשל כאן.
אמנם עדיין נוכל לטרוק להם את הדלת בצורה לא נעימה בכלל, עם הקוד הזה:
req.socket.destroy()
למהדרין ניתן בקלות להוסיף קוד שיריץ פקודה בiptables (או netsh עבור WIN) שתבצע את החסימה ולא תאפשר ל"שחורים" לפתוח סוקט עם השרת, אבל שני פרטים כדאי לזכור:
- לא קיימת דרך (יותר נכון לא מוכרת לי כזו) שבה תקבלו מידע מהמערכת על IP שחור שמנסה לבצע גישה.
- הרשימה תישאר חסומה כל עוד לא תמחקו אותה (למהדרין מן המהדרין אולי אפשר לבצע אינטגרציה אוטמטית עם fail2ban כדי להוציא אותם אוטומטית משם, אבל סביר להניח שעדיף כבר להתממשק עם netfilter).
השאלות שנשארו פתוחות:
- האם זה לא משהו שיכולתם לממש בעצמכם?
- איזה שירותים לדוגמה מאפשרים את השימוש בתכונה הזו?
אשמח למידע בתגובות.
קישור לפרטים נוספים באתר הדוקו של Node.js
קישור למימוש הפנימי של התכונה בדף Node בgithub