Credits: Steem Connect
Building the Steem Connect auth system is easy and secure. It is so that easy everyone seems to be doing the very same predictable thing. All one had to do was connect to Steem Connect and retrieve the user access_token from the callback query string. We could now then use it to authorize the user's Steem activities.
Besides, what else could a Steem user do other than post, vote, follow and move funds? For a typical Steemit, Busy or eSteem interface, those are the typical activities.
The Steem JS tutorials explain the Steem Connect setup in details. The tutorials are great for learning, proof of concepts and trails. Then comes the time for production-grade development. Things don't look that easy anymore.
I'm completing a minimalist core update for Peer Query beta. This core update would make Peer Query a platform with diverse products other than posting posts.
Users could be able to create custom items such as projects, queries and more. We have shifted from the typical post, vote, follow, transfer framework into something more.
We need to refactor our site for functionalities beyond posting, voting, following and funds transfer. In this post, I will highlight some of the challenges sites I faced as well as outline the concept solutions I came up with. Sharing this information might help anyone who faces similar challenges.
The new model challenge
The typical client
The typical client is the Steem database browsing interface, a client that connects only to the Steem blockchain.
Busy, Steemit, eSteem, Peer Query alpha Steem Peak and more are prime examples. All operations done on these sites are Steem related.
The custom client
These types of clients have functionalities beyond the basic operations of posting, voting, resteem, and follow.
Let me use the Peer Query beta in development to illustrate this. On the beta, we have reports which are the usual Steem posts. Yet, we also have projects, queries and more.
These new objects are not the typical Steem post and need to be stored as a standalone system. Other sites would have chat, shops and other forms of products that don't match the Steem "post" type.
Even though it is possible to make these objects as Steem posts, it would bloat the chain. Also, not all developers would want that.
With Smart Media Tokens around the corner, many websites would adapt Steem. Some of them would not want to build on Steem, instead, they would add Steem as a new utility on their sites.
There might be several challenges facing existing businesses adopting Steem, I would address two here:
- securing the auth tokens
- managing seamless dual login
Challenge #1: Securing *access_token*
Securely saving auth tokens
For a simple user client that is Steem based, we might use to consider storing our tokens in the cookies. Transaction signing is all done client side so its faster for scalability even though cookies are not very secure.
For a full platform with custom functionality beyond Steem operations, we need to evolve. Containing the auth tokens in the cookie might work for Steem operations but not for custom site operations.
We cannot base the entire auth of a platform on the Steem Connect tokens stored client-side in cookies. When users want to do a Steem specific activity, they would use the Steem connect tokens.
How about when they want to do a site-specific activity like chatting or updating their site-specific profile on the site. The site cannot rely on the Steem connect tokens as auth for such operations.
With a platform app, we need more security since there is more at stake than just risking the posting, voting and resteem rights. If the auth is compromised, the entire site's operations are damned.
Concept #1: encrypt and store tokens in DB
This is the most difficult solution of all. Storing auth tokens as plain text is out of the question. Instead, we could encrypt them and then store the encrypted data in the DB.
The security of the encryption key now becomes a challenge. We could address this by again encrypting the encryption key, putting it into user's JWTs payload. The JWT would be stored in httpOnly & secure cookies, fragmenting our auth keys.
This system is secure even though it comes at a server load cost. Frequent decryption for activities such as posting and voting might be server compute intensive.
If your app uses other features of Steem more than the frequent voting and commenting then this is will do.
Concept #2: store tokens in JWTs
With this concept, we store the auth tokens within JWTs which is then stored in cookies. This will make it possible to have just one auth system. This system would allow you to have both the native site auth and the Steem Connect auth together.
The challenges, however, is that JWT is not encrypted data. You could get by this challenge by encrypting the Steem Connect payload within the JWTs. If your encryption key is stolen, well, we're done.
Also, the server would need to decrypt it whenever a user operation demands it.
Concept #3: encrypt and store tokens with user
You could also encrypt it, then send the encrypted tokens to the user as cookies. This would be great, however, whenever the user needed it, you would have to decrypt it for the user.
A smart solution could ask the user to supply their own temporal session secret which could be used to decrypt it for the user client side. This frees the backend from decryption and the need to encrypt or store encryption keys.
Challenge #2: *Uniform account auth*
Dual login dilemma
When a user logs into a specialized Steem app, they are actually logged into two system at once:
- they are logged into Steem Connect
- they are also logged into the custom app
For Steem related operations users use Steem Connect auths; for the site-specific operation they use the site's own auth.
For the typical Steem only app such as Busy, the user never actually log in to them. They only login into Steem Connect and use their Steem Connect auth to authorize their activities through the site.
It's strange when you actually grasp the concept. Yet the truth is that you never actually login into any Steem-only site which is powered by Steem Connect. You only login Steem Connect and remain a guest or virtual user on the site.
The reason is that with Steem only sites, all the activities are done by Steem Connect: posting, commenting, voting, resteem, follow and more. The users never do anything within the site itself, they only use the interface. All transactions are signed in the browser and nothing is done by the user on the server, hence no need to maintain a database at all.
However, sites with their own custom products as well as Steem's posting utility would need to maintain a dual auth system;
- one for Steem related operations
- another for the site's own native operations
Unifying the login system
Maintaining two auth systems and two different active sessions for one user at the same time is difficult. It also requires a high level of security for effective implementation.
Eventually, the app builders would have to choose between the two auth systems for a master-slave relationship. Steem Connect as their primary auth system with a secondary system for the site itself. Another option is maintaining their own native auth system as the primary while making Steem connect secondary.
The former would mean that user would also would their Steem accounts as their primary username or unique identifier. This would also limit the site to not being able to accept users who do not have Steem accounts.
The below recommendations might not save your app from being hacked. However, they might reduce limit the potential for an effective, widespread and damaging exploit.
Use IV tolerant encryption
As recommended by Vance Lucas, add randomness to your data encryption. That way, the same data encrypted multiple times with the same secret and scheme would produce different encrypted data each time.
If you are looking to encrypt any of your Steem Connect or auth data, consider adding randomness.
Make your security apparatus random
If your code is not open source, then you could make your security setup a jigsaw. If you are storing some tokens in the DB, it does not have to be the auth tokens, it could be the encrypted keys, while the real encrypted auths are stored in JWTs.
Even if an attacker has both the key and the auths, they are useless since they are both encrypted with different secrets. The naked auth tokens are encrypted with a secure and randomly generated key.
Then again the random keys themselves are also encrypted with another secret. Finally, everything is scattered everywhere; a nice way to waste an attacker's time.
Make your security apparatus as cruel as an attacker
If you have time, you could even make a special table or collection just for auth tokens. It sounds bad I know but in practice it's secure. The table would be populated with the encrypted auth tokens without any mapping to the owner. The keys to the encrypted auth tokens would also be encrypted and stored with the users in a JWTs payload.
The user's encrypted JWT's would also contain the encrypted ID of the encrypted auth token in the table or collection. This would mean that the user tables themselves would not have any auth tokens at all nor would they contain the ID to their auth tokens.
This setup would be ideal for wasting the time of every attacker. To be able to access a token, one would need to have access to the entire user table, the entire auth table, the server, and all JWTs issued to all active sessions. Without just one of these, no one can ever hack even one key even if they had full access to everything else.
Open source wisely
Open sourcing your code is great even though it doesn't mean much to an app's credibility. The code running on the server might not the same code on Github.
If you are going to open source your app, then bear in mind these very important lessons:
- very few good willed developers would have time to volunteer to review the security of your open source app
- lots of ill-willed developers will make time just to review your code for vulnerabilities so they can launch an attack
Most hackers command far more skills and experience than you and your entire team might ever have.
I would advise you to use a copyleft license for your code. This will ensure that all others who use your code keep their versions open source and you can benefit from their security updates.
Stay minimal and reversible
Let your Steem Connect app ask for only simple account rights such as posting, following users and voting. Do not ask for anything that could be used for real damage such active account auth rights since they could be used to transfer funds.
Ask only for auths whose activities could be reversed such as voting, posting and follow. Do not ask for funds transfer rights since funds transfer is irreversible, else you risk attracting hackers.
If even just one of your users has at least 100 liquid Steem in their account, that could attract hackers to find vulnerabilities in your system. Imagine if other users had 1,000, 50,000 and 260,000 liquid Steem.
Understand your code
Seriously. Build tools are great, but be careful not to bundle any auth/secret keys to your front end. Using dotenv and requiring it in the wrong place could send your supposed secret file to the bundled code.
This would be even more disastrous if it included your project's Steem private key which you might be using on the server or encryption keys. dotenv-webpack could help out with this issues if used correctly.
- Secure your site
- Secure your site by:
- wisely using CORs; whatever you do you need CORs
- protecting against Cross-Site Request Forgery even if you have CORs and JWTs
- helmet(or its features for non-Node apps)
- using signed cookies
- using JWT for non-sensitive stateless auth details
- implementing a rate limiter or using CDN
- sanitizing all user inputs
- sanitizing against HTTP Parameter Pollution
- whatever else I might have missed, do your own research, contribute your findings in the comments
Do not support old browsers
Do not let Babel's old browser compatibility tempt you into using it to support everything that can access the web. If you are going to be stored securely encrypted account tokens, auth JWTs or CSRF tokens in the cookie, they should be httpOnly and HTTPS secure.
Do not settle for less; it is not just fewer features, it is also less secure.
This report does not constitute professional security advice. It is only a reflection of my personal judgment and experiences. Consult your security advisory before acting on any recommendation made within this post.