编码、哈希和加密是三件不同的事
这个页面上几乎所有的误解,都来自把这三者当成可以互换的东西,所以值得把它们好好区分开来。
编码是可逆的,其中没有任何秘密。Base64的作用是让二进制数据通过只接受文本的渠道——邮件附件、data URI、JSON字符串——它把每3个字节表示成4个字符,所以结果总会大约大三分之一。它没有密钥。任何看到这个字符串的人都能一步解码,包括在本站上。Base64不是安全措施,也算不上像样的混淆,更不能向任何认真查看的人隐藏什么。百分号编码是用于URL的同类东西,另一个常见bug也出在这里:对放进查询字符串的单个值进行编码,和整理一个已拼好的完整URL是两种不同的操作,因为只有前者会转义构成URL结构的&、=、?和/。一旦搞错,“Smith & Sons”到达服务器时就会变成两个参数,而不是一个值。
哈希是单向的,同样没有密钥。哈希接受任意输入,生成一个固定长度的指纹,而且无法撤销——不是因为它被加密了,而是因为信息已经丢失了。您能做的是猜测输入再进行比对,而这正是MD5、SHA-1和SHA-256不适合存储密码的原因:它们很快,一块显卡每秒能计算数十亿次,被盗的哈希表会以这个速度被破解。密码需要的是刻意放慢、加盐的函数——bcrypt、scrypt或Argon2。另外,MD5和SHA-1还因另一个原因而被攻破:现在已经可以刻意构造出哈希值相同的两个不同文件,所以两者都无法证明某个文件就是您期望的那个文件,不过用它们来发现意外损坏的下载文件仍然没问题。
加密才是有密钥的那一个,而这些工具都不做加密。这不是遗漏。要正确地做加密,就需要管理密钥,而网页不是存放密钥的地方。
JWT是签名的,不是加密的,这一点总让人栽跟头。令牌的头部和载荷是Base64URL——任何持有令牌的人都能读取,不需要密钥。签名能阻止别人篡改令牌,却完全无法阻止别人读取它,所以任何机密信息都不应放进载荷。由此可见,解码一个令牌对它的真伪什么都证明不了:任何人都可以随意写入任何声明,再做Base64编码。验证意味着用签发方的密钥重新计算签名,这是您的服务器要做的事,网站绝不应该向您索要。因此我们的解析工具拒绝暗示任何验证结论,只标出表明存在问题的情形——令牌已过期、令牌根本没有过期时间,以及算法为“none”,后者是一个有据可查的身份验证绕过漏洞,而不是什么奇闻。
JSON有一个大多数格式化工具会不知不觉踩进去的数字精度限制。JSON本身并不限制数字大小,但JavaScript的数字是64位浮点数,所以整数只有在不超过9,007,199,254,740,991时才能保持精确。Twitter帖子ID、Discord雪花ID、64位数据库主键或银行账号都比这个数大,把它们交给常见的“一行代码”格式化工具——先parse再stringify——末尾几位数字就会被改掉。结果看上去仍是一个合理的数字,这正是危险所在:7205759403792793600会悄无声息地变成7205759403792793000。我们的JSON工具会按您粘贴的原始数字重新输出,并告诉您有多少个数字被保护了下来。
UUID的版本说明的是它如何生成,而不是它能保证什么。没有任何机构登记UUID或强制保证其唯一性;唯一性是概率上的,版本号告诉您这些位来自哪里。第4版是由浏览器的加密随机数生成器产生的122位随机数,因此无法被猜到,适合用于重置密码链接、邀请码以及任何需要保密的东西——但不适合用作数据库主键,因为随机的键会分散到有序索引的各处,造成碎片化。第7版把48位毫秒级时间戳放在最前面,所以新键会像自增整数一样追加到索引末尾,同时依然全局唯一。代价是,任何拿到v7 UUID的人都能直接看出它是何时创建的,精确到毫秒。
浏览器确实不适合的情况
这十三款工具都不会把任何内容发送到任何地方。不发出任何请求,不记录任何日志,断开网络后页面依然照常工作——正因如此,面对满是客户记录的API响应,或者您不会用邮件发给任何人的令牌,它们也可以放心使用。这是支持它们的真实理由。下面是反对它们的真实理由。
把正在使用的生产环境密钥粘贴到网页里,是一个最好不要养成的习惯——包括本站在内。我们关于隐私的说法是真的,但保护您的不是说法,而是代码的行为;对待任何网页,唯一明智的态度都是去验证,而不是去信任。验证不花任何成本:页面加载完成后断开网络,看着这些工具继续工作。需要服务器的工具会停止运行。如果某个凭据仍然有效,而您已经把它粘贴到了一个无法审查的地方,正确的做法是更换它,而不是琢磨它是否安全。对于眼下仍然有效的令牌,在本地解码——用您所用编程语言自带的Base64函数,或者在终端里敲两行命令——比使用任何网站都更稳妥,包括我们。
Schema校验。我们的JSON和XML工具检查的是文档格式是否正确,这和它是否符合某个Schema是两个问题。按JSON Schema或XSD进行校验,需要获取并应用一个Schema文件,而这需要发出网络请求,这些页面刻意从不这样做。JSON Schema请用ajv,XSD和DTD请用xmllint;两者都免费,而且都比网页做得更好。
真正的大文件或流式数据。文档会被整个载入内存,格式化时有时还会占用两份,所以上限就是标签页。几MB的内容瞬间完成,几百MB就不行了。jq能流式处理任何浏览器都打不开的几GB大小的JSON文件,ripgrep搜索整个代码仓库的速度比您粘贴一个文件还快。哈希计算面临同样但更严格的限制:浏览器的加密接口不支持增量摘要,所以整个文件必须一次性装入内存——DVD镜像会失败,而sha256sum、shasum或certutil可以轻松地流式处理。
自动化和CI。这些工具只在有人点击时才运行。格式化仓库里的每个文件、为测试夹具生成一万个UUID、在流水线中检查JSON,这些都应该写成脚本:jq、xmllint、uuidgen、openssl和base64命令在大多数机器上都已预装。
验证任何签名内容。签名验证、证书链以及任何需要私钥的操作,都应在您的服务器上用持续维护的库来完成。本页面会告诉您令牌声称了什么,但绝不会告诉您令牌是真的;任何声称不需要您的密钥就能做到这一点的网站,都不值得信任。
名字相似的工具怎么选
JSON格式化与JSON校验。同一个解析器,回答不同的问题。格式化工具用于阅读和调整已经能正常解析的文档——缩进、压缩,并保持大数字不变。校验工具用于无法解析的文档,它给出的是行号、列号、出错字符(下方用插入符号标出),以及属于四种常见原因中的哪一种。如果您正对着“Unexpected token”发愁,您需要的是校验工具。XML格式化和XML校验也是同样的分工。
Base64解码与JWT解析。JWT由三段Base64URL组成,所以通用解码工具也能显示这几段内容。JWT页面则会帮您拆分开来、格式化JSON,把过期时间、签发时间和生效时间这几个声明从Unix时间戳转换成您所在时区的真实日期,标注已注册的声明,并对过期和算法为“none”的情况发出警告。Base64数据块用通用解码工具,令牌用JWT页面。
Base64编码与URL编码。两者都生成“安全”的文本,但用途不同。Base64把任意字节转换成文本,代价是体积增大33%,而且标准Base64在URL中根本不安全——其中的+、/和=在URL里都有特殊含义,这就是Base64URL存在的原因。百分号编码让可读的文本保持可读,只转义会破坏URL的字符,搜索词、邮箱地址或重定向目标需要的正是这种编码。把整个文件编码进查询字符串,通常说明它本该放进请求体里。
哈希值生成器与UUID生成器。哈希值由输入推导而来,相同的输入总是得到相同的值——这正是它能充当指纹的原因,适合校验下载文件或对相同内容去重。UUID是全新的随机数,与任何东西都没有关联,所以永不重复。如果您希望相同的内容得到相同的标识符,就计算哈希;如果您需要一个新的标识符,就生成UUID。
二维码生成器与条形码生成器。二维码是一种二维网格,能容纳任意文本——网址、Wi-Fi凭据、电话号码——用手机摄像头读取。条形码页面生成的是零售和物流用的一维码:EAN-13、EAN-8、UPC-A、ITF-14、Code 128和Code 39,会自动计算零售校验位;错误或位数不足的号码会被拒绝,而不是被悄悄补齐成另一件商品的有效条形码。