One script to rule them all..
In the past few weeks Hive has been plagued by phishing campaigns and some users lost access to their account or lost their funds. This is nothing new and has been going on for years on
To make things worse, registrars are not quick enough to take down phishing domains..
It is time that the Hive community tries to mitigate this issue on their own.
Potential solution:Use a plug-and-play universal script (or a modified copy of it) that all Hive frontends can integrate to block phishing links in a timely matter. As soon as a phishing campaign starts on Hive and the attacker's domain is spotted and added to a blacklist API (eg. @spaminator's), the universal script would immediately block the attack in all Hive frontends and at the same time leave the User Experience unaffected.
As a matter of fact, this is what I worked on in the past few days.
- Here is the universal script for all Hive frontends: https://github.com/keys-defender/hive/blob/master/scripts/universal-bridge-against-phishing.js
- Here is a demo website with my script in action: https://keys-defender.github.io/hive
NOTE : the demo page will not be sanitized by my universal script until you: 1. Navigate to https://cors-anywhere.herokuapp.com/corsdemo and click on the "Request temporary access" button, 2. refresh the demo website.
I have proved my universal script working on: (see screenshots down below)
HIVE.BLOG - https://hive.blog - @blocktrades @quochuy
PEAKD - https://peakd.com - @asgarth, @jarvie
LEOFINANCE - https://leofinance.io - @khaleelkazi
ECENCY - https://ecency.com - @good-karma
3SPEAK - https://3speak.tv - @theycallmedan, @starkerz
D.BUZZ - https://d.buzz - @chrisrice
STEM social - https://stem.openhive.network - @lemouth
STEM geeks - https://stemgeeks.net - @enforcer48 ?
It would be great if each Hive frontend maintaner could review it and adapt it to their needs (if they need to -- I tested this script in all major Hive frontends already and it did not seem to have unwanted side effects). I highly recommend though that the integration is thoroughly regression tested before it's used in production.
FYI also for:
AnonRamblings @emrebeyler, DTube @heimindanger, Engage @arcange, Engrave @engrave, @nicniezgrublem, Hive-db @jesta, Hive-Engine @aggroed, Hiveblockexplorer @penguinpablo, Hiveblocks @roadscape, HiveEngine @holger80, Quello @tobias-g, ...
+ other project maintaners: @yabapmatt, @stoodkev, @aggroed, @louis88, @mahdiyari, @acidyo, @fbslo, @rishi556.
(apologies for the mass tag)
I believe that blocking phishing campaigns in an unified way in all frontends is possible despite the different tech stacks in use. It does no matter which framework the website maintainer used to develop their website (eg. ReactJS, AngularJS, VueJS, JQuery, vanilla JS, etc). Using JS global overrides it is possible to prevent users from falling victim of phishing.
What does my script do?
It uses the native APIs offered by all browsers in order to immediately block phishing links and images in all Hive frontends as soon as they are discovered and published into the blacklist.
Any known phishing link in the UI will be red and strikethrough-ed. Furthermore if you click on a known phishing link you'll see an alert telling you that it's phishing and it won't allow you to navigate to the phishing domain from the Hive frontend.
Any image that is marked as a phishing attempt (eg. an image with some text saying "claim your $ 1000 in tokens at www.justinscam.com") will not be displayed in any frontend that uses my script (or a variant of it) as soon as its URL is published into the public blacklist.
More details will follow towards the end of the post to list all the native features of the browser that are hardened by my script in order to prevent phishing (ie. iframes, scripts, window open, XHR requests, Fetch api).
How difficult is it to try out my universal script in your Hive-powered website?
Integrating this script is very straightforward. In order to integrate it, simply add a tag to the head section of your index.html page. That's it!
Your index.html :
<head> // ADD THIS ONE LINE WITH MY SCRIPT vvv <script src="https://keys-defender.github.io/hive/scripts/universal-bridge-against-phishing.js"> ... </head> <body> ... </body> </html>
This should be enough for your tests. Some intial recommendations though:
- Keep an eye on CORS issues. If a request is failing because of CORS, use https://cors-anywhere.herokuapp.com for your tests (it's already there commented out in my script).
- Update your Content Security Policy if necessary to allow your website to load my script from Github and to allow the request to @spaminator's api to go through.
- It is highly recommended that you host your own version of this script for your production website. It is clearly not a good idea to rely on my github as it's a single point of failure. For example, what if my github account gets compromised? If all Hive frontends point to my script they would all be affected!
And now.. some proof of this thing working:
Overview of protected features on various Hive Frontends
[screenshot taken after applying my script to Leofinance]
styling applied to known phishing links: red and strike-through
alert displayed when a known phishing link is clicked:
[screenshot taken after applying my script to 3speak]
[screenshot taken after applying my script to d.buzz]
(my post: https://hive.blog/hive-193084/@keys-defender/zy71ydsnp4o1ufhs6j7ksz)
[screenshot taken after applying my script to Stem social]
[screenshot taken after applying my script to Stem Geeks]:
Both "img" tags and direct uploads get correctly sanitized:
[screenshot taken after applying my script to hive.blog]
Eg. malicious ads - see this phishing campaign
[screenshot taken after applying my script to hive.blog]
[screenshot taken after applying my script to Peakd]
[screenshot taken after applying my script to Ecency]
Not supported by my universal script:
- window.location (window.location.replace(url) and assignments to window.location.href)
The window.location object is immutable therefore if you use that approach to open external lilnks, please consider switching to window.open so that my script will protect your user base from known phishing domains.
- Instead of just using a list of known phishing domains, the blacklist could be restructured to something similar to what I'm currently using for @keys-defender's blacklist (merged with the @spaminator's one).
Instead of just using a list of links, I use an array of objects and each object has the following properties:
-- "regex": used to match a known phishing domain
-- "fullLink": full link to the homepage of the phishing site
-- "threatType": PHISHING or COMPROMISED_DOMAIN. I used the latter in the past when I found a critical vulnerability in a Hive website and only until it got patched. This attribute is also used in @keys-defender auto-replies to phishing comments to categorize threats.
-- "infoBlog": link to article that warns against the phishing campaing in progress. This is already in @keys-defender auto-replies and if none is provided it defaults to an intro post.
If we had multiple blacklists and they all shared this interface (eg. using a swagger file) it would be easy to merge them and use them to block phishing campaigns in their early stages. The warnings displayed to the users would be more accurate as well.
- Add a warning to all Hive frontends for all comments containing links left by known compromised accounts. This should be easy to add re-using the global overrides already added in my script and @spaminator's api dedicated for this purpose.
- Display a proper message on the UI instead of just an alert (probably not important, a fancy UX in my opinion is not necessary for this edge case). Hive frontends maintainers can adjust the messaging as they see fit though. My alerts are more of a Proof of Concept after all.
Centralization. Despite being a good start, we should not rely only on @spaminator's API. Some potential solutions to this is to allow users to choose which blacklist to use, or fetch multiple blacklists at the same time. Or even have each Hive website maintainer host their own blacklist.
No load testing was performed on @spaminator API. If all Hive frontends start using their API it may become an issue. @spaminator team to chim in on this.
The requests from my script are already throttled significantly on the client side and a local cache is used as well.
Proper server-side caching on @spaminator API. Eg. Cache-Control response header set to 5 mins? Cloudfront (AWS) caching / Varnish / Redis?
Scaling. How scalable is @spaminator API? Would they incur in prohibitive cost if too many requests were to be sent daily? Do they have a defense mechanism in place for DDOS attacks? This is not a deal breaker since all users will have a cached version of the blacklist, but an attacker could try and take advantage of this for a new phishing campaign.
Notes for devs that try my script:
- To turn OFF verbose logging and remove the cors-anywhere proxy, set
- @spaminator's list of blacklisted domains lives at https://spaminator.me/api/p/domains.json. This list of known phishing domains targeting Hive is stored in the localstorage as well. If the API is down (eg. DDOS-ed) the Frontends will still block known phishing domains.
The list of global overrides added in my script is: document.createElement, element.setAttribute and getAttribute, history.pushState, XMLHttpRequestClass, window.fetch, window.open.
The DOM elements sanitized by my script are: iframe, script, img, a. Other potential candidates that would be easy to cover: input, audio, embed, source, track, video, link.
When the document is fully rendered all the risky elements (anchors, images, etc) are checked again if they weren't already. Eg. comments fetched after the page is fully loaded.
In order to reduce the load on @spaminator's servers, if the user is idle on a page the API is queried again only every 15 minutes. If the user navigates to a different subpath of a Single Page Application (eg. https://hive.blog) (would not cause a page refresh) the api is queried only if at least 5 mins passed from the last query.
The same applies for non-SPA (eg. Leofinance, Peakd). A new API request to fetch the updated list of domains is sent only if more than 5 minutes passed from the last request.
Example of phishing domains fetch on history change
Metrics - I added some timers and I noticed no significant performance impact validating all risky elements against the list of known phishing domains.
The script is already resilient to API 500 responses (eg. DDOS-ed). We could also periodically rotate to different APIs or fetch multiple ones at the same time.
Here is a link to my script transpiled with BabelJS for older browsers and minified to save some milliseconds in the load time: https://keys-defender.github.io/scripts/universal-bridge-against-phishing.min.js
- Test it!
To reiterate, before using this script in production it is highly recommended that you perform regression testing to make sure that existing features are not affected by the global overrides in my script. From my own tests they seem to all work correctly but this should not be put in production as is without review and further testing on each Hive frontend.
- When all frontends will have in place an effective defense mechanism against phishing, it won't be necessary anymore for @keys-defender to keep warning Hive users against shortened links (eg. https://hive.blog/hive-156509/@keys-defender/antiunsafelinks-keys-defender-bot-1615861217985) and phishing campaigns (eg. https://peakd.com/@leemikyung/qpwsgn).
Are you bored with your day and willing to read more?
If you're not tired of hearing me rumble about this stuff in your head, here is some more stuff to read..
Why did I decide to work on this solution?
There were some discussion ongoing on Discord and Slack about how to timely react to phishing campaigns that recently plagued Hive. @guiltyparties and @pfunk claimed that our best shot would be to block dangerous links directly in the Hive frontends.
This though required every single maintainer of the Hive condensers to implement their own logic from scratch in order to block attackers.
The idea I already have had in mind for a while though was to use a single script, as an universal solution for most Hive frontend out there.
I had already worked on something similar for a client of my employer. It was not for phishing, but the same logic applied. The platform I was working on used a custom scheme (eg. somescheme:// instead of https://) and all relative protocol links added by third party scripts (eg. "//www.googletagmanager.com") were defaulting to the custom scheme instead of using HTTPS.
In that occasion I had to use some global overrides to add the https prefix to anything that was meant to go out to third parties. That ended up being handy for this solution on Hive.
When did I work on this Proof of Concept for Hive frontends, you ask?
Late at night in the past few days while caring for my few-days-old newborn. I'm off work and getting bored so I decided to put my sleep deprivation at work and started writing some code for this purpose. I hope it will be useful and get used to effectively improve our Hive frontends and discourage scammers.
- XSS vulnerabilities in #########.com
- XSS vulnerabilities in hive-db.com
- XSS vulnerabilities in scribe.hivekings.com
- XSS vulnerabilities in hiveblockexplorer.com
- Malicious ads redirecting all Steemit iOS users to a phishing site
- Reverse tabnabbing and clickjacking in steem.chat and steeemit registration page
- Universal script to prevent phishing in all Hive frontends
- Phishing protection [live scan of commentsa and posts to warn users against known phishing campaigns and compromised domains, scan of memos]
- Re-posting detection [mitigates the issue of re-posters]
- Code injections detection [live scan of blocks for malicious code targeting dapps of the Hive ecosystem]
- Anti spam efforts [counteracts spam from hive haters]
Please don't forget to upvote and reblog. Donations are welcome as well, those damn diapers aren't free! =P