حماية socket.io من السيرفر


التعليقات

تحقق من الرأس (header) Origin في طلبات WebSocket للتأكد من أن الطلبات تأتي من نطاقات موثوقة فقط.

const allowedOrigins = ["https://website.com"];


io.use((socket, next) => {
    const origin = socket.handshake.headers.origin;
    if (allowedOrigins.includes(origin)) {
        return next();
    }
    return next(new Error("Unauthorized"));
});

مع استخدام التحقق من التوكن لكونه أسلوب فعال لضمان أن الاتصالات تأتي من مستخدمين موثقين.

فعلى جانب العميل توليد وإرسال توكن عند إنشاء اتصال WebSocket.

socket = io("", {
    reconnection: true,
    closeOnBeforeunload: false,
    transports: trans,
    query: {
        token: "GENERATED_TOKEN"
    }
});

وعلى جانب الخادم التحقق من التوكن عند استلام الطلب.

io.use((socket, next) => {
    const token = socket.handshake.query.token;
    if (isValidToken(token)) {
        return next();
    }
    return next(new Error("Authentication error"));
});


function isValidToken(token) {
    // تحقق من صلاحية التوكن
    return token === "EXPECTED_TOKEN";
}

أيضًأ تحديد معدل الاتصالات يساعد في تجنب الهجمات التي تعتمد على إرسال طلبات لا نهائية.

const rateLimit = require("express-rate-limit");


const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, 
    max: 100, 
    message: "Too many requests, please try again later."
});


app.use("/socket.io", limiter);

بالطبع عليك تعديل بعض البيانات فيما سبق مثل التوكين والموقع وخلافه.

وكما ذكرت لك من قبل، استخدام جدار حماية لتطبيقات الويب (WAF) يوفر طبقة إضافية من الحماية ضد الهجمات الشائعة.

مع تحديد وقت الانتظار للاتصالات غير النشطة لتقليل الحمل على الخادم.

io.set('heartbeat timeout', 5000); // وقت الانتظار

وإذا أردت قم باستخدام reverse proxy مثل NGINX أو Apache لتمرير طلبات العملاء إلى الخادم الفعلي ويعيد الاستجابة إلى العملاء، أي يضيف طبقة إضافية من الحماية.

إن لم يكن لديك NGINX مثبتًا، تستطيع تثبيته باستخدام الأوامر التالية على لينكس:

sudo apt update
sudo apt install nginx

ثم إنشاء أو تعديل ملف تكوين NGINX لإعداد الوكيل العكسي، ويكون الملف في /etc/nginx/sites-available/ وباستطاعتك تسميته كما تشاء، مثلاً socketio_proxy.

server {
    listen 80;
    server_name your-website.com;


    location /socket.io/ {
        proxy_pass http://localhost:3000/socket.io/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }


    location / {
        proxy_pass http://localhost:3000/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

ثم تفعيل ملف التكوين الجديد وإعادة تشغيل خدمة NGINX لتطبيق التغييرات.

sudo ln -s /etc/nginx/sites-available/socketio_proxy /etc/nginx/sites-enabled/
sudo nginx -t # لاختبار التكوين والتأكد من عدم وجود أخطاء
sudo systemctl restart nginx

لاحظ proxy_pass http://localhost:3000/socke... بتوجيه طلبات Socket.IO إلى خادمك الفعلي الذي يعمل على المنفذ 3000، لذا عليك تعديل تلك الإعدادات.

و proxy_set_header يضمن تمرير الرؤوس الضرورية للحفاظ على الاتصال وترقية البروتوكول إلى WebSocket إذا لزم الأمر.

وتستطيع ضبطه لتصفية الطلبات غير المرغوب فيها أو الضارة قبل أن تصل إلى خادمك الفعلي، وذلك يشمل حظر عناوين IP معينة، أو تصفية الطلبات بناءًا على نمط معين.

http {
    # قائمة IPs محظورة
    deny 192.168.1.1;
    deny 203.0.113.0/24;


    # السماح لبقية العناوين
    allow all;
}

الهجمات الموزعة لحجب الخدمة تستهدف إغراق الخادم بعدد كبير من الطلبات، مما يؤدي إلى تعطيله، لكن NGINX يساعد في الحد من تأثير تلك الهجمات بعدة طرق، منها Rate Limiting أو Connection Limiting عليك ضبطه وفقًا لذلك.

الان عند تحقق من الرأس (header) Origin

وبعد ضبط الأكواد أصبح الموقع يعيد الإتصال ولا يتصل لا اعلم ما هو السبب

ربما لأنه لم ينشئ رمز token

أما بالنسبة الى

app.use("/socket.io", limiter);

لا يتم حظر طلبات socket ربما لأن المحدد هو equals وليس contains

حاول إذن استخدام التالي:

app.use("/socket.io/*", limiter)

وهل التوكن يتم توليده بشكل صحيح على جانب العميل ثم التحقق منه في جانب الخادم؟ أي توليد وإرسال توكن عند إنشاء اتصال WebSocket، ثم التحقق من التوكن على جانب الخادم.

جربت هكذا

app.use("/socket.io/*", limiter);

وهكذا

app.use("/socket.io/=*", limiter);

ولم تنجح أيضا

اما بالنسبة الى التوكن

لا يتم توليد token وإنما الذي يظهر في رابط wss هو GENERATED_TOKEN

وليس رمز توكن

لاحظ أن قيم مثل GENERATED_TOKEN هي placeholder لشرح الكود فقط أي عليك استبدالها بقيمة التوكن لديك

نعم فهمت الان ’ كل الشكر

قمت بإنشاء رمز توكين ونجحت الطريقة لك جزيلا الشكر اخ مصطفى


تطوير الويب

مجتمع لمناقشة وتبادل الخبرات حول تطوير الويب. ناقش أحدث التقنيات، اللغات، والأدوات في عالم تطوير المواقع والتطبيقات. شارك مشاريعك، اسأل عن نصائح، وتعاون مع مطورين محترفين وهواة.

90.4 ألف متابع