This is a talk I recently gave at Devcon IV on October 31, 2018. It has been modified slightly for a wider online audience since you’ll be reading it instead of listening to it. 💖
Hi, I’m Taylor and I am the founder and CEO of MyCrypto. Previously I founded MyEtherWallet.
In case you aren’t familiar, MyCrypto is an interface that allows you to interact with the Ethereum blockchain. You can store your Ether and tokens via MyCrypto, send them around, swap between different coins, interact with smart contracts, and more.
We are a remote team of about 20 people and our amazing team is distributed around the world.
I’ve been building products that have actual users in this space since 2015. Those users have taught me a lot about myself, my design process, and what can happen even if you are exceptionally careful.
Today we are going to explore the unintended consequences of product design. And many of you may be sitting here thinking, “What the heck does that even mean?”
It means that in the early days we managed to screw a lot of things up. We made a lot of choices that were not necessarily the best decisions that we could ever make. And, unfortunately, these decisions had some unintended side effects, like lost funds and confused users.
I want to be clear. I’m not doing this to be negative or to call out certain products (especially my own 😉). The reality is, these mistakes could be made by literally anyone. And if we aren’t careful, these mistakes will happen again.
I hope that everyone in this ecosystem can learn and grow from other peoples’ mistakes so we don’t repeat history and we can ultimately be safer, more secure, and provide a better experience for everyone.
So, let’s kick things off with one of the biggest mistakes I made with one of the most fundamental interactions on MyCrypto: creating a new account.
We have to go back in time to August of 2015. Ethereum had just launched. This was before MyCrypto and, in fact, before “MyEtherWallet”. It was just “Ether Wallet” living on a Github pages URL. This is what it looked like.
When you landed on the website, it wasn’t anything fancy. No explanations. No long on-boarding dialog. Just an interface prompting you to generate a new wallet. You typed in a password and pressed a button. That’s it.
When you clicked this button it would just spit out all of your information. Your private key. QR Codes. JSON files. Address. Boom.
And, by the way, I loved this. From a UX perspective, it was about as simple as it could possibly be. I loved that we could cut through all the clutter and bullshit and just give people what they needed. Generating a new wallet literally took one click.
The problem was, even though we really wanted to make the process of generating a new wallet as quick, easy, and friction-less as possible, it had the nasty consequence of people not saving their private keys.
Because we gave the user their address up front, they were able to copy the address and send funds to the new account without ever saving their private key. Somewhere down the road, they would realize, “Oops, I don’t have any way of actually accessing my wallet.” This ultimately resulted in lost funds.
How did we solve this? We stopped giving the user their private keys & addresses up front. Above is the new interface on MyCrypto.
- First, you enter a password. And confirm the password.
- Then it forces you to save your keystore file. In order to continue on, you must download this file.
- And even then we don’t give you your address. Instead we make the user unlock their wallet.
That’s right, you have to unlock your wallet using the file you just downloaded before you can successfully get your address. This ensures that the user has created their wallet, backed up the wallet, and is able to successfully unlock the wallet now and again in the future.
This cut down on the amount of loss we were seeing on a day-to-day basis (and also helped immensely with the number of support tickets we were receiving at the time)!
What can we learn from this?
Sometimes friction is a good thing. You need to force people to save the necessary information. You need to force people down the correct path, even if that path does have some extra steps involved. Users will always — always — take the easiest route. If you don’t literally force them to save their private key or seed phrase, they won’t.
Educate them on why it is important. Remind them incessantly. But, most of all, don’t allow them to take an alternative path. Remember, the end goal is to successfully get people using cryptocurrencies and if they lose access to their funds, you were not successful.
This next example is quite terrifying and I’ve been seeing it more and more frequently. It’s the practice of saving private keys, or other secret information, to local storage.
If you are a dapp developer, you want to create the most seamless experience possible. It may seem like a great idea to save a user’s access to their wallet in their browser so that they don’t have to back it up. But, please, don’t.
Let’s look at one example of how terribly wrong this can go.
This is EtherDelta — but they aren’t the only ones guilty of this. EtherDelta was one of the most widely used decentralized exchanges in 2017.
EtherDelta has a “create account” functionality on their website as well. (Yeah, they don’t force you backup your private key either.)
But it’s kind of okay because they just save it to local storage for you. /s
For those who don’t know, local storage is kind of like a cookie. It’s another way of saving information in the user’s browser so that if you leave the website or close the browser and then return to the website, it can read this information and re-access or re-authenticate your account.
If you look closely you can see the private key that they spit out at you previously and there it is again, saved in local storage, unencrypted.
This is incredibly dangerous. Local storage is not a secure place for secrets. It’s not a secure place for private keys. And it’s especially dangerous for cryptocurrency websites because they are such a huge target for attackers.
Now, I know I said I wasn’t giving this talk to shame product creators, but, I’m just going to be blunt about this: I am shaming EtherDelta. Because in December of 2017, they got hacked. They should have already learned their lesson. They didn’t. They are still saving keys to local storage to this day.
Last December, the attackers re-routed the website to a fake EtherDelta site. It looked identical to the original EtherDelta. People had no idea they were on a fake site.
This is bad in itself as anyone visiting the website doesn’t know that they are on a malicious website and are prone to enter secret information, like private keys, which go directly to the hackers’ servers.
However, with EtherDelta it was especially bad because they saved their private keys to local storage. Anyone who simply visited the website had their keys, and therefore their funds, stolen.
Let me say that again: anyone who read the news, got scared, and simply visited EtherDelta, had their private keys and then funds stolen. The user had to take no action beyond visiting the compromised website. The scope of loss has never been fully determined, but it was huge and it would have been smaller if they hadn’t saved private keys to local storage.
This is an immensely harmful thing for the ecosystem, users, and to everyone’s safety.
Again, cryptocurrency websites are a huge target for attackers. We have to be secure and safe. That means your own personal security, your company’s security, your website’s security, and, most of all, your user’s security,
We know the intentions of EtherDelta and other dapps in this ecosystem: they are trying to make this process easier. They are making it so people don’t have go back up their private key right off the bat. They are trying to avoid forcing people to re-enter their keys every time.
But, unintentionally, they painted a huge target on their backs. Attackers could see this popular browser-based site. They could see all the users and funds flowing through this website. They essentially waved at the attackers and hackers and said, “hey, I’m right here! Come and get me!”
And then when the hackers did successfully find an entry point, it resulted in way more loss. This is simply unacceptable.
If you are a dapp developer and you are trying to make the experience more friction-less, I applaud you. However, please do not save secrets to local storage.
It’s not philosophical or complicated. It doesn’t matter how much better it makes people’s lives. It is dangerous and paints a giant target on your back and will ultimately result in loss.
Don’t save it unencrypted. Don’t save it encrypted. Don’t save secrets to local storage.
Moving on! In the last example, we covered how storing secret information to local storage can be dangerous. But what if it isn’t secret information? What if it’s just normal information? Local storage can be really helpful and can help simplify the experience. Can you use it for other information?
Let’s look at the original ENS Dapp from when the ENS first launched in May of 2017. The way the ENS works is that you place a bid on an ENS name, then you wait a few days and other people can bid on that name, and then you reveal your bid to hopefully win the auction.
Because it’s all decentralized and on the blockchain and the auction and bid amounts are secret, you have to provide certain pieces of information in order to reveal your ENS name. You need your bid amount, your masked bid amount, and a secret string of characters. Different apps dealt with these pieces of information differently, but one of the dapps saved this piece of information to local storage.
The developers of this particular dapp meant well. Instead of telling users, “you need to remember this and that and save this and also, make sure you don’t lose it”, they just saved it for them. It’s the perfect thing to abstract away during an already fairly complex process.
The problem was, especially in the cryptocurrency space, people love using different devices and/or TOR and/or incognito mode. We use all these things to protect our privacy. However, in doing so it also prevents things like cookies and items saved to local storage, from persisting. When you close that browser window, that information is gone.
Shortly after the first bids were placed and ready to be revealed, support channels were suddenly flooded with reports of people being unable to reveal their bids as they had lost pieces of information.
Saving this information to local storage unintentionally led to auctions that couldn’t be revealed if people had used incognito or a different device.
Thankfully, the developers fixed the issue really, really quickly and made a point of educating people and giving them a way to backup this piece of information externally.
We can see the intentions of this dapp—and other dapps—we want to abstract complexity away. We want the user to have to perform less actions. We don’t want to overwhelm the user with all the details of what is going on behind the scenes because the user doesn’t really care. The user wants to obtain their ENS name, not understand the inner workings of the system.
Unfortunately in this case it resulted in confused users who couldn’t reveal their bids and there was no way to recover this information once it was lost.
I don’t want to toot my own horn but I had already learned my lesson about backing up private keys by this point. The above is how we handled this same situation with the ENS.
We incessantly reminded people to save this information. We told them during the auction process. We told them on the confirmation dialog, right before they sent. We showed it after they sent. We gave them the ability to screenshot and to save a string. We still had heaps of support tickets, but thankfully most people had saved some information and we were able to help them sort out their mess of bids, rather than having to write them off as a lost cause.
What can we takeaway from this example? We know it’s not acceptable to save secrets to local storage. It can be acceptable to save non-secret information to local storage. However, if you do save things to local storage to make people’s lives easier, please make sure that you do not rely on it to persist information. User environments can and do change.
When making design decision, I often play this game called “Best Case / Worst Case”. This forces me to be really explicit with the choices I am making.
Ask yourself, “What is the best possible outcome?” In this case, the user seamlessly bids on a name, reveals it, and doesn’t need to take any action in order to reveal.
“What’s the worst case?” The user can’t reveal their bids and their ETH is lost.
For me, the best possible outcome doesn’t make up for the worst possible outcome. No amount of loss is an acceptable experience. What steps can we take to mitigate the loss? How can we change this interaction to make it better? These are the questions we should be asking during the design and development phases.
Now, let’s think about those same questions with something like settings or user preferences though. Let’s say you remember that a user prefers a certain currency or language or that they prefer to use your app in “night mode.”
- Best case: when the user returns to your site again, the dapp remembers that the user prefers a certain language or color scheme.
- Worst case: the user has to re-select those settings when they visit the site again.
For me, this is what local storage should be used for because it’s not the end of the world if that information is lost, nor does that information have any value to hackers. 👍
Even though we have been focusing on crypto sites so far, these lessons are not limited to the blockchain space. The big boys make choices that can have unintended consequences, too. Even Google.
When you set up a new Google account, they force you to add a phone number and an old email address.
Let’s go through the process of setting up a new account — a process all of you have probably done multiple times.
First, you are asked to provide your standard stuff: name, email address, password.
Next, it asks for your phone number. You are forced to provide a phone number so they can text you a confirmation code.
Now that I have verified the account using my phone number, it prompts me to set up a backup email address (and some other personal information.)
Once you set up your account, if you happen to explore your account settings, you will see that you can recover your account at any time using this email address or phone number. That you were forced to give them.
Above is what happens if I click “forgot password.” Now, you may think that having a recovery email or recovery phone number is a good thing. If you forget your password, it’ll be okay! You can just recover it.
The problem is that hackers and attackers can also “recover” your accounts.
Google makes it unclear on how to actually remove these options as a recovery option.
There is no “delete” button. You have to edit it. Remove all the content. And then say “done.” You would expect to see an error, but it lets you save the empty field and effectively removes the email address.
Oh, but it gets worse. If you do remove the recovery email or phone, every single time you sign into Google they will try to get you to add it back.
So, why is this bad?
Think about your gmail accounts right now. You probably have one that you use for Coinbase or another exchange, right? Maybe it’s called firstname.lastname@example.org.
And this is the account that you’ve taken the time to make sure it’s secure. You have 2FA enabled on it, you’ve gone through all the settings, you are super careful with it.
The problem is when you created the account you provided your previous email address, email@example.com, as a recovery option.
And when you set up that email, you provided you very professional firstname.lastname@example.org as a recovery option.
And that email was set up using your old gamer tag from high school that you don’t even admit you have anymore.
So even though you have secured your email@example.com, that account is only as secure as your old gamer tag from high school. The password for that account, that you probably don’t even remember at this point, is currently protecting all your crypto on all your exchanges right now.
That password is probably also sitting in one of the many, many password dumps online right now. Just because you don’t remember it, doesn’t mean an attacker doesn’t have it on a list somewhere. (psst…you can see just how many times your various accounts have been compromised on https://haveibeenpwned.com/)
If an attacker gets access to any of your previous email accounts, it’s trivial for them to work up the chain and gain access to your exchange accounts and steal all your crypto (as well gather all sorts of other data from services like Paypal, Dropbox, Google Drive, Apple, Facebook, Amazon, etc. This is one reason you should never, ever store secret information in Dropbox or Google Drive.)
Or, if they can’t get into your email accounts, they could just port your phone number or do a sim-swap. You may be sitting here thinking, “Oh, I’m small time. I’m just reading this crypto post but I’m really nobody. I’m not a target.”
Wrong. If you are reading this post right now, you are in crypto and you are a target. There are thousands of attackers out there who spend all day, every day trying to social engineer the phone companies. They try to to port your number or do a sim-swap so they can access your Google accounts or your Apple accounts or your social media accounts and gather information and ultimately get into your exchange accounts to steal your funds.
It’s a great thing that user accounts can be recovered. Don’t get me wrong. There are a ton of really, really smart people out there in this crypto world trying to figure out a safe way to give users the ability to recover their private keys.
But—and this is a big but—if you can recover your account, a hacker can also potentially “recover” your account.
What choices can we make to mitigate this as a creator of a product?
This is a hard one because recoverability is an immensely important feature. There would be way more lost accounts and information if this feature wasn’t available. It’s not entirely realistic to expect every user of Google use a Yubikey. That is a hard ask.
But, think about it. Google knows literally everything about us. They read all our emails, they know what websites we visit, they know where we are. They already know if you are a high-target individual. They know if you have cryptocurrency or have other traits that would make you a target for hackers (e.g. public figures, celebrities, politicians.)
One thing Google could do with all that data is differentiate between average and high-risk individuals. Why not educate the people who are celebrities or politicians or cryptocurrency holders on how to be more secure? Why not force them to remove insecure recovery methods and use secure ones instead? Maybe they could use some of that data for good.
Please remember as you are building dapps that every single one of your users is a target for attackers. You need to prioritize the security of your users, not just usability.
Quick PSA: please go secure all your Google accounts—even your old ones! Remove the recovery email and phone numbers from every single one of your Google accounts and add 2FA via Google Authenticator or Yubikey or your Ledger hardware wallet. Use backup codes. Do not use email / SMS recovery. Dive into your Google security settings. Also, read all our security recommendations. Please don’t get hacked.
Alright, almost done!
I talked earlier about one of my biggest mistakes earlier. But this one is my absolute biggest mistake and has had lasting consequences not just on my website and my company and my users, but the entire ecosystem because people started following suit. We allowed—actually we recommended—that people to enter private keys on our website.
I always thought that the reason you don’t enter your private key on a website was because the creators of the website could exit scam and steal all your funds at some point.
Since I was the creator of this product, I knew I wouldn’t steal people’s money and therefore I assumed this “rule” didn’t apply to us. This was naive.
The attack vectors on the web are huge.
- You have to trust the creators of the website.
- You have to trust that the code on the website is the same today as it was yesterday.
- You have to trust the creators of the website haven’t been hacked.
- You have to trust the DNS wasn’t hacked.
- You have to trust that you are on the correct website and that the website is legitimate and not a scam.
As these various attack vectors came to fruition, we did a lot to educate users about safer methods of accessing their accounts.
We started yelling at people via our atrocious 10-freaking-page onboarding dialog that forced users to click through each slide while the buttons hopped around. We made our users checked boxes acknowledging that they were being unsafe. We added friction every step of the way, trying to make people to understand the dangers.
But education was not enough. You can yell and shout and stomp your feet all you want but the user is always going to take the easiest path. If you allow people, no matter how much friction you add, to enter their private keys on a website, they will enter their private keys on a website and it will eventually result in loss.
By allowing people to enter their keys on our website we were instilling bad habits in them. When ICOs became popular and a different type of person took over our userbase, they would enter their private keys not just on our website, but every website. Any website that offered them free money, for example.
We had failed.
So we removed the ability to use private keys on our website.
We no longer give users the option of taking this easy, insecure road. If you are on our website, you are pushed to download our desktop application.
From a UX perspective, it feels wrong. It feels like adding friction. It makes user take additional steps. We don’t have any analytics but if we did have analytics, watching this would be painful. We would be watching user after user drop off right here due to this added step.
But the reality is if we let users continue to use their private keys on our website, it would result in more and more money being lost. Therefore, it’s worth it. It’s worth it for me and this ecosystem.
At least I am losing users, not having those users lose money.
I’ve always wanted to lower the barriers to entering the blockchain space. I want to make the experience of obtaining and holding crypto as simple as possible.
By recommending that people enter their private keys on our website, I inadvertently created a environment where phishing websites and scam websites thrived, registrars were being hacked, and attackers were coming up with increasingly sophisticated ways to steal money, like the recent BGP attack on MyEtherWallet.
Most disappointing is that other sites in this ecosystem followed our lead, amplifying the problem and teaching users to copy and paste their private keys onto any website out there.
Again this is not philosophical. This is not complicated. Do not let your users enter their private keys or mnemonic phrases or seed phrases or keystore files or other secret information on your website. Period.
It’s not secure. It’s training your users to be insecure. Build an Electron app. Use a Chrome Extension. Rely on MetaMask or Trust Wallet or Ledger or Trezor hardware wallets. If you don’t, you will spend all day, every day protecting against these attacks and trying to educate your users and creating pretty onboarding modals that yell at your users instead of, you know, just focusing on building your product like you should be doing. Let’s be safe by default.
What have we learned? What are the glorious takeaways from everything we have been talking about?
First, do not underestimate the people using your product.
Your users are smart, talented, intelligent people. You can educate them. You can teach them. They can learn. They can be retrained. They can get into crypto because of you and your product.
But! They are also naive and ignorant. They are really, really good at screwing things up. They carry assumptions with them. The world has taught them anything and everything can be recovered and there is no such thing as complete and utter loss.
Don’t underestimate them on either end of the spectrum and be prepared for everything.
Next, people will always—always—take the easiest path. If you allow them to skip steps or not back up their private keys, they will skip those steps and not back up their private keys. It will eventually negatively affect your product and your reputation and, most importantly, their experience with cryptocurrencies.
Education! Education helps a lot! Those tooltips and messaging and cute icons that help people identify core concepts can work wonders. But it only goes so far. You must build good habits in your users and set them up for success. You must empower your users.
If you find that your educational materials start yelling at your users, it’s time to take a step back and ask yourself how you can build a product that is safe and secure by default.
Learn from others mistakes. I just gave you five examples of ways we have already screwed things up. However, there are countless more. Work together. Talk to each other. Share experiences. Stay up to date on what is going on. Study where people failed and how attacks happened. Make it a priority to prevent it from happening again.
This goes hand-in-hand with the previous point: be mindful of the various attack vectors out there. This whole blockchain / cryptocurrency world is really, really different. The loss of a single string of information results in loss funds. The ROI for attackers is huge and allows them to spend a lot of time and effort and creativity on their attacks. They evolve. They change. They will do almost anything to compromise your company, your product, your team, or your users.
Always consider the “worst case” vs “best case” when making design decisions. Even if the design can result in a superior experience (the “best case”), if the worst case is lost funds then you need to take steps to prevent that loss.
Realize that building the decentralized future is really, really hard. This is why it is so important that we all work together and learn from each other. We want to be decentralized. We don’t want to hold people’s private keys and take custody, but that means we are entering a new frontier in authentication and usability and security.
Dedicate proper resources to solving complex problems and preventing bad things from happening to yourself, your company, and your users.
Always individually strive to be better and always push those around you to be better as well.
We need to realize, whether you are a developer or just an average person, whether you have been in this space for one year or two years or five years or two months, everyone has their role to play.
If you are seeing another product creator making a lazy choice, even if you are just an average person on crypto-Twitter, you play a huge role. You have the ability to ask questions. You have the ability to hold product creators accountable. Point things out. If they are doing something unsafe, question their choice and push them to make their product safer.
The best companies will take your feedback and questions to heart and strive to be better. I know I really cherish the messages that we get that are questioning my choices or pointing things out as they force me to be better.
Lastly, building together makes building this decentralized future a whole lot easier. This space is far too small for real competition. We have to all work together. We need to build this ecosystem up. Learn from each other. Help each other. Lean on each other. Be kind and helpful and connect to make this ecosystem stronger and safer.
We need to think about it as us—“us” being everyone reading this right now—vs the world. And the world is the big world out there that doesn’t even know what cryptocurrency is and has no chance of using it right now.
We can not be fighting with each other. We cannot be instigating Twitter drama. We cannot be dealing with little tiny pieces of bullshit. We need to be looking at the big picture. We need to focus on the world out there and conquering it.
I met with a lot of people at Devcon and I noticed a trend of people who had one foot in and one foot out. Some were working part time in the space or just observing from afar.
Let me tell you this: every single person has the ability to make this ecosystem better. It doesn’t matter if you don’t know how to code. We need the people who can write copy and design. We need the people that have endless patience to answer support tickets. We need people who will focus on making the entire experience better.
We need you all. We need the product people and the engineers and the project managers and the designers and researchers and everything in between. We need moms and daughters and fathers and sons and even the grandparents. We need people of different upbringings and backgrounds. Those who are college educated and that didn’t finish high school. In order for us to build a global, decentralized ecosystem, the people building that ecosystem that anyone in this entire world can use, the people building need to reflect that world. It cannot just be one demographic. It cannot just be the coders and the technical geniuses. It has to be everyone.
If you have one foot in, I need you to step all the way in. I need you to think about your unique skills and your unique experiences and figure out how you can make this ecosystem better.
If you want to help us specifically on our mission, email us at firstname.lastname@example.org and pitch yourself. We are always hiring remarkable people who want to make a difference.
And, that’s all I’ve got for you today. I hope you can carry this with you as you go out and build and create and help usher in this decentralized future.
I hope you have made connections here that will be lasting.