语录
用于Node.js,浏览器和边缘运行时间(Cloudflare Workers等)的Avatar解析器库.
重要说明
- 带上你的连锁客户端.
AvatarResolver使用一个ChainClient而不是一个原始供应商, 所以核心船与 捆绑中没有醚。用一行适配器来包装您的 SDK :- 醚:
import { fromEthers } from '@ensdomains/ens-avatar/ethers'→new AvatarResolver(fromEthers(provider)) - 维姆:
import { fromViem } from '@ensdomains/ens-avatar/viem'→new AvatarResolver(fromViem(client)) ethers财务报告和审定财务报表viem为: 可选同伴依赖关系 ——只安装您使用的一款.
- 醚:
- 1.0.4 + 版本 (中文) 使用本地获取 API 实现最大兼容性跨平台,包括 Cloudflare Workers 和其他边缘运行时间.
- 名称分辨率使用 ENS 通用解析器,因此脱链(出道/L2)和ENSIP-10通卡名称解析出框. 醚适配器在一次CCIP-read-aware呼叫中获取所有者地址和avatar/header记录(再加一次,只分类为模棱两可的失败);viem适配器使用viem的
getEnsText财务报告和审定财务报表getEnsAddress(两通来电. 通过适配器推翻合同universalResolverAddress选项. - NFT变相器必须位于客户的链上. 两个适配器都报告其链式ID和
eip155:<chainId>/…另一条链子上的活字ChainMismatch而不是被从错误的链子读取。 习惯ChainClient选择通过执行getChainId()。 。 。 .
平台支持
这个图书馆无缝地跨越:
- ✅ 节点.js 22+( 使用本地取取或 http 适配器)
- ✅ 浏览器 (所有现代浏览器)
- ✅ 云花岗工人
- ✅ 其他边缘运行时间 支持标准获取 API
开始
先决条件
- 准备一个乙醚v6供应商或viem公共客户端.
好样的! SVG sanitization在浏览器,Node.js, 和边缘运行时间(Cloudflare Workers)——不需要DOM多填充(jsdom).
安装
# npm
npm i @ensdomains/ens-avatar
# pnpm
pnpm add @ensdomains/ens-avatar
# yarn
yarn add @ensdomains/ens-avatar
使用量
有乙醚的:
import { JsonRpcProvider } from 'ethers';
import { AvatarResolver, utils as avtUtils } from '@ensdomains/ens-avatar';
import { fromEthers } from '@ensdomains/ens-avatar/ethers';
const provider = new JsonRpcProvider(/* ... */);
const resolver = new AvatarResolver(fromEthers(provider));
async function getAvatar() {
const avatarURI = await resolver.getAvatar('tanrikulu.eth');
// avatarURI = https://ipfs.io/ipfs/QmUShgfoZQSHK3TQyuTfUpsc8UfeNfD8KwPUvDBUdZ4nmR
}
async function getHeader() {
const headerURI = await resolver.getHeader('tanrikulu.eth');
// headerURI = https://ipfs.io/ipfs/QmRFnn6c9rj6NuHenFVyKXb6tuKxynAvGiw7yszQJ2EsjN
}
async function getAvatarMetadata() {
const avatarMetadata = await resolver.getMetadata('tanrikulu.eth');
// avatarMetadata = { image: ... , uri: ... , name: ... , description: ... }
const headerMetadata = await resolver.getMetadata('tanrikulu.eth', 'header');
// headerMetadata = { image: ... , uri: ... , name: ... , description: ... }
const avatarURI = avtUtils.getImageURI({ metadata: avatarMetadata });
// avatarURI = https://ipfs.io/ipfs/QmUShgfoZQSHK3TQyuTfUpsc8UfeNfD8KwPUvDBUdZ4nmR
}
与Viem: (英语)
import { createPublicClient, http } from 'viem';
import { mainnet } from 'viem/chains';
import { AvatarResolver } from '@ensdomains/ens-avatar';
import { fromViem } from '@ensdomains/ens-avatar/viem';
const client = createPublicClient({ chain: mainnet, transport: http() });
const resolver = new AvatarResolver(fromViem(client));
const avatarURI = await resolver.getAvatar('tanrikulu.eth');
支持的外观规格
无
- ERC 721 (韩语)
- ERC1155 (英语)
URIs 参考文献
- HTTP 密码
- 基础64
- 森林小组
选项
缓存 (默认: 已禁用)
const avt = new AvatarResolver(provider, { cache: 300 }); // 5 min response cache in memory
自定义IPFS网关 (默认 : https://ipfs.io (英语)(中文(简体) )
const avt = new AvatarResolver(provider, { ipfs: 'https://dweb.link' });
自定义线网关 (默认 : https://arweave.net (中文(简体) )(中文(简体) )
const avt = new AvatarResolver(provider, { arweave: 'https://arweave.net' });
市场 Api 键 (过失: )
const avt = new AvatarResolver(provider, {
apiKey: {
opensea: 'YOUR_API_KEY',
},
});
URL 拒绝列表 (过失:[])
要拒绝的主机名( 包含子域) 。 条目是主机名,而不是URL;匹配忽略了大小写和后缀点.
const avt = new AvatarResolver(client, {
urlDenyList: ['maliciouswebsite.com'],
});
限制 (故障: 3000 ms, 1 MiB, 5个方向调整, 256 KiB SVG)
timeout——每个HTTP请求作为一个整体的最后期限:方向调整,标题和正文.maxContentLength——取回的响应机构的最大尺寸(如NFT元数据JSON);较大的响应被拒绝.maxRedirects按要求重新定向。 每个hop是一个子请求;默认情况下,一个解析度最多给出了~30个HTTP请求(Cloudflare Workers Free's 50).maxSvgLength内含物的最大长度 /data:SVG活化; 较大者决心null。 。 。 .utils.sanitizeSVG(svg, { maxLength })同样的限制.
数值必须是正整数(maxRedirects 可能为0; NaN, (中文(简体) ), Infinity 而不是无声地使限制失效.
const avt = new AvatarResolver(client, {
timeout: 8000,
maxContentLength: 512 * 1024,
maxRedirects: 3,
maxSvgLength: 128 * 1024,
});
自定义代理( 只有节点 )
您可以为高级用户提供自定义的 HTTP/ HTTPS 代理 :
import http from 'http';
import https from 'https';
const avt = new AvatarResolver(provider, {
agents: {
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
},
});
安全警报:当您提供自定义代理时, ens- avatar 会把它们当作是 不应用 SSRF 保护。您负责确保您的定制代理拥有适当的安全措施,以防止服务器-Side请求伪造攻击.
如果需要海关人员保护,请用 ssrf-req-filter数字 :
import http from 'http';
import https from 'https';
const { requestFilterHandler } = require('ssrf-req-filter');
const avt = new AvatarResolver(provider, {
agents: {
httpAgent: requestFilterHandler(new http.Agent({ keepAlive: true })),
httpsAgent: requestFilterHandler(new https.Agent({ keepAlive: true })),
},
});
允许私人IP (错误:虚假) - 说吧 仅限开发
当您需要访问本地主机或私人网络服务时, 本地开发 :
const avt = new AvatarResolver(provider, {
allowPrivateIPs: true, // Allows localhost, 127.0.0.1, 10.x.x.x, 192.168.x.x, etc.
});
警告声: 这禁用 SSRF 保护 。 只用于本地开发(例如本地IPFS节点,测试服务器). 永远不能在生产中允许。 。 。 .
地方共同发展设想:
- 本地IPFS节点 :
http://127.0.0.1:5001 - 本地 Ethereum 节点 :
http://localhost:8545 - 私人网络上的容器
说明: 如果您提供自定义代理( 在此情况下您控制安全) , 此旗帜会被忽略 .
云花岗工人
- 维姆: 用一个
chain。没有适配器,适配器必须要求RPCeth_chainId它这样做时没有viem的要求去除dupe, 因为当一个要求中开始的许诺在另一个要求中等待时,工人就挂了. - viem运输: 不允许请求批量( q)
http(url, { batch: true }))或(或)batch: { multicall: true }一个客户在各种请求中共享:两人在排队时都持有电话,其承诺最终会被不同的请求所等待,而请求被搁置. - 需要维埃姆 2.35 或晚点 (通用解析器 v3 错误处理).
- 醚: 6.13 或更晚时间 需要(更早的6.x释放从未超过悬浮请求,即使有
FetchRequest.timeout页:1 使用new JsonRpcProvider(url, network, { staticNetwork: true, batchMaxCount: 1 }),所以提供者既不重新发现网络,也不将呼叫按批次进行,这些请求是共享的.
警卫
getMetadata返回已发布的元数据. 字段类image, (中文(简体) ),image_url财务报告和审定财务报表image_data是原始的、攻击者控制的值getAvatar编号 :getHeader编号 :utils.getImageURI以证实和消毒.
SS 保护
所有用户生成的 SVG 内容自动进行消毒以防止 XSS 攻击 :
- 单一的、以解剖法为基础的消毒器(消毒- html 系统 + 键 员额运行 同样的 在浏览器,Node.js,和边缘运行时间(Cloudflare Workers)中——不需要DOM或jsdom.
- 标记和属性仅限于严格的 SVG 允许列表( 否)
script, (中文(简体) ),foreignObject,事件处理器,或外部href编号 :use编号 :image目标. - CSS 输入
style属性和<style>块被过滤到属性允许列表,每个 CSS 函数必须在函数允许列表上(colors,calc编号 :min编号 :max编号 :clamp,转换,过滤函数,基本形状,计时函数;不var, (中文(简体) ),env, (中文(简体) ),attr, (中文(简体) ),src, (中文(简体) ),image-set, ...) 演示文稿属性(fill, (中文(简体) ),stroke, (中文(简体) ),filter, (中文(简体) ),mask, (中文(简体) ),clip-path, (中文(简体) ),marker-*) 检查方式相同. url()到处都是只有内部的 属性,style属性和<style>块:仅可参考#id在文档中,所以在外部加载(跟踪,过滤)被屏蔽时,梯度,剪接,口罩,过滤和标记会继续工作.href(和xlink:href,该名称改为href)同样仅限于:#id附加时data:image/*打开<image>编号 :<feImage>。 。 。 .- 每个
<svg>元素被绑定在 SVG 命名空间上 (xmlns),因此无法切换到 XHTML. - 常见编辑器输出幸存: Figma 过滤链(
fe*原始语中包含.feDropShadow), Illustrator (英语)<style>块被一个 CDATA 区域包裹,梯度继承通过href,以及任何元素的演示文稿属性.
拉斯特·阿凡达斯(Raster avatarspng, (中文(简体) ), jpeg, (中文(简体) ), gif, (中文(简体) ), webp,是否作为 data: URI或远程 URL,在验证后传递——它们作为惰性 <img> 源,而不需重写.
内置对远程 SVGs
SVG可视器的处理取决于其来源:
- 内置 SVG 链上
<svg>…</svg>记录和记录data:image/svg+xmlURI —— 返回 已经消毒。 。 。 .getAvatar编号 :utils.getImageURI手拿回来data:image/svg+xml;base64,…URI 有脚本,事件处理器,以及外部引用被剥去. - 远程 SVGs - 其记录是:
http(s)在 SVG 中指向的 URL 返回 作为原始的 URL, 未卫生. ens-avatar 是解析器: 对于远程图像, 它返回 地点 图像是活的,而不是它的字节, 所以安全着陆的策略是你的选择。 (解析期间进行的内容类型检查不是安全保障——服务器可以发布广告)image/svg+xml并仍然返回一个敌对的身体
当您制作一个远程 SVG时, 要么装入一个已经装入沙盒的上下文中 <img src> 标签, CSS background-image,或一个 <image href> 在另一个SVG内部,没有一个执行脚本或加载外部子资源—— 或者说,如果获取字节来服务,存储或内置,则首先用库用于内置SVG的同一种引擎进行消毒:
import { utils as avtUtils } from '@ensdomains/ens-avatar';
// `avatarUrl` came back from resolver.getAvatar(...) and points at an SVG
const svg = await fetch(avatarUrl).then(res => res.text());
const safeSvg = avtUtils.sanitizeSVGDocument(svg, { maxLength: 1024 * 1024 });
// safeSvg: a standalone '<svg …>…</svg>' document, or null if nothing usable remains
sanitizeSVGDocument(svg, { maxLength }): string | null 只返回根 <svg> 元素,因此结果是一个有效的独立文档(在根外的文本,例如 DOCTYPE 的内部子集被删除),并保持原样的白空间. maxLength 默认为 256 KiB. sanitizeSVG(svg, { maxLength }) 是下级函数: 它返回已静态标记为字符串( q)'' ),包括根外的任何文字。 两者都拒绝前方格式错误的输入: a < 不启动标签、注释、 CDATA 部分、 处理指令或声明是无效的 XML( 没有 SVG 渲染器会显示它) , 并且是最昂贵的消毒输入 .
对于SSRF安全获取器(私人地址屏蔽、重定向重新验证、尺寸上限-见下文),使用库自己的获取器,而不是全局的获取器 fetch数字 : const { get } = avtUtils.createFetcher();。 。 。 .
SSRF 保护
默认, ens- avatar 包括内置保护, 防止服务器- Side Request Forgery (SSRF) 在Node.js环境中的攻击. 这阻止了恶意角色使用Avatar URL来探测内部网络.
默认行为 (建议生产):
- 仅限
http:财务报告和审定财务报表https:获取 URL - QQ 仅允许全球roubable unicast地址 : 回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回回
- QQ 每一次重定向 hop 重新验证, 并请求除
Accept编号 :Range(API 密钥) 一旦重定向离开原始来源就会被丢弃 - * 每项请求都有一个涵盖主体的最后期限,响应机构被限制大小,方向调整有限(见 限制(中文(简体) )
自定义代理: 如果您提供了自己的HTTP/HTTPS代理, ens-avatar将会在不应用SSF保护的情况下使用它们 。 您负责您的定制代理.
地方发展设置 allowPrivateIPs: true 当您需要访问本地服务(例如本地IPFS节点)时,可禁用SSF保护。 不要用这个生产.
见 自定义代理 详细情况.
看守阅读的CCIP
ens-avatar 从未让 CCIP 阅读 (EIP-3668) 请求自己: 脱链取景由 : 链式客户端( 供应商或viem) ccipRead),与客户端的HTTP堆栈和限制. 一个名字的网关是攻击者控制的输入,所以在服务器上:
用你的客户的钩子保护它——viem: ccipRead: { request } 打开 createPublicClient; 醚类: FetchRequest.registerGetUrl - 里面还有:
- 检查您实际要求的 URL,即替换后
{sender}财务报告和审定财务报表{data}输入网关模板。 检查模板还不够 :https://{data}/有数据0x7f000001目标127.0.0.1启用avtUtils.validateUrl(url)。 。 。 . - 别让
fetch跟踪方向. 使用redirect: 'manual'运行validateUrl每页Location跳,有一个跳限. - 每项请求都有约束力 (超时,响应大小)和整个取景点:尝试和嵌入取景点的网关 URL数量(采行者停止于 10,
MAX_CCIP_REDIRECTS;viem没有自己的限制). - 在节点上,还检查已解决的地址.
validateUrl仅查看 URL: 一个网关主机名,其DNS在私人地址的指针通过它. 通过一个检查它连接的IP的代理(例如undici),使网关请求Agent带有验证lookup,或ssrf-req-filter) (中文(简体) ).
服役的阿凡达人
如果您从自己的来源代理或保留了可视化字节, 请发送 X-Content-Type-Options: nosniff 和沙箱式CSP,例如: Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox,因此敌对文件不能作为页面运行在您的域名上.
安全分遣队
虽然 " 救援者 " 执行安全措施,帮助防范XSS和SSF的袭击, 您对申请的安全负最终责任我们强烈建议:
- 在投入生产前自行进行安全审计
- 执行适合您使用的更多安全层
- 不断更新库以接收安全补丁
- 处理用户生成的内容时遵循安全最佳做法
- 正确配置所有与安全有关的环境选项
这个图书馆是在没有保证的情况下提供的"as-is". 维护者对使用该图书馆的应用程序中的任何安全弱点不负责任.
演示
-
用 INFURA KEY env 变量创建.env 文件
-
构建库
-
节点示例
node example/node.js ENS_NAME
- 浏览器示例
pnpm build:demo
http-server example