I am sure many of you had a similar experience when you were about to sign something online and you were not sure what you were signing and how secure it is. Many websites today ask you to sign a message when just logging in to dapp. Sometimes it shows you some message you are about to sign in clear format but sometimes it’s just some random hex values and you are not sure what you are signing. So I wondered how secure or insecure that is and what are we doing by signing some messages.
So one of the scariest things was eth_sign, which would mean that your signature could get used to sign malicious transactions if you were uncareful and submitted it to some malicious website. Transactions are arbitrary hex data that is signed by your private key, this process involves a cryptographic function that takes your transaction details and private key as inputs. The output is a signature that uniquely corresponds to the transaction details but does not reveal your private key. That data is then broadcast to the network and if validated it is included in blockchain.
If you sign a piece of data that is ambiguously formatted or not specific enough, that signature could potentially be reused in a different context where the same data format is valid.
Luckily things around this have been changing. First Metamask now by default has disabled eth_sign, the same thing is with Rabby and I believe many other wallets. Then there is an EIP 191 from 2016 that is widely implemented and prepends signed data with 0x19 byte making it INVALID for RLP structure which means it can never be an Ethereum transaction. Also, check EIP 712 which is helping this cause. Today most of the web3 apps now use personal_sign RPC method for signing messages as it is the easiest way to request human-readable signatures, A simple way of doing this would be
personalSignButton.addEventListener("click", async function (event) {
event.preventDefault();
const exampleMessage = "Example `personal_sign` message.";
try {
const from = accounts[0];
// For historical reasons, you must submit the message to sign in hex-encoded UTF-8.
// This uses a Node.js-style buffer shim in the browser.
const msg = `0x${Buffer.from(exampleMessage, "utf8").toString("hex")}`;
const sign = await ethereum.request({
method: "personal_sign",
params: [msg, from],
});
personalSignResult.innerHTML = sign;
personalSignVerify.disabled = false;
} catch (err) {
console.error(err);
personalSign.innerHTML = `Error: ${err.message}`;
}
});
the other RPC method to use that is secure is **eth_signTypedData_v4 **which is based on the previously mentioned ERC 712 and is used to sign typed structured data.
It seems now both eth_sign and personal_sign prepend "\x19Ethereum Signed Message:\n" for most signers. Also, signMessage in Ethers.js is doing/expecting the same as well as geth node RPC calls stating this is prepended. An additional note is that personal_sign and personal namespace is in deprecation process. Another topic you might find connected to this is blind signing here is more info on it and why sometimes you must enable it on the ledger.
For the END, here is the Metamask tool where you can try out all sorts of transactions and signings, both valid and malicious, and see what info is shown and what to expect in those cases.