A History of eth_sign in MetaMask

A History of eth_sign in MetaMask

Ah, eth_sign. I can tell that story. I may look like a fool for part of it, but I’ll try to represent the historical contexts that made the sequence of decisions make sense. This’ll be a personal perspective, closely tied to my position as a person of significant influence at MetaMask.

eth_sign here means the method that allows signing a plain hash. It is opaque by definition, and so should be assumed to be capable of doing anything a single signature on the eth curve can do.

eth_sign was originally a geth feature, back when it was the only signer. Of course a CLI signer has a signing method to do anything with, right?

Then Mist, the original web3 browser which contained a full node added geth’s RPC to its web3 API injected into every page. Back then they’d just sign anything the site asked it to without user interaction. This was safe for a moment because there were basically no users or Ðapps, and phishing was limited to pranks.

MetaMask originally cloned Mist’s API in the name of being compliant with what we perceived as a nascent standard, but we added confirmations before any signing interaction. We eventually added some important improvements like only revealing a user’s accounts upon a site initiated confirmation.

Before any readable signature standards existed, any contract that implemented a slightly novel signing scheme would depend on eth_sign to innovate. personal_sign added string signing, but was not efficient to parse onchain. EIP-712 signTypedData improved machine efficiency, but still struggles for sufficient readability to be considered safe.

Early on we identified the risk to eth_sign and added our most severe warning text of any confirmation, basically saying the signature could potentially transfer away all of your funds, even though at the time such an attack was theoretical (unlike post EIP-3074).

screenshot

Early on we also:

At the time I had high optimism that users could learn to take confirmations (and their warnings) seriously. I have since transitioned to more interest in alternative interaction patterns that encode consent in the user’s behavior rather than depend on reading, like a file picker. I gave a talk about a proposed next step for readable wallet-site connections at Devconnect 2023 in Istanbul.

I defended keeping eth_sign longer than many. At that stage, I felt preserving the functionality of the existing ecosystem was critical, and had high faith the warning was doing its job. I argued against Pedro from walletconnect. I argued against my own cofounder Kumavis. I probably defended it too long. I often cited this email by Linus Torvalds.

I still believe the principle of non-breaking APIs is under-appreciated in web3. I collected notes on how different platforms drew the line. Security was clearly the top justifying reason, but I hadn’t seen the proof that our warnings were insufficient.

Largely driven by Harry Denley, we eventually added enough metrics that I was convinced there was enough phishing activity and not enough legitimate usage of the method that it was time to sunset it. We initially have it soft-deprecated, and it’s buried in settings behind another heavy warning. Our metrics show this has been sufficient to all but eliminate its usage. Some phishing kits still hit the method, but they are auto-rejected and tracked for flagging.

Now that 3074 is looking likely for inclusion, we’re finally completely removing the option to enable this method, even for advanced users going into settings. They’ll need to use a command line tool if they really need to sign messages that are formed outside the bounds of the existing signing methods.

I'd also like to note that even "readable signature methods" can be misused. OpenSea's Wyvern 1.3 contracts used the plain-text personal_sign signature method to sign an opaque hash as text, which is a good example that these methods only work if it's obvious how to use them safely to every member of the interaction. The lazy path needs to be the safe path.

I hope if anything stands out here, it’s the gradualness of the process that we have been involved in. I think we’re finally starting to see some eth adoption of more coherent readability (I mean onchain safety, not simulation), but I don’t think true safe interaction patterns can be back-ported to EOAs. I think users need to be able to enforce their own policies around what they own, and that requires the power of code.

I’d hoped for some years that the existing smart account wallet teams would uncover some safer usage patterns that we could integrate via our Snaps plugin system, and we are now seeing the first SCAs pursue a standardized permissions system for connecting to Ðapps. I’ve long advocated for 3074 because I saw it as a path for EOAs to eventually enjoy some of the same assurances that SCAs unlock (so old accounts have some safety, not so we can prevent new users from using safer accounts).

I hope that's at least an interesting history. I haven’t been omniscient, and none of us have been, but at least you can see how the safety requirements of an ecosystem may change over time, along with our abilities to reason about them. I hope you don't judge me too harshly for my part.