diff --git a/lib/utils/ens-cache.js b/lib/utils/ens-cache.js --- a/lib/utils/ens-cache.js +++ b/lib/utils/ens-cache.js @@ -22,6 +22,17 @@ +normalizedETHAddress: ?string, }; +const normalizeETHAddress = (ethAddress: string) => ethAddress.toLowerCase(); + +// Note: this normalization is a little different than the ETH address +// normalization. The difference is that ETH addresses are +// case-insensitive, but a normalized ENS name is not the same as its input. +// Whereas we use normalizeETHAddress just to dedup inputs, we use this +// function to check if an ENS name matches its normalized ENS name, as a way +// to prevent homograph attacks. +// See https://docs.ens.domains/dapp-developer-guide/resolving-names#reverse-resolution +const normalizeENSName = (ensName: string) => namehash.normalize(ensName); + // We have a need for querying ENS names from both clients as well as from // keyserver code. On the client side, we could use wagmi's caching behavior, // but that doesn't work for keyserver since it's React-specific. To keep @@ -42,15 +53,15 @@ // 1. Since any address can set a reverse resolution to an arbitrary ENS name // (without permission from the owner), this function will also perform a // "forward resolution" to confirm that the owner of the ENS name has - // mapped it to this address. + // mapped it to this address // 2. We only consider an ENS name valid if it's equal to its normalized // version via eth-ens-namehash. This is to protect against homograph - // attacks. See https://docs.ens.domains/dapp-developer-guide/resolving-names#reverse-resolution + // attacks // If we fail to find an ENS name for an address, fail to confirm a matching // forward resolution, or if the ENS name does not equal its normalized // version, we will return undefined. async getNameForAddress(ethAddress: string): Promise { - const normalizedETHAddress = ethAddress.toLowerCase(); + const normalizedETHAddress = normalizeETHAddress(ethAddress); const cacheResult = this.getCachedNameForAddress(normalizedETHAddress); if (cacheResult) { @@ -72,7 +83,7 @@ return cacheAndReturnResult(undefined); } - const normalizedENSName = namehash.normalize(ensName); + const normalizedENSName = normalizeENSName(ensName); if (normalizedENSName !== ensName) { return cacheAndReturnResult(undefined); } @@ -81,7 +92,7 @@ } getCachedNameForAddress(ethAddress: string): ?string { - const normalizedETHAddress = ethAddress.toLowerCase(); + const normalizedETHAddress = normalizeETHAddress(ethAddress); const cacheResult = this.nameQueryCache.get(normalizedETHAddress); if (!cacheResult) { @@ -98,7 +109,7 @@ } async getAddressForName(ensName: string): Promise { - const normalizedENSName = namehash.normalize(ensName); + const normalizedENSName = normalizeENSName(ensName); if (normalizedENSName !== ensName) { return undefined; } @@ -121,11 +132,11 @@ if (!ethAddress) { return cacheAndReturnResult(undefined); } - return cacheAndReturnResult(ethAddress.toLowerCase()); + return cacheAndReturnResult(normalizeETHAddress(ethAddress)); } getCachedAddressForName(ensName: string): ?string { - const normalizedENSName = namehash.normalize(ensName); + const normalizedENSName = normalizeENSName(ensName); if (normalizedENSName !== ensName) { return undefined; }