flawopen.com/ssrf/Javascript

● CWE-918 · उच्च
सुरक्षा अनुसंधान · FlawOpen

सुरक्षा भेद्यता: Server-Side Request Forgery in JavaScript

How axios and fetch allow internal network scanning and cloud metadata theft, and how to implement safe lookup agents in Node.js.

💡 आसान भाषा में (ELI5)

कल्पना करें कि आप एक सहायक को सार्वजनिक दुकान से पार्सल लाने के लिए भेजते हैं, लेकिन उसे प्रबंधक के कार्यालय में रखी बंद तिजोरी का पता देते हैं। चूंकि सहायक के पास आंतरिक सुरक्षा बैज है, वह तिजोरी खोलकर कंपनी के रहस्य आपको सौंप देता है।

इस पेज के मुख्य शब्द

Web Application Security
सुरक्षा अवधारणा (Web Application Security): Core architecture component affected by CWE-918.
CWE-918
सुरक्षा अवधारणा (CWE-918): Standard Common Weakness Enumeration classification for ssrf-javascript.
Defense-in-Depth
सुरक्षा अवधारणा (Defense-in-Depth): Multi-layered engineering verification and runtime boundary isolation.

हमले का चरण-दर-चरण प्रवाह

Step 1

अविश्वसनीय रिमोट URL इनपुट

एप्लिकेशन इमेज फ़ेचिंग, वेबहुक या पीडीएफ़ जनरेशन के लिए उपयोगकर्ता द्वारा दिया गया URL स्वीकार करता है।

Step 2

आंतरिक नेटवर्क एड्रेसिंग

हमलावर क्लाउड मेटाडेटा सेवा (उदा. http://169.254.169.254/latest/meta-data/) या लूपबैक एड्रेस इनपुट करता है।

Step 3

अनियंत्रित सॉकेट आउटबाउंड कनेक्शन

सर्वर बिना आईपी सत्यापन के आंतरिक वीपीसी नेटवर्क से अनुरोध भेज देता है।

Step 4

क्लाउड क्रेडेंशियल चोरी

आंतरिक मेटाडेटा सेवा सर्वर के अनुरोध पर विश्वास करके अस्थायी आईएएम टोकन और गुप्त कुंजियाँ लौटा देती है।

सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन

✕ कमज़ोर कार्यान्वयन
// VULNERABLE: Naively fetching user URL with axios
const express = require('express');
const axios = require('axios');

const app = express();

app.get('/proxy-image', async (req, res) => {
  const imageUrl = req.query.url;
  // Attacker input: http://169.254.169.254/latest/meta-data/
  try {
    const response = await axios.get(imageUrl);
    res.send(response.data);
  } catch (err) {
    res.status(500).send('Fetch error');
  }
});
✓ सुरक्षित और सुदृढ़ फ़िक्स
// HARDENED: Custom DNS lookup agent blocking private & metadata IP ranges
const express = require('express');
const http = require('http');
const https = require('https');
const dns = require('dns');
const ipaddr = require('ipaddr.js'); // npm i ipaddr.js
const axios = require('axios');

const app = express();

function safeLookup(hostname, options, callback) {
  dns.lookup(hostname, options, (err, address, family) => {
    if (err) return callback(err);

    try {
      const addr = ipaddr.parse(address);
      const range = addr.range();

      // Deny private, loopback, link-local, and reserved ranges
      const blockedRanges = ['loopback', 'private', 'linkLocal', 'carrierGradeNat', 'reserved'];
      if (blockedRanges.includes(range)) {
        return callback(new Error(`SSRF Blocked: Destination IP ${address} is in blocked range (${range})`));
      }

      callback(null, address, family);
    } catch (parseErr) {
      callback(new Error('Invalid IP address resolved'));
    }
  });
}

const safeHttpAgent = new http.Agent({ lookup: safeLookup });
const safeHttpsAgent = new https.Agent({ lookup: safeLookup });

app.get('/proxy-image', async (req, res) => {
  const imageUrl = req.query.url;
  try {
    const parsed = new URL(imageUrl);
    if (!['http:', 'https:'].includes(parsed.protocol)) {
      return res.status(400).send('Invalid protocol');
    }

    const response = await axios.get(imageUrl, {
      httpAgent: safeHttpAgent,
      httpsAgent: safeHttpsAgent,
      maxRedirects: 0, // Prevent redirect bypasses
      timeout: 3000
    });

    res.send(response.data);
  } catch (err) {
    res.status(403).send('Request blocked: ' + err.message);
  }
});

इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट

References