{"version":"https://jsonfeed.org/version/1","title":"CXPLAY World","description":"CXPLAY's Blog.","favicon":"https://blog.cxplay.org/favicon.ico","home_page_url":"https://blog.cxplay.org","feed_url":"https://blog.cxplay.org/feed.json","author":{"name":"CXPLAY"},"items":[{"id":"https://blog.cxplay.org/works/glade/","url":"https://blog.cxplay.org/works/glade/","title":"林间空地","content_html":"<h2 id=\"序\"><a href=\"#序\"></a>序</h2>\n<p>在这里, 平坦的地方不一定都是草原, 因为被风或其他什么东西带来的种子除了会长成草, 还可能长成树, 即使没有外来的种子, 一些草也迟早会长成木. 森林越加茂密, 小型的草木越加难以存活, 在草甸植物的视角下, 森林就是它们的 &quot;沙漠&quot;, 林间空地才是这些荒漠中的 &quot;绿洲&quot;.</p>\n<h2 id=\"正文\"><a href=\"#正文\"></a>正文</h2>\n<p>斗篷知道这已经是在巨墙之上, 也见过长着草的雪原, 但还是没有人能想象这样的森林会在这里被发现. 如果不是有人亲眼所见并口耳相传吸引更多的斗篷来此见证, 也不会有人相信在这片立锥之地居然会有森林.</p>\n<p>在进入森林的路上, 斗篷都会互相告诫彼此森林中的守则, 因为视野受限, 高度再次被扩展, 原来在平原上的生存守则也不再适用. 这森林中, 斗篷们最为禁忌的事情有两条: 失去同类和遭遇同类. 要么孤身进入森林, 要么三位及以上进入森林, 如果变为两位, 那么尽快分开可能比在一起更加安全, 因为在穿越森林时刻警惕环境的时候一个斗篷无法同时保证身边的唯一一位斗篷经过长时间的穿越后还是是进入森林的那一位, 如果是三人及以上就能尽量确保身边人的安全. 斗篷之间能确认身份的办法除了约定好的暗号以及行为举止上独特的特征外几乎没有其他更有效的办法, 在昏暗密林里面难度会更高.</p>\n<p>森林中的主要威胁并非是其他斗篷, 而是来自头顶之上, 也就是那些长时间积攒的积雪. 森林的树木尺寸非同寻常, 仅仅是从枝桠上落下的积雪都是一场局部雪崩, 积雪随时会因为一点震动后垮塌而淹没底下的一切, 在这里轻声细语是必要的. 在这里要解决掉一个斗篷最好的办法也是故意制造雪崩, 并不是亲自出手, 两位陌生斗篷相遇也并不会更加优先爆发冲突, 而是保持距离后互相远离. 毕竟斗篷们也知道, 在这里消灭一个其他的斗篷没有太多意义, 引发的雪崩很容易产生连锁反应, 很难不波及自身.</p>\n<p>这里最安全的地方是罕见的林间空地, 森林中的空地就和平原没有两样, 身处空地之中还能借住周围积雪的树木建立临时庇护所, 因此和平原上一样, 林间空地往往也有很多驻守在那里的斗篷, 不再前进和后退, 林间空地也是斗篷们重新募集和集结的地方. 但就和沙漠中的绿洲一样, 森林也没有固定的通往林间空地的路线, 曾有斗篷尝试做过标记, 但无一例外的最后都被其他斗篷毁掉了路标, 也有斗篷故意制造假的路标, 因此除了真的拥有超群经验和记忆的斗篷, 其他的宣称通往林间空地的路线指引几乎都是不可信的.</p>\n<p>斗篷进入森林的主要动机要么是完全穿越森林到达新的平原, 要么就是寻找林间空地. 作为定居点而言, 距离巨墙边缘越近的地方越嘈杂纷乱, 因此森林成了一个被新发现的屏障, 致命的雪崩阻挡了不愿意守规矩的斗篷. 林间空地也由于周围巨型树木环绕而免于被狂风侵袭, 本身就是很好的 &quot;风水宝地&quot;, 但由于始终还是在森林中, 周围巨木的环绕下会让不少斗篷产生不好的回忆, 因此林间空地也不是所有进入森林的斗篷都青睐和愿意长期停留的地方.</p>\n<p>但是穿越森林的斗篷为了那片更加静谧的平原, 也没有任何一位斗篷拥有 &quot;森林另一头真的有新平原&quot; 的消息来佐证. 所有愿意进入森林的斗篷都认为, 是去往了新平原的斗篷都不会再选择回头或是留在了森林中. 所有来到森林边缘的斗篷都相信: 如果这片森林有一个边缘, 那么就一定有另一个边缘, 如果连巨墙和平原都不是连绵不绝的, 那么在这之上生长的森林肯定也不会是.</p>\n<p>目前能确认的只有斗篷们, 森林入口边缘, 雪崩和林间空地在森林中是切实存在的.</p>\n<h2 id=\"后记\"><a href=\"#后记\"></a>后记</h2>\n<p>按理说, 森林是种比草原更加立体的生态群系, 理应会产生比平原上更多自然的生物, 但是还没有斗篷发现森林中有除了草木之外的其他生物. 很多斗篷都在寻找诸如鸟兽鱼虫这样的自然生物, 如果斗篷本身无法造物, 且斗篷都是被制造的一种物, 那么应该还会有更多的新物种, 而在这里的鸟兽鱼虫也变成了新物种, 没有被发现的先例.</p>\n<p>比起新的物种, 新的地形和生态群系更加稀有, 也许在森林的另一边, 有比林间空地更加有趣的地形.</p>\n","content_text":"序 在这里, 平坦的地方不一定都是草原, 因为被风或其他什么东西带来的种子除了会长成草, 还可能长成树, 即使没有外来的种子, 一些草也迟早会长成木. 森林越加茂密, 小型的草木越加难以存活, 在草甸植物的视角下, 森林就是它们的 &quot;沙漠&quot;, 林间空地才是这些荒漠中的 &quot;绿洲&quot;. 正文 斗篷知道这已经是在巨墙之上, 也见过长着草的雪原, 但还是没有人能想象这样的森林会在这里被发现. 如果不是有人亲眼所见并口耳相传吸引更多的斗篷来此见证, 也不会有人相信在这片立锥之地居然会有森林. 在进入森林的路上, 斗篷都会互相告诫彼此森林中的守则, 因为视野受限, 高度再次被扩展, 原来在平原上的生存守则也不再适用. 这森林中, 斗篷们最为禁忌的事情有两条: 失去同类和遭遇同类. 要么孤身进入森林, 要么三位及以上进入森林, 如果变为两位, 那么尽快分开可能比在一起更加安全, 因为在穿越森林时刻警惕环境的时候一个斗篷无法同时保证身边的唯一一位斗篷经过长时间的穿越后还是是进入森林的那一位, 如果是三人及以上就能尽量确保身边人的安全. 斗篷之间能确认身份的办法除了约定好的暗号以及行为举止上独特的特征外几乎没有其他更有效的办法, 在昏暗密林里面难度会更高. 森林中的主要威胁并非是其他斗篷, 而是来自头顶之上, 也就是那些长时间积攒的积雪. 森林的树木尺寸非同寻常, 仅仅是从枝桠上落下的积雪都是一场局部雪崩, 积雪随时会因为一点震动后垮塌而淹没底下的一切, 在这里轻声细语是必要的. 在这里要解决掉一个斗篷最好的办法也是故意制造雪崩, 并不是亲自出手, 两位陌生斗篷相遇也并不会更加优先爆发冲突, 而是保持距离后互相远离. 毕竟斗篷们也知道, 在这里消灭一个其他的斗篷没有太多意义, 引发的雪崩很容易产生连锁反应, 很难不波及自身. 这里最安全的地方是罕见的林间空地, 森林中的空地就和平原没有两样, 身处空地之中还能借住周围积雪的树木建立临时庇护所, 因此和平原上一样, 林间空地往往也有很多驻守在那里的斗篷, 不再前进和后退, 林间空地也是斗篷们重新募集和集结的地方. 但就和沙漠中的绿洲一样, 森林也没有固定的通往林间空地的路线, 曾有斗篷尝试做过标记, 但无一例外的最后都被其他斗篷毁掉了路标, 也有斗篷故意制造假的路标, 因此除了真的拥有超群经验和记忆的斗篷, 其他的宣称通往林间空地的路线指引几乎都是不可信的. 斗篷进入森林的主要动机要么是完全穿越森林到达新的平原, 要么就是寻找林间空地. 作为定居点而言, 距离巨墙边缘越近的地方越嘈杂纷乱, 因此森林成了一个被新发现的屏障, 致命的雪崩阻挡了不愿意守规矩的斗篷. 林间空地也由于周围巨型树木环绕而免于被狂风侵袭, 本身就是很好的 &quot;风水宝地&quot;, 但由于始终还是在森林中, 周围巨木的环绕下会让不少斗篷产生不好的回忆, 因此林间空地也不是所有进入森林的斗篷都青睐和愿意长期停留的地方. 但是穿越森林的斗篷为了那片更加静谧的平原, 也没有任何一位斗篷拥有 &quot;森林另一头真的有新平原&quot; 的消息来佐证. 所有愿意进入森林的斗篷都认为, 是去往了新平原的斗篷都不会再选择回头或是留在了森林中. 所有来到森林边缘的斗篷都相信: 如果这片森林有一个边缘, 那么就一定有另一个边缘, 如果连巨墙和平原都不是连绵不绝的, 那么在这之上生长的森林肯定也不会是. 目前能确认的只有斗篷们, 森林入口边缘, 雪崩和林间空地在森林中是切实存在的. 后记 按理说, 森林是种比草原更加立体的生态群系, 理应会产生比平原上更多自然的生物, 但是还没有斗篷发现森林中有除了草木之外的其他生物. 很多斗篷都在寻找诸如鸟兽鱼虫这样的自然生物, 如果斗篷本身无法造物, 且斗篷都是被制造的一种物, 那么应该还会有更多的新物种, 而在这里的鸟兽鱼虫也变成了新物种, 没有被发现的先例. 比起新的物种, 新的地形和生态群系更加稀有, 也许在森林的另一边, 有比林间空地更加有趣的地形.","summary":"序 在这里, 平坦的地方不一定都是草原, 因为被风或其他什么东西带来的种子除了会长成草, 还可能长成树, 即使没有外来的种子, 一些草也迟早会长成木. 森林越加茂密, 小型的草木越加难以存活, 在草甸植物的视角下, 森林就是它们的 &quot;沙漠&quot;, 林间空地才是这些荒漠中的 &quot;绿洲&quot;. 正文 斗篷知道这已经是在巨墙之上, 也见过长着草的雪原, 但还是没有人能想象这样的森林会在这里被发现. 如果不是有人亲眼所见并口耳相传吸引更多的斗篷来此见证, 也不会有人相信在这片立锥之地居然会有森林. 在进入森林的路上, 斗篷都会互相告诫彼此森林中的守则, 因为视野受限, 高度再次被扩展, 原来在平原上的生存守则也不再适用. 这森林中, 斗篷们最为禁忌的事情有两条: 失去同类和遭遇同类. 要么孤身进入森林, 要么三位及以上进入森林, 如果变为两位, 那么尽快分开可能比在一起更加安全, 因为在穿越森林时刻警惕环境的时候一个斗篷无法同时保证身边的唯一一位斗篷经过长时间的穿越后还是是进入森林的那一位, 如果是三人及以上就能尽量确保身边人的安全. 斗篷之间能确认身份的办法除了约定好的暗号以及行为举止上独特的特征外几乎没有其他更有效的办法, 在昏暗密林里面难度会更高. 森林中的主要威胁并非是其他斗篷, 而是来自头顶之上, 也就是那些长时间积攒的积雪. 森林的树木尺寸非同寻常, 仅仅是从枝桠上落下的积雪都是一场局部雪崩, 积雪随时会因为一点震动后垮塌而淹没底下的一切, 在这里轻声细语是必要的. 在这里要解决掉一个斗篷最好的办法也是故意制造雪崩, 并不是亲自出手, 两位陌生斗篷相遇也并不会更加优先爆发冲突, 而是保持距离后互相远离. 毕竟斗篷们也知道, 在这里消灭一个其他的斗篷没有太多意义, 引发的雪崩很容易产生连锁反应, 很难不波及自身. 这里最安全的地方是罕见的林间空地, 森林中的空地就和平原没有两样, 身处空地之中还能借住周围积雪的树木建立临时庇护所, 因此和平原上一样, 林间空地往往也有很多驻守在那里的斗篷, 不再前进和后退, 林间空地也是斗篷们重新募集和集结的地方. 但就和沙漠中的绿洲一样, 森林也没有固定的通往林间空地的路线, 曾有斗篷尝试做过标记, 但无一例外的最后都被其他斗篷毁掉了路标, 也有斗篷故意制造假的路标, 因此除了真的拥有超群经验和记忆的斗篷, 其他的宣称通往林间空地的路线指引几乎都是不可信的. 斗篷进入森林的主要动机要么是完全穿越森林到达新的平原, 要么就是寻找林间空地. 作为定居点而言, 距离巨墙边缘越近的地方越嘈杂纷乱, 因此森林成了一个被新发现的屏障, 致命的雪崩阻挡了不愿意守规矩的斗篷. 林间空地也由于周围巨型树木环绕而免于被狂风侵袭, 本身就是很好的 &quot;风水宝地&quot;, 但由于始终还是在森林中, 周围巨木的环绕下会让不少斗篷产生不好的回忆, 因此林间空地也不是所有进入森林的斗篷都青睐和愿意长期停留的地方. 但是穿越森林的斗篷为了那片更加静谧的平原, 也没有任何一位斗篷拥有 &quot;森林另一头真的有新平原&quot; 的消息来佐证. 所有愿意进入森林的斗篷都认为, 是去往了新平原的斗篷都不会再选择回头或是留在了森林中. 所有来到森林边缘的斗篷都相信: 如果这片森林有一个边缘, 那么就一定有另一个边缘, 如果连巨墙和平原都不是连绵不绝的, 那么在这之上生长的森林肯定也不会是. 目前能确认的只有斗篷们, 森林入口边缘, 雪崩和林间空地在森林中是切实存在的. 后记 按理说, 森林是种比草原更加立体的生态群系, 理应会产生比平原上更多自然的生物, 但是还没有斗篷发现森林中有除了草木之外的其他生物. 很多斗篷都在寻找诸如鸟兽鱼虫这样的自然生物, 如果斗篷本身无法造物, 且斗篷都是被制造的一种物, 那么应该还会有更多的新物种, 而在这里的鸟兽鱼虫也变成了新物种, 没有被发现的先例. 比起新的物种, 新的地形和生态群系更加稀有, 也许在森林的另一边, 有比林间空地更加有趣的地形.","date_published":"2025-10-30T14:37:00.000Z","tags":["密语瓶"]},{"id":"https://blog.cxplay.org/works/my-internet-chronicles/","url":"https://blog.cxplay.org/works/my-internet-chronicles/","title":"我的互联网编年史: 从田园牧歌到草木皆兵","content_html":"<h2 id=\"前言\"><a href=\"#前言\"></a>前言</h2>\n<p>注意, 本文并非完全纪实, 越到后面会越接近科幻创作, 请读者知悉.</p>\n<blockquote>\n<p>我又不是先知, 我只能写点暴论了.</p>\n</blockquote>\n<h2 id=\"web-1-0\"><a href=\"#web-1-0\"></a>Web 1.0</h2>\n<blockquote>\n<p><strong>创造模式 &amp; 黄金时代: DoS (1990 年代 ~ 2000 年代早期)</strong></p>\n</blockquote>\n<p>这是一个网络世界的拓荒年代, 但互联网是人造的, 是如同没有原住民和原生生物威胁的新大陆一般, 只有无限可能与未被触及的疆域. 人工智能刚刚进入低谷, 而互联网却充满着希望, 使得这两个事物看起来毫不相干. 资源在当时显得富足而慷慨, 但这些资源却被极少数人所拥有, 这些人被称为互联网的 &quot;先锋&quot;, 互联网的 &quot;缔造者&quot;, 他们是「极客」, 是大学的研究者, 是资金雄厚的先行者. 这一时期的互联网的门槛极高, 世界也还没有与互联网融合, <ruby>万维网<rt>World Wide Web</rt></ruby>的概念才刚刚诞生. 也因此形成了一个小而紧密的社区. 只要这里的人愿意, 他们就可以做到现实世界彼此认识, 甚至在网络上建立深厚的友谊. 他们在自己的虚拟领土上辛勤耕耘, 创作个人网站和论坛, 用朴素的 HTML 记录自己的东西, 然后邀请他人来观赏, 交流.</p>\n<p>这个时代的网络技术简单而纯粹, 从未过多考虑过安全与加密. 计算机与计算机之间的连接就像人与人之间的握手, 一切建立在信任与坦诚之上. 此时的 HTTP 协议如同在广场上用传声筒交流一样简单直接; SMTP 像真实邮差递送信件一样毫无遮掩. 网络的架构师们默认进入这片数字世界的人都是怀着善意的探险者, 恶意攻击比如 DoS 虽然存在, 但往往只是这群技术爱好者之间的恶作剧并不认为是真正的威胁, 尽管如此这也让网络攻防的概念开始萌芽.</p>\n<p>IPv4 地址在当时仿佛取之不尽用之不竭的「自然资源」, 每个设备一开始接入互联网就可以拥有一个独一无二的身份标识. 互联网的第一批公司在硅谷中诞生, 它们在网络疆域上圈地跑马, 建设自己的 &quot;数字农场&quot; 与 &quot;虚拟工厂&quot;, 为早期网络用户提供浏览器, 电子邮件和门户网站等这一时代的基础服务. 与此同时, 互联网的行动纲领 —— RFC 被起草, 这些文档是这个网络世界的 &quot;宪法&quot;, 为未来的技术发展奠定了基石. 开源运动也在这一时期萌芽, Linux 等项目的出现让技术不再是少数人的专属.</p>\n<p>这是一段黄金岁月, 互联网如同一片沃土, 充满了创造与分享的激情. 然而, 信任的基石也埋下了隐患, 当更多人进入这片土地时, 原始的单纯将不再适用, 总会有一批人变成最早期的原住民. 这一时代由风险投资主导了互联网经济的蛮荒, 最终表现在了末期爆发的互联网经济泡沫, 但还是让很多后世的巨头在泡沫之中成长起来, 这一经济模式还是成为了后世互联网经济的核心.</p>\n<h2 id=\"web-2-0\"><a href=\"#web-2-0\"></a>Web 2.0</h2>\n<blockquote>\n<p><strong>生存模式 &amp; 白银时代: DDoS (2000 年代 ~ 2010 年代)</strong></p>\n</blockquote>\n<p>二十一世纪到来, 互联网从精英的游乐场转变为全民的竞技场. 全球化运动达到巅峰, 使得互联网公司们有机会开始在全世界范围内攻城略地, 运用一切手段割据一方, 资源逐渐集中化. 大众互联网形成, 普通人也可以通过越来越便宜的宽带连接到网络, 市场接近饱和, 竞争变得异常激烈. 新生代互联网公司崛起, 它们不再满足于单纯提供服务, 而是通过广告, 数据和用户黏性构建自己的商业帝国, 形成了坚不可摧的 &quot;数字城墙&quot;, 互联网大陆板块开始裂解.</p>\n<p>巨头主导的互联网普及让内容生产模式发生革命性变化, 由 PGC 逐渐被 UGC 取代, 社交媒体和视频平台让每个人都成为信息的创造者. 信息孤岛开始出现, 用户开始被算法推荐缠身. 广告和金融市场收益开始超越用户生态的直接收益, 对流量和注意力的追求超越对艺术和文化的追求, 互联网公司变得更关心如何从用户身上榨取价值, 而非为用户创造价值. 互联网成为一个真正以机器运行速度驱使人类进步的行业, 传统行业不得不开始寻求融合, 被公认为「第三次工业革命」.</p>\n<p>随着互联网的真正普及, IPv4 资源逐渐枯竭, NAT 成为资源匮乏地区的常态, IPv6 终于开始痛苦且缓慢地推广. 网络攻击从单机的 DoS 演变为分布式的 DDoS, 网络安全开始成为互联网世界的核心议题. 为满足市场, 网络攻击和网络安全开始成为一种服务而被制造, 供应和消费.</p>\n<p>有人开始反思互联网过于集中化的弊端, 去中心化的概念开始兴起, 比特币诞生. 开源运动如火如荼, 互联网成为政治运动的沃土, 全球协作与共享成为新的信仰. 但互联网的主导权依然掌握在少数科技巨头手中, 用户的数据和隐私仍然被肆意收集交易, 互联网中的人与人的信任开始崩塌.</p>\n<p>白银时代, 是大部分普通人才能接触到的时代. 有部分人奉黄金时代的先驱为神而去膜拜, 这也确实坚定了无数后人对互联网的开发的信念. 但是人人都能「淘金」的时代已经过去, 互联网第一次感受到了激烈的生存和竞争压力, 资源变成稀缺品, 为有限资源而开始的过度竞争开始显现. 卖淘金使用的「铲子」变得比淘金本身更加容易获利, AWS 为代表的集中式云计算出现, 它们为后世互联网的淘金者们提供了数量最多的铲子. 此时, 人工智能领域走出 &quot;低谷&quot;, 理论突破出现, 此时的开始富集的算力资源和数据也为深度学习提供了最早的养料.</p>\n<h2 id=\"web-3-0\"><a href=\"#web-3-0\"></a>Web 3.0</h2>\n<blockquote>\n<p><strong>塔防模式 &amp; 青铜时代: 人工智能 (2010 年代 ~ 至今)</strong></p>\n</blockquote>\n<p>人与人, 人与机器, 机器与网络之间默认不再信任, 信任链条只能通过密码学技术来构建, Web 1.0 时代的网络基础变成网络安全障碍. 基于密码学的零信任模型和区块链技术诞生试图重新定义互联网的人机关系. 区块链甚至开始尝试对金融系统, 公司治理乃至社会结构的进行颠覆性改变, 然后发明了<ruby>去中心化金融<rt>DeFi</rt></ruby>, <ruby>非同质化代币<rt>NFT</rt></ruby>等概念. 逆全球化运动开始, 互联网「无中介」概念诞生, 数字主权运动萌芽.</p>\n<p>互联网的最大漏洞变成了人本身, 社会工程学攻击泛滥, 借助钓鱼邮件, 虚假身份和心理操控成为这一时代的网络攻击者绕过数字甚至物理安全工具最有效的武器. 安全设施开始以反人类的方向进化, 变得复杂和晦涩, 普通用户开始无法理解自己使用的数字工具, 新生代不再关心互联网本身. 用户和机器人一同被困在层层加密和验证的迷宫中, 小型组织维护网络安全的开销开始反超网络建设本身的开销, 云原生概念诞生, 网络安全供应商盛行, 供应链攻击愈演愈烈.</p>\n<p><ruby>物联网<rt>IoT</rt></ruby>设备的数量激增, 借助 5G 网络开始超越传统的人机设备数量, 智能家居, 穿戴设备和工业传感器无处不在. IPv4 彻底告罄, IPv6 推广加速, 但普及速度仍然无法满足需求. 网络安全进入了「塔防模式」, 互联网企业以堡垒为核心构建重重防线, 默认需要主动抵御威胁, 蜜罐陷阱遍布堡垒之外. 爬虫流量逐渐接近甚至有趋势超过真实用户流量, 互联网产品在基础协议上构建复杂的应用层协议将用户隔离在网络之外, 普通人再也难以直接接触它们的 &quot;底层&quot;.</p>\n<p>人工智能技术的应用迎来爆发式增长. 算法构建的信息茧房将每个人原子化, 用户被困在定制化的信息泡泡中, 认知被无形操控. 数字身份不再是一个类人的整体的概念, 而是被拆解为无数标签的集合, 互联网公司和政府支持的组织借助标签影响人类用户行为, 间接操控现实世界的政治与社会格局. 人工智能主导的深度伪造, 虚假信息和算法偏见成为常见威胁, 人类在数字世界中的自主性开始丧失, 互联网大陆开始板块漂移.</p>\n<p>开源运动从软件扩展到硬件领域, 但开源理想与世界的现实仍然存在明显冲突. 互联网的黑暗面愈发凸显, 互联网居民开始怀旧 Web 1.0 时代, 并开始尝试将自己曾经耕耘互联网的「犁头」铸造为「剑」. 这是一个青铜时代, 技术带来了力量, 但青铜时代的器物依然是王公贵族才能完全拥有的东西.</p>\n<h2 id=\"后-web-3-0-web-5-0\"><a href=\"#后-web-3-0-web-5-0\"></a>后 Web 3.0 ~ Web 5.0</h2>\n<blockquote>\n<p><strong>迷雾模式 &amp; 铁器时代: 量子计算?</strong></p>\n</blockquote>\n<p>互联网进入「迷雾模式」. 量子计算产生突破, 网络信任链条遭遇危机, 传统加密技术再次肩负修复网络信任崩塌风险的重任, 后量子加密算法成为新防线. 互联网的运行逻辑变得更加不可预测, 卫星网络和量子网络颠覆数据传输的方式, 信息流动的速度和形式超越人类的直观理解.</p>\n<p>互联网更加嵌入现实世界, 虚拟与现实的界限开始融合. <ruby>增强现实<rt>AR</rt></ruby>, <ruby>虚拟现实<rt>VR</rt></ruby>和脑机接口开始真正发展, 尝试让人类直接与数字世界融合, 让网络不再是工具而成为人类意识的延伸, 人类意识将摆脱人机交互直接和硬件连接. 而已经与互联网软件和硬件深度融合的人类变成了网络攻击的目标, 人类的思想开始被直接攻击或操控, 相对于人类与网络分离为前提的「隐私」概念即将被边缘化.</p>\n<p>网络安全进入全新的阶段, 原本清晰的网络视野变成带有战争迷雾的战场, 攻击者隐藏在用户, 软件和硬件中. 攻击者与防御者都在未知的领域中摸索. 人, 人工智能, 量子计算和 &quot;数字幽灵&quot; 的载体 —— 联网硬件, 让攻防博弈达到前所未有的复杂程度. 去中心化技术进一步发展, Web 1.0 时代的构想被颠覆, 个人数据脱离云端直接存储在私有的「数字保险箱」中, 数据和信息不再默认被共享和传播. 大型经济体将数字主权与国家主权并列, 网络防火墙从国家数字主权边境开始生长, 由于长期依赖, 人与人的信任也从「鸿沟」变成了一道巨墙, <ruby>通用人工智能<rt>AGI</rt></ruby>开始发展.</p>\n<p>铁器时代, 任何网络技术能轻易铸造精致完美的双刃剑, 人人都是执剑人和被剑指的人, 基础设施被再次重视, 但已经不再是全球化下的分工协作, 也不再是创造, 而是对 &quot;旧时代贵族&quot; 割据下的依附或挑战的选择.</p>\n<h2 id=\"web\"><a href=\"#web\"></a>Web ???</h2>\n<blockquote>\n<p><strong>避难所模式 &amp; 黑铁时代: 后量子与虚拟现实?</strong></p>\n</blockquote>\n<p>互联网进步的根基遭遇瓶颈, 人类直面量子隧穿对摩尔定律的挑战. 人类与网络的关系进入一种诡异的平衡状态. 量子计算和后量子技术改变互联网的底层架构, 网络不再是人类可以理解的系统, 而是如自然界一般自运行和自演化的「数字生态」, 互联网信息如同农作物一般被不可理解地自动播种与收获.</p>\n<p>人类不再是互联网的中心, 自主行动的人工智能和分布式网络系统成为主导力量. 人类为了保护自己退回到一个个「数字避难所」中, 这些避难所可能是基于区块链的完全去中心化的原子化社区, 也可能是由强大人工智能防火墙保护的「数字堡垒」. 网络攻击不再是单纯的技术问题, 而是演化为一种 &quot;生态竞争&quot;, 不同数字实体之间的博弈如同生物界的生存斗争.</p>\n<p>社会结构可能发生深刻变革, 人类的身份, 信任和价值体系被重塑. 互联网技术不再服务于人类, 而是和人一起成为一种独立的存在, 人类需要重新寻找自己在数字世界中的位置. 这是网络文明的黑铁时代, 但技术已经从冷兵器进化为热兵器, 每个人都持有, 每个机器也能持有, 人类需要在其中寻找最后的容身之处.</p>\n<h2 id=\"后记\"><a href=\"#后记\"></a>后记</h2>\n<p>Web 技术的代际更迭并不是互联网变成如今格局的罪魁祸首, 而是人类社会与互联网深度融合后, 技术被人的欲望, 恐惧和需求所驱动的结果, 它们是「果」而不是「因」. 每一代互联网技术的设计都反映了那个时代的生存逻辑与社会价值观, 它们是人类对未来的设想, 也是对现实困境的回应. 然而, 技术的发展从未摆脱人的影子 —— 最大的变数, 永远是人性本身. 无论是创造还是竞争, 又或者是信任的崩塌与重建, 互联网的每一次演化都深深烙印着人类社会的矛盾与挣扎.</p>\n","content_text":"前言 注意, 本文并非完全纪实, 越到后面会越接近科幻创作, 请读者知悉. 我又不是先知, 我只能写点暴论了. Web 1.0 创造模式 &amp; 黄金时代: DoS (1990 年代 ~ 2000 年代早期) 这是一个网络世界的拓荒年代, 但互联网是人造的, 是如同没有原住民和原生生物威胁的新大陆一般, 只有无限可能与未被触及的疆域. 人工智能刚刚进入低谷, 而互联网却充满着希望, 使得这两个事物看起来毫不相干. 资源在当时显得富足而慷慨, 但这些资源却被极少数人所拥有, 这些人被称为互联网的 &quot;先锋&quot;, 互联网的 &quot;缔造者&quot;, 他们是「极客」, 是大学的研究者, 是资金雄厚的先行者. 这一时期的互联网的门槛极高, 世界也还没有与互联网融合, 万维网World Wide Web的概念才刚刚诞生. 也因此形成了一个小而紧密的社区. 只要这里的人愿意, 他们就可以做到现实世界彼此认识, 甚至在网络上建立深厚的友谊. 他们在自己的虚拟领土上辛勤耕耘, 创作个人网站和论坛, 用朴素的 HTML 记录自己的东西, 然后邀请他人来观赏, 交流. 这个时代的网络技术简单而纯粹, 从未过多考虑过安全与加密. 计算机与计算机之间的连接就像人与人之间的握手, 一切建立在信任与坦诚之上. 此时的 HTTP 协议如同在广场上用传声筒交流一样简单直接; SMTP 像真实邮差递送信件一样毫无遮掩. 网络的架构师们默认进入这片数字世界的人都是怀着善意的探险者, 恶意攻击比如 DoS 虽然存在, 但往往只是这群技术爱好者之间的恶作剧并不认为是真正的威胁, 尽管如此这也让网络攻防的概念开始萌芽. IPv4 地址在当时仿佛取之不尽用之不竭的「自然资源」, 每个设备一开始接入互联网就可以拥有一个独一无二的身份标识. 互联网的第一批公司在硅谷中诞生, 它们在网络疆域上圈地跑马, 建设自己的 &quot;数字农场&quot; 与 &quot;虚拟工厂&quot;, 为早期网络用户提供浏览器, 电子邮件和门户网站等这一时代的基础服务. 与此同时, 互联网的行动纲领 —— RFC 被起草, 这些文档是这个网络世界的 &quot;宪法&quot;, 为未来的技术发展奠定了基石. 开源运动也在这一时期萌芽, Linux 等项目的出现让技术不再是少数人的专属. 这是一段黄金岁月, 互联网如同一片沃土, 充满了创造与分享的激情. 然而, 信任的基石也埋下了隐患, 当更多人进入这片土地时, 原始的单纯将不再适用, 总会有一批人变成最早期的原住民. 这一时代由风险投资主导了互联网经济的蛮荒, 最终表现在了末期爆发的互联网经济泡沫, 但还是让很多后世的巨头在泡沫之中成长起来, 这一经济模式还是成为了后世互联网经济的核心. Web 2.0 生存模式 &amp; 白银时代: DDoS (2000 年代 ~ 2010 年代) 二十一世纪到来, 互联网从精英的游乐场转变为全民的竞技场. 全球化运动达到巅峰, 使得互联网公司们有机会开始在全世界范围内攻城略地, 运用一切手段割据一方, 资源逐渐集中化. 大众互联网形成, 普通人也可以通过越来越便宜的宽带连接到网络, 市场接近饱和, 竞争变得异常激烈. 新生代互联网公司崛起, 它们不再满足于单纯提供服务, 而是通过广告, 数据和用户黏性构建自己的商业帝国, 形成了坚不可摧的 &quot;数字城墙&quot;, 互联网大陆板块开始裂解. 巨头主导的互联网普及让内容生产模式发生革命性变化, 由 PGC 逐渐被 UGC 取代, 社交媒体和视频平台让每个人都成为信息的创造者. 信息孤岛开始出现, 用户开始被算法推荐缠身. 广告和金融市场收益开始超越用户生态的直接收益, 对流量和注意力的追求超越对艺术和文化的追求, 互联网公司变得更关心如何从用户身上榨取价值, 而非为用户创造价值. 互联网成为一个真正以机器运行速度驱使人类进步的行业, 传统行业不得不开始寻求融合, 被公认为「第三次工业革命」. 随着互联网的真正普及, IPv4 资源逐渐枯竭, NAT 成为资源匮乏地区的常态, IPv6 终于开始痛苦且缓慢地推广. 网络攻击从单机的 DoS 演变为分布式的 DDoS, 网络安全开始成为互联网世界的核心议题. 为满足市场, 网络攻击和网络安全开始成为一种服务而被制造, 供应和消费. 有人开始反思互联网过于集中化的弊端, 去中心化的概念开始兴起, 比特币诞生. 开源运动如火如荼, 互联网成为政治运动的沃土, 全球协作与共享成为新的信仰. 但互联网的主导权依然掌握在少数科技巨头手中, 用户的数据和隐私仍然被肆意收集交易, 互联网中的人与人的信任开始崩塌. 白银时代, 是大部分普通人才能接触到的时代. 有部分人奉黄金时代的先驱为神而去膜拜, 这也确实坚定了无数后人对互联网的开发的信念. 但是人人都能「淘金」的时代已经过去, 互联网第一次感受到了激烈的生存和竞争压力, 资源变成稀缺品, 为有限资源而开始的过度竞争开始显现. 卖淘金使用的「铲子」变得比淘金本身更加容易获利, AWS 为代表的集中式云计算出现, 它们为后世互联网的淘金者们提供了数量最多的铲子. 此时, 人工智能领域走出 &quot;低谷&quot;, 理论突破出现, 此时的开始富集的算力资源和数据也为深度学习提供了最早的养料. Web 3.0 塔防模式 &amp; 青铜时代: 人工智能 (2010 年代 ~ 至今) 人与人, 人与机器, 机器与网络之间默认不再信任, 信任链条只能通过密码学技术来构建, Web 1.0 时代的网络基础变成网络安全障碍. 基于密码学的零信任模型和区块链技术诞生试图重新定义互联网的人机关系. 区块链甚至开始尝试对金融系统, 公司治理乃至社会结构的进行颠覆性改变, 然后发明了去中心化金融DeFi, 非同质化代币NFT等概念. 逆全球化运动开始, 互联网「无中介」概念诞生, 数字主权运动萌芽. 互联网的最大漏洞变成了人本身, 社会工程学攻击泛滥, 借助钓鱼邮件, 虚假身份和心理操控成为这一时代的网络攻击者绕过数字甚至物理安全工具最有效的武器. 安全设施开始以反人类的方向进化, 变得复杂和晦涩, 普通用户开始无法理解自己使用的数字工具, 新生代不再关心互联网本身. 用户和机器人一同被困在层层加密和验证的迷宫中, 小型组织维护网络安全的开销开始反超网络建设本身的开销, 云原生概念诞生, 网络安全供应商盛行, 供应链攻击愈演愈烈. 物联网IoT设备的数量激增, 借助 5G 网络开始超越传统的人机设备数量, 智能家居, 穿戴设备和工业传感器无处不在. IPv4 彻底告罄, IPv6 推广加速, 但普及速度仍然无法满足需求. 网络安全进入了「塔防模式」, 互联网企业以堡垒为核心构建重重防线, 默认需要主动抵御威胁, 蜜罐陷阱遍布堡垒之外. 爬虫流量逐渐接近甚至有趋势超过真实用户流量, 互联网产品在基础协议上构建复杂的应用层协议将用户隔离在网络之外, 普通人再也难以直接接触它们的 &quot;底层&quot;. 人工智能技术的应用迎来爆发式增长. 算法构建的信息茧房将每个人原子化, 用户被困在定制化的信息泡泡中, 认知被无形操控. 数字身份不再是一个类人的整体的概念, 而是被拆解为无数标签的集合, 互联网公司和政府支持的组织借助标签影响人类用户行为, 间接操控现实世界的政治与社会格局. 人工智能主导的深度伪造, 虚假信息和算法偏见成为常见威胁, 人类在数字世界中的自主性开始丧失, 互联网大陆开始板块漂移. 开源运动从软件扩展到硬件领域, 但开源理想与世界的现实仍然存在明显冲突. 互联网的黑暗面愈发凸显, 互联网居民开始怀旧 Web 1.0 时代, 并开始尝试将自己曾经耕耘互联网的「犁头」铸造为「剑」. 这是一个青铜时代, 技术带来了力量, 但青铜时代的器物依然是王公贵族才能完全拥有的东西. 后 Web 3.0 ~ Web 5.0 迷雾模式 &amp; 铁器时代: 量子计算? 互联网进入「迷雾模式」. 量子计算产生突破, 网络信任链条遭遇危机, 传统加密技术再次肩负修复网络信任崩塌风险的重任, 后量子加密算法成为新防线. 互联网的运行逻辑变得更加不可预测, 卫星网络和量子网络颠覆数据传输的方式, 信息流动的速度和形式超越人类的直观理解. 互联网更加嵌入现实世界, 虚拟与现实的界限开始融合. 增强现实AR, 虚拟现实VR和脑机接口开始真正发展, 尝试让人类直接与数字世界融合, 让网络不再是工具而成为人类意识的延伸, 人类意识将摆脱人机交互直接和硬件连接. 而已经与互联网软件和硬件深度融合的人类变成了网络攻击的目标, 人类的思想开始被直接攻击或操控, 相对于人类与网络分离为前提的「隐私」概念即将被边缘化. 网络安全进入全新的阶段, 原本清晰的网络视野变成带有战争迷雾的战场, 攻击者隐藏在用户, 软件和硬件中. 攻击者与防御者都在未知的领域中摸索. 人, 人工智能, 量子计算和 &quot;数字幽灵&quot; 的载体 —— 联网硬件, 让攻防博弈达到前所未有的复杂程度. 去中心化技术进一步发展, Web 1.0 时代的构想被颠覆, 个人数据脱离云端直接存储在私有的「数字保险箱」中, 数据和信息不再默认被共享和传播. 大型经济体将数字主权与国家主权并列, 网络防火墙从国家数字主权边境开始生长, 由于长期依赖, 人与人的信任也从「鸿沟」变成了一道巨墙, 通用人工智能AGI开始发展. 铁器时代, 任何网络技术能轻易铸造精致完美的双刃剑, 人人都是执剑人和被剑指的人, 基础设施被再次重视, 但已经不再是全球化下的分工协作, 也不再是创造, 而是对 &quot;旧时代贵族&quot; 割据下的依附或挑战的选择. Web ??? 避难所模式 &amp; 黑铁时代: 后量子与虚拟现实? 互联网进步的根基遭遇瓶颈, 人类直面量子隧穿对摩尔定律的挑战. 人类与网络的关系进入一种诡异的平衡状态. 量子计算和后量子技术改变互联网的底层架构, 网络不再是人类可以理解的系统, 而是如自然界一般自运行和自演化的「数字生态」, 互联网信息如同农作物一般被不可理解地自动播种与收获. 人类不再是互联网的中心, 自主行动的人工智能和分布式网络系统成为主导力量. 人类为了保护自己退回到一个个「数字避难所」中, 这些避难所可能是基于区块链的完全去中心化的原子化社区, 也可能是由强大人工智能防火墙保护的「数字堡垒」. 网络攻击不再是单纯的技术问题, 而是演化为一种 &quot;生态竞争&quot;, 不同数字实体之间的博弈如同生物界的生存斗争. 社会结构可能发生深刻变革, 人类的身份, 信任和价值体系被重塑. 互联网技术不再服务于人类, 而是和人一起成为一种独立的存在, 人类需要重新寻找自己在数字世界中的位置. 这是网络文明的黑铁时代, 但技术已经从冷兵器进化为热兵器, 每个人都持有, 每个机器也能持有, 人类需要在其中寻找最后的容身之处. 后记 Web 技术的代际更迭并不是互联网变成如今格局的罪魁祸首, 而是人类社会与互联网深度融合后, 技术被人的欲望, 恐惧和需求所驱动的结果, 它们是「果」而不是「因」. 每一代互联网技术的设计都反映了那个时代的生存逻辑与社会价值观, 它们是人类对未来的设想, 也是对现实困境的回应. 然而, 技术的发展从未摆脱人的影子 —— 最大的变数, 永远是人性本身. 无论是创造还是竞争, 又或者是信任的崩塌与重建, 互联网的每一次演化都深深烙印着人类社会的矛盾与挣扎.","summary":"前言 注意, 本文并非完全纪实, 越到后面会越接近科幻创作, 请读者知悉. 我又不是先知, 我只能写点暴论了. Web 1.0 创造模式 &amp; 黄金时代: DoS (1990 年代 ~ 2000 年代早期) 这是一个网络世界的拓荒年代, 但互联网是人造的, 是如同没有原住民和原生生物威胁的新大陆一般, 只有无限可能与未被触及的疆域. 人工智能刚刚进入低谷, 而互联网却充满着希望, 使得这两个事物看起来毫不相干. 资源在当时显得富足而慷慨, 但这些资源却被极少数人所拥有, 这些人被称为互联网的 &quot;先锋&quot;, 互联网的 &quot;缔造者&quot;, 他们是「极客」, 是大学的研究者, 是资金雄厚的先行者. 这一时期的互联网的门槛极高, 世界也还没有与互联网融合, 万维网World Wide Web的概念才刚刚诞生. 也因此形成了一个小而紧密的社区. 只要这里的人愿意, 他们就可以做到现实世界彼此认识, 甚至在网络上建立深厚的友谊. 他们在自己的虚拟领土上辛勤耕耘, 创作个人网站和论坛, 用朴素的 HTML 记录自己的东西, 然后邀请他人来观赏, 交流. 这个时代的网络技术简单而纯粹, 从未过多考虑过安全与加密. 计算机与计算机之间的连接就像人与人之间的握手, 一切建立在信任与坦诚之上. 此时的 HTTP 协议如同在广场上用传声筒交流一样简单直接; SMTP 像真实邮差递送信件一样毫无遮掩. 网络的架构师们默认进入这片数字世界的人都是怀着善意的探险者, 恶意攻击比如 DoS 虽然存在, 但往往只是这群技术爱好者之间的恶作剧并不认为是真正的威胁, 尽管如此这也让网络攻防的概念开始萌芽. IPv4 地址在当时仿佛取之不尽用之不竭的「自然资源」, 每个设备一开始接入互联网就可以拥有一个独一无二的身份标识. 互联网的第一批公司在硅谷中诞生, 它们在网络疆域上圈地跑马, 建设自己的 &quot;数字农场&quot; 与 &quot;虚拟工厂&quot;, 为早期网络用户提供浏览器, 电子邮件和门户网站等这一时代的基础服务. 与此同时, 互联网的行动纲领 —— RFC 被起草, 这些文档是这个网络世界的 &quot;宪法&quot;, 为未来的技术发展奠定了基石. 开源运动也在这一时期萌芽, Linux 等项目的出现让技术不再是少数人的专属. 这是一段黄金岁月, 互联网如同一片沃土, 充满了创造与分享的激情. 然而, 信任的基石也埋下了隐患, 当更多人进入这片土地时, 原始的单纯将不再适用, 总会有一批人变成最早期的原住民. 这一时代由风险投资主导了互联网经济的蛮荒, 最终表现在了末期爆发的互联网经济泡沫, 但还是让很多后世的巨头在泡沫之中成长起来, 这一经济模式还是成为了后世互联网经济的核心. Web 2.0 生存模式 &amp; 白银时代: DDoS (2000 年代 ~ 2010 年代) 二十一世纪到来, 互联网从精英的游乐场转变为全民的竞技场. 全球化运动达到巅峰, 使得互联网公司们有机会开始在全世界范围内攻城略地, 运用一切手段割据一方, 资源逐渐集中化. 大众互联网形成, 普通人也可以通过越来越便宜的宽带连接到网络, 市场接近饱和, 竞争变得异常激烈. 新生代互联网公司崛起, 它们不再满足于单纯提供服务, 而是通过广告, 数据和用户黏性构建自己的商业帝国, 形成了坚不可摧的 &quot;数字城墙&quot;, 互联网大陆板块开始裂解. 巨头主导的互联网普及让内容生产模式发生革命性变化, 由 PGC 逐渐被 UGC 取代, 社交媒体和视频平台让每个人都成为信息的创造者. 信息孤岛开始出现, 用户开始被算法推荐缠身. 广告和金融市场收益开始超越用户生态的直接收益, 对流量和注意力的追求超越对艺术和文化的追求, 互联网公司变得更关心如何从用户身上榨取价值, 而非为用户创造价值. 互联网成为一个真正以机器运行速度驱使人类进步的行业, 传统行业不得不开始寻求融合, 被公认为「第三次工业革命」. 随着互联网的真正普及, IPv4 资源逐渐枯竭, NAT 成为资源匮乏地区的常态, IPv6 终于开始痛苦且缓慢地推广. 网络攻击从单机的 DoS 演变为分布式的 DDoS, 网络安全开始成为互联网世界的核心议题. 为满足市场, 网络攻击和网络安全开始成为一种服务而被制造, 供应和消费. 有人开始反思互联网过于集中化的弊端, 去中心化的概念开始兴起, 比特币诞生. 开源运动如火如荼, 互联网成为政治运动的沃土, 全球协作与共享成为新的信仰. 但互联网的主导权依然掌握在少数科技巨头手中, 用户的数据和隐私仍然被肆意收集交易, 互联网中的人与人的信任开始崩塌. 白银时代, 是大部分普通人才能接触到的时代. 有部分人奉黄金时代的先驱为神而去膜拜, 这也确实坚定了无数后人对互联网的开发的信念. 但是人人都能「淘金」的时代已经过去, 互联网第一次感受到了激烈的生存和竞争压力, 资源变成稀缺品, 为有限资源而开始的过度竞争开始显现. 卖淘金使用的「铲子」变得比淘金本身更加容易获利, AWS 为代表的集中式云计算出现, 它们为后世互联网的淘金者们提供了数量最多的铲子. 此时, 人工智能领域走出 &quot;低谷&quot;, 理论突破出现, 此时的开始富集的算力资源和数据也为深度学习提供了最早的养料. Web 3.0 塔防模式 &amp; 青铜时代: 人工智能 (2010 年代 ~ 至今) 人与人, 人与机器, 机器与网络之间默认不再信任, 信任链条只能通过密码学技术来构建, Web 1.0 时代的网络基础变成网络安全障碍. 基于密码学的零信任模型和区块链技术诞生试图重新定义互联网的人机关系. 区块链甚至开始尝试对金融系统, 公司治理乃至社会结构的进行颠覆性改变, 然后发明了去中心化金融DeFi, 非同质化代币NFT等概念. 逆全球化运动开始, 互联网「无中介」概念诞生, 数字主权运动萌芽. 互联网的最大漏洞变成了人本身, 社会工程学攻击泛滥, 借助钓鱼邮件, 虚假身份和心理操控成为这一时代的网络攻击者绕过数字甚至物理安全工具最有效的武器. 安全设施开始以反人类的方向进化, 变得复杂和晦涩, 普通用户开始无法理解自己使用的数字工具, 新生代不再关心互联网本身. 用户和机器人一同被困在层层加密和验证的迷宫中, 小型组织维护网络安全的开销开始反超网络建设本身的开销, 云原生概念诞生, 网络安全供应商盛行, 供应链攻击愈演愈烈. 物联网IoT设备的数量激增, 借助 5G 网络开始超越传统的人机设备数量, 智能家居, 穿戴设备和工业传感器无处不在. IPv4 彻底告罄, IPv6 推广加速, 但普及速度仍然无法满足需求. 网络安全进入了「塔防模式」, 互联网企业以堡垒为核心构建重重防线, 默认需要主动抵御威胁, 蜜罐陷阱遍布堡垒之外. 爬虫流量逐渐接近甚至有趋势超过真实用户流量, 互联网产品在基础协议上构建复杂的应用层协议将用户隔离在网络之外, 普通人再也难以直接接触它们的 &quot;底层&quot;. 人工智能技术的应用迎来爆发式增长. 算法构建的信息茧房将每个人原子化, 用户被困在定制化的信息泡泡中, 认知被无形操控. 数字身份不再是一个类人的整体的概念, 而是被拆解为无数标签的集合, 互联网公司和政府支持的组织借助标签影响人类用户行为, 间接操控现实世界的政治与社会格局. 人工智能主导的深度伪造, 虚假信息和算法偏见成为常见威胁, 人类在数字世界中的自主性开始丧失, 互联网大陆开始板块漂移. 开源运动从软件扩展到硬件领域, 但开源理想与世界的现实仍然存在明显冲突. 互联网的黑暗面愈发凸显, 互联网居民开始怀旧 Web 1.0 时代, 并开始尝试将自己曾经耕耘互联网的「犁头」铸造为「剑」. 这是一个青铜时代, 技术带来了力量, 但青铜时代的器物依然是王公贵族才能完全拥有的东西. 后 Web 3.0 ~ Web 5.0 迷雾模式 &amp; 铁器时代: 量子计算? 互联网进入「迷雾模式」. 量子计算产生突破, 网络信任链条遭遇危机, 传统加密技术再次肩负修复网络信任崩塌风险的重任, 后量子加密算法成为新防线. 互联网的运行逻辑变得更加不可预测, 卫星网络和量子网络颠覆数据传输的方式, 信息流动的速度和形式超越人类的直观理解. 互联网更加嵌入现实世界, 虚拟与现实的界限开始融合. 增强现实AR, 虚拟现实VR和脑机接口开始真正发展, 尝试让人类直接与数字世界融合, 让网络不再是工具而成为人类意识的延伸, 人类意识将摆脱人机交互直接和硬件连接. 而已经与互联网软件和硬件深度融合的人类变成了网络攻击的目标, 人类的思想开始被直接攻击或操控, 相对于人类与网络分离为前提的「隐私」概念即将被边缘化. 网络安全进入全新的阶段, 原本清晰的网络视野变成带有战争迷雾的战场, 攻击者隐藏在用户, 软件和硬件中. 攻击者与防御者都在未知的领域中摸索. 人, 人工智能, 量子计算和 &quot;数字幽灵&quot; 的载体 —— 联网硬件, 让攻防博弈达到前所未有的复杂程度. 去中心化技术进一步发展, Web 1.0 时代的构想被颠覆, 个人数据脱离云端直接存储在私有的「数字保险箱」中, 数据和信息不再默认被共享和传播. 大型经济体将数字主权与国家主权并列, 网络防火墙从国家数字主权边境开始生长, 由于长期依赖, 人与人的信任也从「鸿沟」变成了一道巨墙, 通用人工智能AGI开始发展. 铁器时代, 任何网络技术能轻易铸造精致完美的双刃剑, 人人都是执剑人和被剑指的人, 基础设施被再次重视, 但已经不再是全球化下的分工协作, 也不再是创造, 而是对 &quot;旧时代贵族&quot; 割据下的依附或挑战的选择. Web ??? 避难所模式 &amp; 黑铁时代: 后量子与虚拟现实? 互联网进步的根基遭遇瓶颈, 人类直面量子隧穿对摩尔定律的挑战. 人类与网络的关系进入一种诡异的平衡状态. 量子计算和后量子技术改变互联网的底层架构, 网络不再是人类可以理解的系统, 而是如自然界一般自运行和自演化的「数字生态」, 互联网信息如同农作物一般被不可理解地自动播种与收获. 人类不再是互联网的中心, 自主行动的人工智能和分布式网络系统成为主导力量. 人类为了保护自己退回到一个个「数字避难所」中, 这些避难所可能是基于区块链的完全去中心化的原子化社区, 也可能是由强大人工智能防火墙保护的「数字堡垒」. 网络攻击不再是单纯的技术问题, 而是演化为一种 &quot;生态竞争&quot;, 不同数字实体之间的博弈如同生物界的生存斗争. 社会结构可能发生深刻变革, 人类的身份, 信任和价值体系被重塑. 互联网技术不再服务于人类, 而是和人一起成为一种独立的存在, 人类需要重新寻找自己在数字世界中的位置. 这是网络文明的黑铁时代, 但技术已经从冷兵器进化为热兵器, 每个人都持有, 每个机器也能持有, 人类需要在其中寻找最后的容身之处. 后记 Web 技术的代际更迭并不是互联网变成如今格局的罪魁祸首, 而是人类社会与互联网深度融合后, 技术被人的欲望, 恐惧和需求所驱动的结果, 它们是「果」而不是「因」. 每一代互联网技术的设计都反映了那个时代的生存逻辑与社会价值观, 它们是人类对未来的设想, 也是对现实困境的回应. 然而, 技术的发展从未摆脱人的影子 —— 最大的变数, 永远是人性本身. 无论是创造还是竞争, 又或者是信任的崩塌与重建, 互联网的每一次演化都深深烙印着人类社会的矛盾与挣扎.","date_published":"2025-04-23T07:31:00.000Z","tags":["绝界行","互联网"]},{"id":"https://blog.cxplay.org/works/utm-parameters-explained/","url":"https://blog.cxplay.org/works/utm-parameters-explained/","title":"UTM 参数实践与广告计费类型解析","content_html":"<h2 id=\"utm-参数\"><a href=\"#utm-参数\"></a>UTM 参数</h2>\n<p>UTM 全称为 Urchin Tracking Module, 是被 Google 收购的 Urchin 公司的同名产品发明的跟踪参数标记, 这个产品也就是 Google Analytics 的前身.</p>\n<p>下面表格中的 utm_marketing_tactic, utm_creative_format, utm_source_platform 都是 Google Analytics 4<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup> 新引入的 UTM 参数, 用于更加细致地分析流量来源.</p>\n<table>\n<thead>\n<tr>\n<th>参数</th>\n<th>属性</th>\n<th style=\"text-align:center\">必要</th>\n<th>示例</th>\n<th>解释</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>utm_campaign</td>\n<td>营销活动</td>\n<td style=\"text-align:center\">/</td>\n<td>summer_sale</td>\n<td>用于识别获得的流量所归属的营销活动, 比如季度促销或者针对某特定地区的特定节日策划的营销活动</td>\n</tr>\n<tr>\n<td>utm_id</td>\n<td>活动名称</td>\n<td style=\"text-align:center\">/</td>\n<td>twitter_no23</td>\n<td>用于识别该 URL 所归属的营销活动中的具体活动, 可以将 URL 访客归属于来自特定的广告活动, 比如为了本次营销活动而针对某具体的社群投放的推广.</td>\n</tr>\n<tr>\n<td>utm_content</td>\n<td>活动内容</td>\n<td style=\"text-align:center\">/</td>\n<td>article_body</td>\n<td>用于识别相同营销链接的不同的点击或展示来源, 比如具体的某文章内容, 某展示位置, 某特定的图片, A/B 测试中的某项活动.</td>\n</tr>\n<tr>\n<td>utm_source</td>\n<td>流量来源</td>\n<td style=\"text-align:center\">⭕</td>\n<td>twitter</td>\n<td>用于识别来源, 用于分析特定的投放渠道的点击量.</td>\n</tr>\n<tr>\n<td>utm_medium</td>\n<td>传播介质</td>\n<td style=\"text-align:center\">⭕</td>\n<td>email</td>\n<td>用于识别访问来源的媒体介质类型, 比如电子邮件, 社交媒体或独立站点或营销投放的类型如 CPC, CPM.</td>\n</tr>\n<tr>\n<td>utm_term</td>\n<td>关键词</td>\n<td style=\"text-align:center\">/</td>\n<td>ghibli+aigc+image</td>\n<td>用于识别流量来源的关键词, 常见于搜索广告, 比如搜索结果和搜索预选.</td>\n</tr>\n<tr>\n<td>utm_marketing_tactic</td>\n<td>营销策略</td>\n<td style=\"text-align:center\">/</td>\n<td>cash_back</td>\n<td>用于识别广告的营销策略, 常用于标定一个具体活动的定位, 比如潜在客户强化.</td>\n</tr>\n<tr>\n<td>utm_creative_format</td>\n<td>内容形式</td>\n<td style=\"text-align:center\">/</td>\n<td>video</td>\n<td>用于标识流量所属的广告内容的表现形式, 用于区分单次投放但创作者使用的不同内容类型中的推广植入, 比如视频内容和图文内容.</td>\n</tr>\n<tr>\n<td>utm_source_platform</td>\n<td>广告平台</td>\n<td style=\"text-align:center\">/</td>\n<td>search_ads_360</td>\n<td>用于标识流量来源是归属于哪个广告或营销平台的投放, 用于分析营销平台投放获得的流量.</td>\n</tr>\n</tbody>\n</table>\n<p>一个 URL 示例:</p>\n<pre class=\"language-http\"><code>https://blog.cxplay.org/works/utm-parameters-explained/?utm_campaign=summer_sale&amp;utm_id=twitter_no23&amp;utm_content=article_body&amp;utm_source=twitter&amp;utm_medium=email&amp;utm_term=ghibli+aigc+image&amp;utm_marketing_tactic=cash_back&amp;utm_creative_format=video&amp;utm_source_platform=search_ads_360\n</code></pre>\n<h2 id=\"非标准参数\"><a href=\"#非标准参数\"></a>非标准参数</h2>\n<p>除了 UTM, 还有一些被广泛使用的参数也用于标记和分析流量, 但它们都是约定俗成的, 并非是哪个流量分析产品制造的标准. 这些参数简单易用, 所以才变成了约定俗成的共识.</p>\n<table>\n<thead>\n<tr>\n<th>参数</th>\n<th>属性</th>\n<th>示例</th>\n<th>解释</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>ref</td>\n<td>引用来源</td>\n<td>producthunt</td>\n<td>用于识别是哪个站点跳出带来的引用流量. 由于某些场景下平台不会给外链提供 referrer 标头, 甚至不允许外链, 所以直接使用 ref 参数传递引荐来源是更好的办法. 比如 Product Hunt 会默认传递这个参数, 或者去分析来自 RSS/Atom 订阅的受众的点击.</td>\n</tr>\n<tr>\n<td>referrer</td>\n<td>引用来源</td>\n<td>producthunt</td>\n<td>同上, 只不过是完整的写法.</td>\n</tr>\n</tbody>\n</table>\n<p>一个 URL 示例 (自己引用自己其实是没必要的):</p>\n<pre class=\"language-http\"><code>https://blog.cxplay.org/works/utm-parameters-explained/?ref=cxplay.org\n</code></pre>\n<h2 id=\"参数实践\"><a href=\"#参数实践\"></a>参数实践</h2>\n<p>UTM 参数和非标准参数一样, 一般都需要页内的 JavaScript 脚本去捕获这些参数然后通过 HTTP 表单提交给流量分析平台, 如果无法捕获和分析参数那添加这些参数将没有意义. 虽然可以使用 HTTP 服务器和 WAF 直接对 URL 访问日志进行解析做到这一点, 但这并不能很好地区分流量的类型, 因为 Bot 也会访问链接, 重复访问也会被计算.</p>\n<p>几乎所有的访客分析软件都带有这些参数的解析能力, 比如开源的 Umami:</p>\n<blockquote>\n<p><a href=\"https://umami.is/blog/understanding-utm-link-performance\">Understanding UTM Link Performance with the UTM Report – Blog - Umami</a></p>\n</blockquote>\n<blockquote>\n<p><a href=\"https://umami.is/docs/reports/report-utm\">UTM report – Docs - Umami</a></p>\n</blockquote>\n<p>但非标准参数即使是约定俗成的, 并没有 UTM 一般形成业界中都认可的标准, 所以非标准参数的使用最好仔细测试具体的流量分析平台是否支持.</p>\n<hr>\n<p>为了方便地构建参数大多广告平台都提供了构建工具, 甚至保存参数备忘的功能.</p>\n<blockquote>\n<p><a href=\"https://ga-dev-tools.google/campaign-url-builder/\">Campaign URL Builder </a>(Google)</p>\n</blockquote>\n<p>但笔者在这里推荐兼任营销能力的开发者和技术人员使用图形界面的 HTTP 调试客户端, 比如 <a href=\"https://www.postman.com/?ref=blog.cxplay.org&amp;utm_source=cxplay_blog&amp;utm_medium=article\">Postman</a> 和 <a href=\"https://reqable.com/zh-CN/?ref=blog.cxplay.org&amp;utm_source=cxplay_blog&amp;utm_medium=article\">Reqable</a> 去构建和保存, 这是完全合理的用法. 将本文提供的那些 URL 示例粘贴到调试地址栏就能从参数列表很方便地构建参数, 大多数这类调试工具也带有保存调试的功能.</p>\n<hr>\n<p>同样的, 即使是参数的分析也是基于用户信息收集的, 很多在意隐私的访客并不愿意提供这些流量来源信息, 当今主流的广告拦截器为代表的隐私保护工具都会主动去剥离这些参数, 这会让基于参数的流量分析从 URL 层面直接失效. 对于这种情况, 除非能够在「重隐私型访客」点击链接之前就迫使他们关闭广告拦截器, 或者使用私有的参数类型逃逸这些基于广告拦截器的参数剥离规则, 同时也需要在流量分析软件处定制化解析这些定制参数.</p>\n<p>当今更为彻底或者对于重隐私型访客来说是极端的做法, 是将页面唯一 ID 或路径直接和分析参数编码甚至加密到短链背后的中间页参数或路径, 甚至可以为每个不同归属的链接建立一对一的中间页或者短链接,让这类访客无法去除, 或者选择去掉或无效化这些参数时就会直接无法访问最终网页. 比如页面路径, 营销活动参数必须与 aff 参数对应且缺一不可, 典型的例子是电商的联盟营销, 和一些流量优先的社交媒体平台.</p>\n<p>总的来说, 为了更精细且专业的分析效果就会越来和 UTM 脱离, 为了追求多平台多元化投放分析的通用性会越来越靠近 UTM. 实质上属于 Google 的 UTM 便是基于 URL 分析的现代网络营销的那个中间标准, 如果是本身既是流量主平台也是广告主平台的话, 就几乎一定会倾向于与 UTM 脱离, 特别是移动应用主导的移动互联网广告, 其投放到转化的分析已经变成了软件 SDK 对受众设备信息的直接收集而不是依赖于浏览器环境下的 URL. 有的应用平台还会自己建立自己的用户活动分析框架, 让受众的分析精确到平台内活动的每一次动作, 以提升自身平台的数据分析能力加强对广告主的吸引力, 同时还能给平台内的流量主提供精确的数据分析报告.</p>\n<h2 id=\"广告付费类型\"><a href=\"#广告付费类型\"></a>广告付费类型</h2>\n<ol>\n<li><strong>CPC  - 按点击计费</strong>: Cost Per Click; 广告主按照受众点击广告的次数支付费用. 常见于搜索引擎和社交媒体广告.</li>\n<li><strong>CPM - 按千次展示计费</strong>: Cost Per Mille; 广告主按照广告展示的(浏览)次数每 1000 次为单位支付费用, 这类广告通常注重曝光度, 常见于期望提升知名度的广告主投放. 常见于集中式的广告展示位.</li>\n<li><strong>CPA - 按行动计费</strong>: Cost Per Action; 广告主按照受众完成特定营销活动中的特定行动次数支付费用, 这类广告注重受众转化率, 需要受众完成特定的诸如注册, 购买, 下载和分享等动作才能计算一次有效广告活动. 常见于<ruby>联盟营销<rt>Affiliate</rt></ruby>式推广和各种追求社交媒体裂变的营销活动.</li>\n<li><strong>CPS - 按销售计费</strong>: Cost Per Sale; 广告主按照营销活动带来的实际销售量或付费量支付费用, 常见于联盟营销和电商广告.</li>\n<li><strong>CPL - 按潜在客户计费</strong>: Cost Per Lead; 按照获得的潜在客户数量计费, 常见于无法直接购买的非电商产品, 通常需要收集访客的信息(特别是联系方式), 广告主则按照获得的这类潜在客户数量支付费用.</li>\n<li><strong>CPP - 按播放计费</strong>: Cost Per Play; 按播放量计算费用. 常见于视频平台上投放的广告片, 广告主以自己的广告片的播放量支付费用.</li>\n<li><strong>CPD - 按天计费</strong>: Cost Per Da; 广告主按广告的展示时间支付费用, CPD 是按天计费. 这类广告常见于小型独立站点和特定合作中的广告.</li>\n</ol>\n<h2 id=\"注释\"><a href=\"#注释\"></a>注释</h2>\n<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p><a href=\"https://support.google.com/analytics/answer/10917952\">[GA4] URL builders: Collect campaign data with custom URLs - Analytics Help</a> <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n","content_text":"UTM 参数 UTM 全称为 Urchin Tracking Module, 是被 Google 收购的 Urchin 公司的同名产品发明的跟踪参数标记, 这个产品也就是 Google Analytics 的前身. 下面表格中的 utm_marketing_tactic, utm_creative_format, utm_source_platform 都是 Google Analytics 4[1] 新引入的 UTM 参数, 用于更加细致地分析流量来源. 参数 属性 必要 示例 解释 utm_campaign 营销活动 / summer_sale 用于识别获得的流量所归属的营销活动, 比如季度促销或者针对某特定地区的特定节日策划的营销活动 utm_id 活动名称 / twitter_no23 用于识别该 URL 所归属的营销活动中的具体活动, 可以将 URL 访客归属于来自特定的广告活动, 比如为了本次营销活动而针对某具体的社群投放的推广. utm_content 活动内容 / article_body 用于识别相同营销链接的不同的点击或展示来源, 比如具体的某文章内容, 某展示位置, 某特定的图片, A/B 测试中的某项活动. utm_source 流量来源 ⭕ twitter 用于识别来源, 用于分析特定的投放渠道的点击量. utm_medium 传播介质 ⭕ email 用于识别访问来源的媒体介质类型, 比如电子邮件, 社交媒体或独立站点或营销投放的类型如 CPC, CPM. utm_term 关键词 / ghibli+aigc+image 用于识别流量来源的关键词, 常见于搜索广告, 比如搜索结果和搜索预选. utm_marketing_tactic 营销策略 / cash_back 用于识别广告的营销策略, 常用于标定一个具体活动的定位, 比如潜在客户强化. utm_creative_format 内容形式 / video 用于标识流量所属的广告内容的表现形式, 用于区分单次投放但创作者使用的不同内容类型中的推广植入, 比如视频内容和图文内容. utm_source_platform 广告平台 / search_ads_360 用于标识流量来源是归属于哪个广告或营销平台的投放, 用于分析营销平台投放获得的流量. 一个 URL 示例: 1https://blog.cxplay.org/works/utm-parameters-explained/?utm_campaign=summer_sale&amp;utm_id=twitter_no23&amp;utm_content=article_body&amp;utm_source=twitter&amp;utm_medium=email&amp;utm_term=ghibli+aigc+image&amp;utm_marketing_tactic=cash_back&amp;utm_creative_format=video&amp;utm_source_platform=search_ads_360 非标准参数 除了 UTM, 还有一些被广泛使用的参数也用于标记和分析流量, 但它们都是约定俗成的, 并非是哪个流量分析产品制造的标准. 这些参数简单易用, 所以才变成了约定俗成的共识. 参数 属性 示例 解释 ref 引用来源 producthunt 用于识别是哪个站点跳出带来的引用流量. 由于某些场景下平台不会给外链提供 referrer 标头, 甚至不允许外链, 所以直接使用 ref 参数传递引荐来源是更好的办法. 比如 Product Hunt 会默认传递这个参数, 或者去分析来自 RSS/Atom 订阅的受众的点击. referrer 引用来源 producthunt 同上, 只不过是完整的写法. 一个 URL 示例 (自己引用自己其实是没必要的): 1https://blog.cxplay.org/works/utm-parameters-explained/?ref=cxplay.org 参数实践 UTM 参数和非标准参数一样, 一般都需要页内的 JavaScript 脚本去捕获这些参数然后通过 HTTP 表单提交给流量分析平台, 如果无法捕获和分析参数那添加这些参数将没有意义. 虽然可以使用 HTTP 服务器和 WAF 直接对 URL 访问日志进行解析做到这一点, 但这并不能很好地区分流量的类型, 因为 Bot 也会访问链接, 重复访问也会被计算. 几乎所有的访客分析软件都带有这些参数的解析能力, 比如开源的 Umami: Understanding UTM Link Performance with the UTM Report – Blog - Umami UTM report – Docs - Umami 但非标准参数即使是约定俗成的, 并没有 UTM 一般形成业界中都认可的标准, 所以非标准参数的使用最好仔细测试具体的流量分析平台是否支持. 为了方便地构建参数大多广告平台都提供了构建工具, 甚至保存参数备忘的功能. Campaign URL Builder (Google) 但笔者在这里推荐兼任营销能力的开发者和技术人员使用图形界面的 HTTP 调试客户端, 比如 Postman 和 Reqable 去构建和保存, 这是完全合理的用法. 将本文提供的那些 URL 示例粘贴到调试地址栏就能从参数列表很方便地构建参数, 大多数这类调试工具也带有保存调试的功能. 同样的, 即使是参数的分析也是基于用户信息收集的, 很多在意隐私的访客并不愿意提供这些流量来源信息, 当今主流的广告拦截器为代表的隐私保护工具都会主动去剥离这些参数, 这会让基于参数的流量分析从 URL 层面直接失效. 对于这种情况, 除非能够在「重隐私型访客」点击链接之前就迫使他们关闭广告拦截器, 或者使用私有的参数类型逃逸这些基于广告拦截器的参数剥离规则, 同时也需要在流量分析软件处定制化解析这些定制参数. 当今更为彻底或者对于重隐私型访客来说是极端的做法, 是将页面唯一 ID 或路径直接和分析参数编码甚至加密到短链背后的中间页参数或路径, 甚至可以为每个不同归属的链接建立一对一的中间页或者短链接,让这类访客无法去除, 或者选择去掉或无效化这些参数时就会直接无法访问最终网页. 比如页面路径, 营销活动参数必须与 aff 参数对应且缺一不可, 典型的例子是电商的联盟营销, 和一些流量优先的社交媒体平台. 总的来说, 为了更精细且专业的分析效果就会越来和 UTM 脱离, 为了追求多平台多元化投放分析的通用性会越来越靠近 UTM. 实质上属于 Google 的 UTM 便是基于 URL 分析的现代网络营销的那个中间标准, 如果是本身既是流量主平台也是广告主平台的话, 就几乎一定会倾向于与 UTM 脱离, 特别是移动应用主导的移动互联网广告, 其投放到转化的分析已经变成了软件 SDK 对受众设备信息的直接收集而不是依赖于浏览器环境下的 URL. 有的应用平台还会自己建立自己的用户活动分析框架, 让受众的分析精确到平台内活动的每一次动作, 以提升自身平台的数据分析能力加强对广告主的吸引力, 同时还能给平台内的流量主提供精确的数据分析报告. 广告付费类型 CPC - 按点击计费: Cost Per Click; 广告主按照受众点击广告的次数支付费用. 常见于搜索引擎和社交媒体广告. CPM - 按千次展示计费: Cost Per Mille; 广告主按照广告展示的(浏览)次数每 1000 次为单位支付费用, 这类广告通常注重曝光度, 常见于期望提升知名度的广告主投放. 常见于集中式的广告展示位. CPA - 按行动计费: Cost Per Action; 广告主按照受众完成特定营销活动中的特定行动次数支付费用, 这类广告注重受众转化率, 需要受众完成特定的诸如注册, 购买, 下载和分享等动作才能计算一次有效广告活动. 常见于联盟营销Affiliate式推广和各种追求社交媒体裂变的营销活动. CPS - 按销售计费: Cost Per Sale; 广告主按照营销活动带来的实际销售量或付费量支付费用, 常见于联盟营销和电商广告. CPL - 按潜在客户计费: Cost Per Lead; 按照获得的潜在客户数量计费, 常见于无法直接购买的非电商产品, 通常需要收集访客的信息(特别是联系方式), 广告主则按照获得的这类潜在客户数量支付费用. CPP - 按播放计费: Cost Per Play; 按播放量计算费用. 常见于视频平台上投放的广告片, 广告主以自己的广告片的播放量支付费用. CPD - 按天计费: Cost Per Da; 广告主按广告的展示时间支付费用, CPD 是按天计费. 这类广告常见于小型独立站点和特定合作中的广告. 注释 [GA4] URL builders: Collect campaign data with custom URLs - Analytics Help ↩︎","summary":"UTM 参数 UTM 全称为 Urchin Tracking Module, 是被 Google 收购的 Urchin 公司的同名产品发明的跟踪参数标记, 这个产品也就是 Google Analytics 的前身. 下面表格中的 utm_marketing_tactic, utm_creative_format, utm_source_platform 都是 Google Analytics 4[1] 新引入的 UTM 参数, 用于更加细致地分析流量来源. 参数 属性 必要 示例 解释 utm_campaign 营销活动 / summer_sale 用于识别获得的流量所归属的营销活动, 比如季度促销或者针对某特定地区的特定节日策划的营销活动 utm_id 活动名称 / twitter_no23 用于识别该 URL 所归属的营销活动中的具体活动, 可以将 URL 访客归属于来自特定的广告活动, 比如为了本次营销活动而针对某具体的社群投放的推广. utm_content 活动内容 / article_body 用于识别相同营销链接的不同的点击或展示来源, 比如具体的某文章内容, 某展示位置, 某特定的图片, A/B 测试中的某项活动. utm_source 流量来源 ⭕ twitter 用于识别来源, 用于分析特定的投放渠道的点击量. utm_medium 传播介质 ⭕ email 用于识别访问来源的媒体介质类型, 比如电子邮件, 社交媒体或独立站点或营销投放的类型如 CPC, CPM. utm_term 关键词 / ghibli+aigc+image 用于识别流量来源的关键词, 常见于搜索广告, 比如搜索结果和搜索预选. utm_marketing_tactic 营销策略 / cash_back 用于识别广告的营销策略, 常用于标定一个具体活动的定位, 比如潜在客户强化. utm_creative_format 内容形式 / video 用于标识流量所属的广告内容的表现形式, 用于区分单次投放但创作者使用的不同内容类型中的推广植入, 比如视频内容和图文内容. utm_source_platform 广告平台 / search_ads_360 用于标识流量来源是归属于哪个广告或营销平台的投放, 用于分析营销平台投放获得的流量. 一个 URL 示例: 1https://blog.cxplay.org/works/utm-parameters-explained/?utm_campaign=summer_sale&amp;utm_id=twitter_no23&amp;utm_content=article_body&amp;utm_source=twitter&amp;utm_medium=email&amp;utm_term=ghibli+aigc+image&amp;utm_marketing_tactic=cash_back&amp;utm_creative_format=video&amp;utm_source_platform=search_ads_360 非标准参数 除了 UTM, 还有一些被广泛使用的参数也用于标记和分析流量, 但它们都是约定俗成的, 并非是哪个流量分析产品制造的标准. 这些参数简单易用, 所以才变成了约定俗成的共识. 参数 属性 示例 解释 ref 引用来源 producthunt 用于识别是哪个站点跳出带来的引用流量. 由于某些场景下平台不会给外链提供 referrer 标头, 甚至不允许外链, 所以直接使用 ref 参数传递引荐来源是更好的办法. 比如 Product Hunt 会默认传递这个参数, 或者去分析来自 RSS/Atom 订阅的受众的点击. referrer 引用来源 producthunt 同上, 只不过是完整的写法. 一个 URL 示例 (自己引用自己其实是没必要的): 1https://blog.cxplay.org/works/utm-parameters-explained/?ref=cxplay.org 参数实践 UTM 参数和非标准参数一样, 一般都需要页内的 JavaScript 脚本去捕获这些参数然后通过 HTTP 表单提交给流量分析平台, 如果无法捕获和分析参数那添加这些参数将没有意义. 虽然可以使用 HTTP 服务器和 WAF 直接对 URL 访问日志进行解析做到这一点, 但这并不能很好地区分流量的类型, 因为 Bot 也会访问链接, 重复访问也会被计算. 几乎所有的访客分析软件都带有这些参数的解析能力, 比如开源的 Umami: Understanding UTM Link Performance with the UTM Report – Blog - Umami UTM report – Docs - Umami 但非标准参数即使是约定俗成的, 并没有 UTM 一般形成业界中都认可的标准, 所以非标准参数的使用最好仔细测试具体的流量分析平台是否支持. 为了方便地构建参数大多广告平台都提供了构建工具, 甚至保存参数备忘的功能. Campaign URL Builder (Google) 但笔者在这里推荐兼任营销能力的开发者和技术人员使用图形界面的 HTTP 调试客户端, 比如 Postman 和 Reqable 去构建和保存, 这是完全合理的用法. 将本文提供的那些 URL 示例粘贴到调试地址栏就能从参数列表很方便地构建参数, 大多数这类调试工具也带有保存调试的功能. 同样的, 即使是参数的分析也是基于用户信息收集的, 很多在意隐私的访客并不愿意提供这些流量来源信息, 当今主流的广告拦截器为代表的隐私保护工具都会主动去剥离这些参数, 这会让基于参数的流量分析从 URL 层面直接失效. 对于这种情况, 除非能够在「重隐私型访客」点击链接之前就迫使他们关闭广告拦截器, 或者使用私有的参数类型逃逸这些基于广告拦截器的参数剥离规则, 同时也需要在流量分析软件处定制化解析这些定制参数. 当今更为彻底或者对于重隐私型访客来说是极端的做法, 是将页面唯一 ID 或路径直接和分析参数编码甚至加密到短链背后的中间页参数或路径, 甚至可以为每个不同归属的链接建立一对一的中间页或者短链接,让这类访客无法去除, 或者选择去掉或无效化这些参数时就会直接无法访问最终网页. 比如页面路径, 营销活动参数必须与 aff 参数对应且缺一不可, 典型的例子是电商的联盟营销, 和一些流量优先的社交媒体平台. 总的来说, 为了更精细且专业的分析效果就会越来和 UTM 脱离, 为了追求多平台多元化投放分析的通用性会越来越靠近 UTM. 实质上属于 Google 的 UTM 便是基于 URL 分析的现代网络营销的那个中间标准, 如果是本身既是流量主平台也是广告主平台的话, 就几乎一定会倾向于与 UTM 脱离, 特别是移动应用主导的移动互联网广告, 其投放到转化的分析已经变成了软件 SDK 对受众设备信息的直接收集而不是依赖于浏览器环境下的 URL. 有的应用平台还会自己建立自己的用户活动分析框架, 让受众的分析精确到平台内活动的每一次动作, 以提升自身平台的数据分析能力加强对广告主的吸引力, 同时还能给平台内的流量主提供精确的数据分析报告. 广告付费类型 CPC - 按点击计费: Cost Per Click; 广告主按照受众点击广告的次数支付费用. 常见于搜索引擎和社交媒体广告. CPM - 按千次展示计费: Cost Per Mille; 广告主按照广告展示的(浏览)次数每 1000 次为单位支付费用, 这类广告通常注重曝光度, 常见于期望提升知名度的广告主投放. 常见于集中式的广告展示位. CPA - 按行动计费: Cost Per Action; 广告主按照受众完成特定营销活动中的特定行动次数支付费用, 这类广告注重受众转化率, 需要受众完成特定的诸如注册, 购买, 下载和分享等动作才能计算一次有效广告活动. 常见于联盟营销Affiliate式推广和各种追求社交媒体裂变的营销活动. CPS - 按销售计费: Cost Per Sale; 广告主按照营销活动带来的实际销售量或付费量支付费用, 常见于联盟营销和电商广告. CPL - 按潜在客户计费: Cost Per Lead; 按照获得的潜在客户数量计费, 常见于无法直接购买的非电商产品, 通常需要收集访客的信息(特别是联系方式), 广告主则按照获得的这类潜在客户数量支付费用. CPP - 按播放计费: Cost Per Play; 按播放量计算费用. 常见于视频平台上投放的广告片, 广告主以自己的广告片的播放量支付费用. CPD - 按天计费: Cost Per Da; 广告主按广告的展示时间支付费用, CPD 是按天计费. 这类广告常见于小型独立站点和特定合作中的广告. 注释 [GA4] URL builders: Collect campaign data with custom URLs - Analytics Help ↩︎","date_published":"2025-04-22T05:13:00.000Z","tags":["资料库","网络营销","数字广告"]},{"id":"https://blog.cxplay.org/works/music-narration-analysis-gunship-when-you-grow-up-your-heart-dies/","url":"https://blog.cxplay.org/works/music-narration-analysis-gunship-when-you-grow-up-your-heart-dies/","title":"音乐旁白解析: GUNSHIP - When You Grow Up, Your Heart Dies","content_html":"<h2 id=\"前言\"><a href=\"#前言\"></a>前言</h2>\n<p>歌曲发布之后, 很多人就在讨论这首歌的那些旁白. 这首歌的 MV 是我见过怀旧意味最浓的的欧美音乐 MV, 其中包含了大量美国<ruby>镀金时代<rt>Gilded Age</rt></ruby>(70 到 90 年代)的经典艺术作品, 大多都是那个时代的美国人的共同记忆.</p>\n<blockquote>\n<iframe width=\"100%\" height=\"315\" src=\"https://www.youtube.com/embed/ri9IefTuNzc?si=cE_kdn_5XvML-l1k\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n</blockquote>\n<p>歌曲的旁白引用全部来自 GUNSHIP 的粉丝, 这些旁白充满了经典表达, 社区中甚至已经认定这些旁白全都出自 80 和 90 年代经典影视作品的台词, 但是官方从来没有公开表示过, 于是粉丝开始了自己的挖掘之路. 当然我也是其中的一员, 很早就想知道这些经典句子来自哪里了.</p>\n<details class=\"toggle\" ><summary class=\"toggle-button\" style=\"\">GUNSHIP 的推文</summary><div class=\"toggle-content\"><blockquote  class=\"twitter-tweet\"><p lang=\"en\" dir=\"ltr\">Not us saying it - all these quotes were from our amazing fans, well... in this case, not fans but our collaborators. 🖤🙏🏼🖤 <a href=\"https://t.co/3eD0NblrWo\">https://t.co/3eD0NblrWo</a></p>&mdash; GUNSHIP (@GUNSHIPMUSIC) <a href=\"https://twitter.com/GUNSHIPMUSIC/status/1146680239675887617?ref_src=twsrc%5Etfw\">July 4, 2019</a></blockquote> <script async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>\n</div></details>\n<p>这其中有大量旁白是非常常见的英语表达, 即使说出是真的来自某某作品, 也很难让人信服, 而且也不会有官方的回答, 所以本文也只是一个很主观的解析. 相对来说, 歌曲 MV 中的画面也值得去分析, 但这就有些超出我的能力范围了, 还是等阅片无数的电影爱好者来做吧.</p>\n<h2 id=\"解析\"><a href=\"#解析\"></a>解析</h2>\n<p><em>When you grow up, your heart dies.</em><br>\nA: 当你长大了, 你的心就死了.</p>\n<p><em>Who cares?</em><br>\n<em>B: 谁在乎呢?</em></p>\n<p><em>I care.</em><br>\nA: 我在乎.</p>\n<p>本段对白出自 1980 年代经典剧集《<ruby>早餐俱乐部<rt>The Breakfast Club</rt></ruby>》<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup>角色<ruby>阿利森·雷诺兹<rt>Allison Reynolds</rt></ruby>(A)的台词, 回应的角色 B 是<ruby>约翰·本德<rt>John Bender</rt></ruby>.</p>\n<details class=\"toggle\" ><summary class=\"toggle-button\" style=\"\">原片切片 (YouTube)</summary><div class=\"toggle-content\"><iframe width=\"100%\" height=\"650\" src=\"https://www.youtube.com/embed/PXUyie0Rs-g?si=o2nXjOFLlwoEzba5\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe></div></details>\n<hr>\n<p><em>Whatever will be, will be.</em><br>\n「万事万物自有轨迹.」</p>\n<p>出自 1950 至 1960 年代最受美国人欢迎的女歌手<ruby>多丽丝·黛<rt>Doris Day</rt></ruby> <em>(1922 ~ 2019)</em> 演唱歌曲 <em>Que Sera, Sera (Whatever Will Be, Will Be)</em> 主要旋律歌词. 这首歌曲也是 1956 年的电影 <em>The Man Who Knew Too Much</em> (《擒凶记》) 的插曲, 该电影由著名英国导演<ruby>阿尔弗雷德·希区柯克<rt>Alfred Hitchcock</rt></ruby>执导, 借助这首歌曲该影片斩获了当年的<ruby>奥斯卡最佳原创歌曲奖<rt>Academy Award for Best Original Score</rt></ruby>.</p>\n<hr>\n<p><em>Don't care what anyone else thinks.</em><br>\n「不必在意别人的看法.  」</p>\n<p>出自开播于 1989 年的美国电视剧《<ruby>救命下课铃<rt>Saved by the Bell</rt></ruby>》, 下方的切片是 2020 年播出的同名改编版本. (其实也是常见的英语表达)</p>\n<details class=\"toggle\" ><summary class=\"toggle-button\" style=\"\">改编版切片 (Twitter)</summary><div class=\"toggle-content\"><blockquote  class=\"twitter-tweet\" data-media-max-width=\"560\"><p lang=\"en\" dir=\"ltr\">&quot;If you don&#39;t face your fears and just say, &#39;This is me, and I don&#39;t care what anyone else thinks,&#39; then you&#39;ll never be the person you were meant to be. You&#39;ll never be free.&quot; <a href=\"https://twitter.com/hashtag/NationalComingOutDay?src=hash&amp;ref_src=twsrc%5Etfw\">#NationalComingOutDay</a>. ❤️ <a href=\"https://t.co/csjo7DcgV5\">pic.twitter.com/csjo7DcgV5</a></p>&mdash; Peacock (@peacock) <a href=\"https://twitter.com/peacock/status/1447577736726388738?ref_src=twsrc%5Etfw\">October 11, 2021</a></blockquote> <script async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>\n</div></details>\n<hr>\n<p><em>You'll be okay, I promise.</em><br>\n你会没事的, 我保证.</p>\n<hr>\n<p><em>Just keep on keeping on.</em><br>\n不要停下脚步.</p>\n<hr>\n<p><em>It's just a bad day, not a bad life.</em><br>\n这只是糟糕的一天, 而不是不幸的一生.</p>\n<hr>\n<p><em>I'll always be there for you.</em><br>\n我永远与你同在.</p>\n<hr>\n<p><em>I swear the breath from my lungs.</em><br>\n我用我的生命起誓.</p>\n<hr>\n<p><em>You'll feel it when you know, life is worth the risk.</em><br>\n当你感受到生命的时候, 你会发现它值得冒险一试.</p>\n<hr>\n<p><em>Live long and prosper.</em><br>\n繁荣昌盛</p>\n<p>出自始于从 70 和 80 年代流行至今的《<ruby>星际迷航<rt>Star Trek</rt></ruby>》中的<ruby>瓦肯人<rt>Vulcan</rt></ruby>的经典祝福语. <sup class=\"footnote-ref\"><a href=\"#fn2\" id=\"fnref2\">[2]</a></sup> Emoji 中的 &quot;🖖&quot; 就是瓦肯人表达祝福时的手势.<br>\n剧中的经典瓦肯人演员<ruby>伦纳德·尼莫伊<rt>Leonard Nimoy</rt></ruby>于十年前逝世.</p>\n<details class=\"toggle\" ><summary class=\"toggle-button\" style=\"\">对瓦肯人祝福的解释 (YouTube)</summary><div class=\"toggle-content\"><iframe width=\"100%\" height=\"315\" src=\"https://www.youtube.com/embed/dijYeGAyg2k?si=qAq5qahrT55Vtkag\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n</div></details>\n<hr>\n<p><em>Believe in yourself and create your own destiny, Don't fear failure.</em><br>\n坚定自己内心, 创造自己的命运, 不要害怕失败.</p>\n<p>这句话出自美国动画电视频道 <em>Cartoon Network</em> 旗下的节目矩阵之一 <em>Toonami</em> <sup class=\"footnote-ref\"><a href=\"#fn3\" id=\"fnref3\">[3]</a></sup>的一个音乐视频, 该栏目 1997 年开始播出, 至今仍然在播出, 其节目标语是 &quot;为你带来更好的卡通节目&quot;. 这个音乐视频名为 <em>Broken Promise (Dreams)</em>, 是 Toonami 于 2001 年发布的音乐的同名动画短片. 这句标语出现在视频的结尾部分.</p>\n<details class=\"toggle\" ><summary class=\"toggle-button\" style=\"\">重新上传的原片 (YouTube)</summary><div class=\"toggle-content\"><iframe width=\"100%\" height=\"315\" src=\"https://www.youtube.com/embed/TfadJj4d5K4?si=ijDejT5FOnrI14S1\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>\n</div></details>\n<hr>\n<p><em>Don't ever give up.</em><br>\n永不言弃.</p>\n<hr>\n<p>Everything worth doing is hard .<br>\n凡有价值之事都不会易如反掌.</p>\n<p>可能出自美国总统<ruby>小西奥多·罗斯福<rt>Theodore Roosevelt</rt></ruby> <em>(任期 1901 ~ 1909)</em> 的格言. 全句是:</p>\n<blockquote>\n<p>Nothing in this world is worth having or worth doing unless it means effort, pain, difficulty. No kind of life is worth leading if it is always an easy life. I know that your life is hard; I know that your work is hard; and hardest of all for those of you who have the highest trained consciences, and who therefore feel always how much you ought to do. I know your work is hard, and that is why I congratulate you with all my heart. I have never in my life envied a human being who led an easy life; I have envied a great many people who led difficult lives and led them well.</p>\n</blockquote>\n<hr>\n<p><em>The only time that matters is right now.</em><br>\n唯一重要的时刻是现在.</p>\n<hr>\n<p><em>It's okay to feel lost.</em><br>\n感到迷茫也没有关系.</p>\n<hr>\n<p><em>Live life to the full because you never know what's around the corner.</em><br>\n毫无保留地去生活, 因为没人知道下一个转角会遇到什么.</p>\n<hr>\n<p><em>So let's take a part in the world and make it our own.</em><br>\n所以, 让我们投入其中创造自己的世界.</p>\n<hr>\n<p>Don't let the bastards grind you down.<br>\n不要让混蛋打垮你.</p>\n<p>出自伪拉丁语格言 <em>Illegitimi non carborundum</em><sup class=\"footnote-ref\"><a href=\"#fn4\" id=\"fnref4\">[4]</a></sup>, 由于其 &quot;伪&quot; 的形态, 无法在拉丁语中呈现含义. 只能使用拉丁语和英语混合进行模拟含义转换, 翻译后的含义即是这句话. 这句话被认为是起源自第二次世界大战, 之后逐渐流行, 被很多著名人物和作品引用, 去表达不惧逆境的含义. 所以也没办法实际找到歌词具体的引用出处.</p>\n<hr>\n<p><em>You saved me.</em><br>\n你拯救了我.</p>\n<hr>\n<p><em>There's no fate but what we make for ourselves.</em><br>\n没有(既定的)命运, 只有我们自己创造的命运.</p>\n<p>出自《<ruby>终结者<rt>The Terminator</rt></ruby>》, 该电影于 1984 年发布, 是来自人类反抗军首领<ruby>约翰·康纳<rt>John Connor</rt></ruby>的留言, 也是他的核心信念. 电影的 <em>No Fate</em> 概念也贯穿全系列, 是这部电影的精神核心, 一直延续到今天的《终结者》系列.</p>\n<hr>\n<p><em>We always have each other.</em><br>\n我们总是拥有彼此.</p>\n<hr>\n<p><em>Keep your head up, kid.</em><br>\n抬起头来, 孩子.</p>\n<hr>\n<p><em>Always fear regret more than failure.</em><br>\n遗憾比失败更令人恐惧.</p>\n<hr>\n<p><em>Stay true to yourself.</em><br>\n忠于自我.</p>\n<hr>\n<p><em>There is still good in the world.</em><br>\n美好仍然存在于世间.</p>\n<hr>\n<p><em>Expect nothing and appreciate everything.</em><br>\n不要期待任何事, 而是去珍惜一切.</p>\n<hr>\n<p><em>ニつに分かれた道は ずっと君のためにある。</em><br>\n这条一分二的路,  一直都是为你而存在的.</p>\n<hr>\n<p><em>It was all worth it.</em><br>\n这一切都值得.</p>\n<hr>\n<p><em>The sun never sets on your dreams.</em><br>\n在你梦中的太阳永远不会落下.</p>\n<h2 id=\"注释\"><a href=\"#注释\"></a>注释</h2>\n<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p><a href=\"https://en.wikipedia.org/wiki/The_Breakfast_Club\">The Breakfast Club - Wikipedia</a> <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn2\" class=\"footnote-item\"><p><a href=\"https://en.wikipedia.org/wiki/Vulcan_salute\">Vulcan salute - Wikipedia</a> <a href=\"#fnref2\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn3\" class=\"footnote-item\"><p><a href=\"https://zh.wikipedia.org/zh-cn/Toonami\">Toonami - 维基百科，自由的百科全书</a> <a href=\"#fnref3\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn4\" class=\"footnote-item\"><p><a href=\"https://en.wikipedia.org/wiki/Illegitimi_non_carborundum\">Illegitimi non carborundum - Wikipedia</a> <a href=\"#fnref4\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n","content_text":"前言 歌曲发布之后, 很多人就在讨论这首歌的那些旁白. 这首歌的 MV 是我见过怀旧意味最浓的的欧美音乐 MV, 其中包含了大量美国镀金时代Gilded Age(70 到 90 年代)的经典艺术作品, 大多都是那个时代的美国人的共同记忆. 歌曲的旁白引用全部来自 GUNSHIP 的粉丝, 这些旁白充满了经典表达, 社区中甚至已经认定这些旁白全都出自 80 和 90 年代经典影视作品的台词, 但是官方从来没有公开表示过, 于是粉丝开始了自己的挖掘之路. 当然我也是其中的一员, 很早就想知道这些经典句子来自哪里了. GUNSHIP 的推文Not us saying it - all these quotes were from our amazing fans, well... in this case, not fans but our collaborators. 🖤🙏🏼🖤 https://t.co/3eD0NblrWo&mdash; GUNSHIP (@GUNSHIPMUSIC) July 4, 2019 这其中有大量旁白是非常常见的英语表达, 即使说出是真的来自某某作品, 也很难让人信服, 而且也不会有官方的回答, 所以本文也只是一个很主观的解析. 相对来说, 歌曲 MV 中的画面也值得去分析, 但这就有些超出我的能力范围了, 还是等阅片无数的电影爱好者来做吧. 解析 When you grow up, your heart dies. A: 当你长大了, 你的心就死了. Who cares? B: 谁在乎呢? I care. A: 我在乎. 本段对白出自 1980 年代经典剧集《早餐俱乐部The Breakfast Club》[1]角色阿利森·雷诺兹Allison Reynolds(A)的台词, 回应的角色 B 是约翰·本德John Bender. 原片切片 (YouTube) Whatever will be, will be. 「万事万物自有轨迹.」 出自 1950 至 1960 年代最受美国人欢迎的女歌手多丽丝·黛Doris Day (1922 ~ 2019) 演唱歌曲 Que Sera, Sera (Whatever Will Be, Will Be) 主要旋律歌词. 这首歌曲也是 1956 年的电影 The Man Who Knew Too Much (《擒凶记》) 的插曲, 该电影由著名英国导演阿尔弗雷德·希区柯克Alfred Hitchcock执导, 借助这首歌曲该影片斩获了当年的奥斯卡最佳原创歌曲奖Academy Award for Best Original Score. Don't care what anyone else thinks. 「不必在意别人的看法. 」 出自开播于 1989 年的美国电视剧《救命下课铃Saved by the Bell》, 下方的切片是 2020 年播出的同名改编版本. (其实也是常见的英语表达) 改编版切片 (Twitter)&quot;If you don&#39;t face your fears and just say, &#39;This is me, and I don&#39;t care what anyone else thinks,&#39; then you&#39;ll never be the person you were meant to be. You&#39;ll never be free.&quot; #NationalComingOutDay. ❤️ pic.twitter.com/csjo7DcgV5&mdash; Peacock (@peacock) October 11, 2021 You'll be okay, I promise. 你会没事的, 我保证. Just keep on keeping on. 不要停下脚步. It's just a bad day, not a bad life. 这只是糟糕的一天, 而不是不幸的一生. I'll always be there for you. 我永远与你同在. I swear the breath from my lungs. 我用我的生命起誓. You'll feel it when you know, life is worth the risk. 当你感受到生命的时候, 你会发现它值得冒险一试. Live long and prosper. 繁荣昌盛 出自始于从 70 和 80 年代流行至今的《星际迷航Star Trek》中的瓦肯人Vulcan的经典祝福语. [2] Emoji 中的 &quot;🖖&quot; 就是瓦肯人表达祝福时的手势. 剧中的经典瓦肯人演员伦纳德·尼莫伊Leonard Nimoy于十年前逝世. 对瓦肯人祝福的解释 (YouTube) Believe in yourself and create your own destiny, Don't fear failure. 坚定自己内心, 创造自己的命运, 不要害怕失败. 这句话出自美国动画电视频道 Cartoon Network 旗下的节目矩阵之一 Toonami [3]的一个音乐视频, 该栏目 1997 年开始播出, 至今仍然在播出, 其节目标语是 &quot;为你带来更好的卡通节目&quot;. 这个音乐视频名为 Broken Promise (Dreams), 是 Toonami 于 2001 年发布的音乐的同名动画短片. 这句标语出现在视频的结尾部分. 重新上传的原片 (YouTube) Don't ever give up. 永不言弃. Everything worth doing is hard . 凡有价值之事都不会易如反掌. 可能出自美国总统小西奥多·罗斯福Theodore Roosevelt (任期 1901 ~ 1909) 的格言. 全句是: Nothing in this world is worth having or worth doing unless it means effort, pain, difficulty. No kind of life is worth leading if it is always an easy life. I know that your life is hard; I know that your work is hard; and hardest of all for those of you who have the highest trained consciences, and who therefore feel always how much you ought to do. I know your work is hard, and that is why I congratulate you with all my heart. I have never in my life envied a human being who led an easy life; I have envied a great many people who led difficult lives and led them well. The only time that matters is right now. 唯一重要的时刻是现在. It's okay to feel lost. 感到迷茫也没有关系. Live life to the full because you never know what's around the corner. 毫无保留地去生活, 因为没人知道下一个转角会遇到什么. So let's take a part in the world and make it our own. 所以, 让我们投入其中创造自己的世界. Don't let the bastards grind you down. 不要让混蛋打垮你. 出自伪拉丁语格言 Illegitimi non carborundum[4], 由于其 &quot;伪&quot; 的形态, 无法在拉丁语中呈现含义. 只能使用拉丁语和英语混合进行模拟含义转换, 翻译后的含义即是这句话. 这句话被认为是起源自第二次世界大战, 之后逐渐流行, 被很多著名人物和作品引用, 去表达不惧逆境的含义. 所以也没办法实际找到歌词具体的引用出处. You saved me. 你拯救了我. There's no fate but what we make for ourselves. 没有(既定的)命运, 只有我们自己创造的命运. 出自《终结者The Terminator》, 该电影于 1984 年发布, 是来自人类反抗军首领约翰·康纳John Connor的留言, 也是他的核心信念. 电影的 No Fate 概念也贯穿全系列, 是这部电影的精神核心, 一直延续到今天的《终结者》系列. We always have each other. 我们总是拥有彼此. Keep your head up, kid. 抬起头来, 孩子. Always fear regret more than failure. 遗憾比失败更令人恐惧. Stay true to yourself. 忠于自我. There is still good in the world. 美好仍然存在于世间. Expect nothing and appreciate everything. 不要期待任何事, 而是去珍惜一切. ニつに分かれた道は ずっと君のためにある。 这条一分二的路, 一直都是为你而存在的. It was all worth it. 这一切都值得. The sun never sets on your dreams. 在你梦中的太阳永远不会落下. 注释 The Breakfast Club - Wikipedia ↩︎ Vulcan salute - Wikipedia ↩︎ Toonami - 维基百科，自由的百科全书 ↩︎ Illegitimi non carborundum - Wikipedia ↩︎","summary":"前言 歌曲发布之后, 很多人就在讨论这首歌的那些旁白. 这首歌的 MV 是我见过怀旧意味最浓的的欧美音乐 MV, 其中包含了大量美国镀金时代Gilded Age(70 到 90 年代)的经典艺术作品, 大多都是那个时代的美国人的共同记忆. 歌曲的旁白引用全部来自 GUNSHIP 的粉丝, 这些旁白充满了经典表达, 社区中甚至已经认定这些旁白全都出自 80 和 90 年代经典影视作品的台词, 但是官方从来没有公开表示过, 于是粉丝开始了自己的挖掘之路. 当然我也是其中的一员, 很早就想知道这些经典句子来自哪里了. GUNSHIP 的推文Not us saying it - all these quotes were from our amazing fans, well... in this case, not fans but our collaborators. 🖤🙏🏼🖤 https://t.co/3eD0NblrWo&mdash; GUNSHIP (@GUNSHIPMUSIC) July 4, 2019 这其中有大量旁白是非常常见的英语表达, 即使说出是真的来自某某作品, 也很难让人信服, 而且也不会有官方的回答, 所以本文也只是一个很主观的解析. 相对来说, 歌曲 MV 中的画面也值得去分析, 但这就有些超出我的能力范围了, 还是等阅片无数的电影爱好者来做吧. 解析 When you grow up, your heart dies. A: 当你长大了, 你的心就死了. Who cares? B: 谁在乎呢? I care. A: 我在乎. 本段对白出自 1980 年代经典剧集《早餐俱乐部The Breakfast Club》[1]角色阿利森·雷诺兹Allison Reynolds(A)的台词, 回应的角色 B 是约翰·本德John Bender. 原片切片 (YouTube) Whatever will be, will be. 「万事万物自有轨迹.」 出自 1950 至 1960 年代最受美国人欢迎的女歌手多丽丝·黛Doris Day (1922 ~ 2019) 演唱歌曲 Que Sera, Sera (Whatever Will Be, Will Be) 主要旋律歌词. 这首歌曲也是 1956 年的电影 The Man Who Knew Too Much (《擒凶记》) 的插曲, 该电影由著名英国导演阿尔弗雷德·希区柯克Alfred Hitchcock执导, 借助这首歌曲该影片斩获了当年的奥斯卡最佳原创歌曲奖Academy Award for Best Original Score. Don't care what anyone else thinks. 「不必在意别人的看法. 」 出自开播于 1989 年的美国电视剧《救命下课铃Saved by the Bell》, 下方的切片是 2020 年播出的同名改编版本. (其实也是常见的英语表达) 改编版切片 (Twitter)&quot;If you don&#39;t face your fears and just say, &#39;This is me, and I don&#39;t care what anyone else thinks,&#39; then you&#39;ll never be the person you were meant to be. You&#39;ll never be free.&quot; #NationalComingOutDay. ❤️ pic.twitter.com/csjo7DcgV5&mdash; Peacock (@peacock) October 11, 2021 You'll be okay, I promise. 你会没事的, 我保证. Just keep on keeping on. 不要停下脚步. It's just a bad day, not a bad life. 这只是糟糕的一天, 而不是不幸的一生. I'll always be there for you. 我永远与你同在. I swear the breath from my lungs. 我用我的生命起誓. You'll feel it when you know, life is worth the risk. 当你感受到生命的时候, 你会发现它值得冒险一试. Live long and prosper. 繁荣昌盛 出自始于从 70 和 80 年代流行至今的《星际迷航Star Trek》中的瓦肯人Vulcan的经典祝福语. [2] Emoji 中的 &quot;🖖&quot; 就是瓦肯人表达祝福时的手势. 剧中的经典瓦肯人演员伦纳德·尼莫伊Leonard Nimoy于十年前逝世. 对瓦肯人祝福的解释 (YouTube) Believe in yourself and create your own destiny, Don't fear failure. 坚定自己内心, 创造自己的命运, 不要害怕失败. 这句话出自美国动画电视频道 Cartoon Network 旗下的节目矩阵之一 Toonami [3]的一个音乐视频, 该栏目 1997 年开始播出, 至今仍然在播出, 其节目标语是 &quot;为你带来更好的卡通节目&quot;. 这个音乐视频名为 Broken Promise (Dreams), 是 Toonami 于 2001 年发布的音乐的同名动画短片. 这句标语出现在视频的结尾部分. 重新上传的原片 (YouTube) Don't ever give up. 永不言弃. Everything worth doing is hard . 凡有价值之事都不会易如反掌. 可能出自美国总统小西奥多·罗斯福Theodore Roosevelt (任期 1901 ~ 1909) 的格言. 全句是: Nothing in this world is worth having or worth doing unless it means effort, pain, difficulty. No kind of life is worth leading if it is always an easy life. I know that your life is hard; I know that your work is hard; and hardest of all for those of you who have the highest trained consciences, and who therefore feel always how much you ought to do. I know your work is hard, and that is why I congratulate you with all my heart. I have never in my life envied a human being who led an easy life; I have envied a great many people who led difficult lives and led them well. The only time that matters is right now. 唯一重要的时刻是现在. It's okay to feel lost. 感到迷茫也没有关系. Live life to the full because you never know what's around the corner. 毫无保留地去生活, 因为没人知道下一个转角会遇到什么. So let's take a part in the world and make it our own. 所以, 让我们投入其中创造自己的世界. Don't let the bastards grind you down. 不要让混蛋打垮你. 出自伪拉丁语格言 Illegitimi non carborundum[4], 由于其 &quot;伪&quot; 的形态, 无法在拉丁语中呈现含义. 只能使用拉丁语和英语混合进行模拟含义转换, 翻译后的含义即是这句话. 这句话被认为是起源自第二次世界大战, 之后逐渐流行, 被很多著名人物和作品引用, 去表达不惧逆境的含义. 所以也没办法实际找到歌词具体的引用出处. You saved me. 你拯救了我. There's no fate but what we make for ourselves. 没有(既定的)命运, 只有我们自己创造的命运. 出自《终结者The Terminator》, 该电影于 1984 年发布, 是来自人类反抗军首领约翰·康纳John Connor的留言, 也是他的核心信念. 电影的 No Fate 概念也贯穿全系列, 是这部电影的精神核心, 一直延续到今天的《终结者》系列. We always have each other. 我们总是拥有彼此. Keep your head up, kid. 抬起头来, 孩子. Always fear regret more than failure. 遗憾比失败更令人恐惧. Stay true to yourself. 忠于自我. There is still good in the world. 美好仍然存在于世间. Expect nothing and appreciate everything. 不要期待任何事, 而是去珍惜一切. ニつに分かれた道は ずっと君のためにある。 这条一分二的路, 一直都是为你而存在的. It was all worth it. 这一切都值得. The sun never sets on your dreams. 在你梦中的太阳永远不会落下. 注释 The Breakfast Club - Wikipedia ↩︎ Vulcan salute - Wikipedia ↩︎ Toonami - 维基百科，自由的百科全书 ↩︎ Illegitimi non carborundum - Wikipedia ↩︎","date_published":"2025-04-21T13:24:00.000Z","tags":["收藏夹","音乐","英语"]},{"id":"https://blog.cxplay.org/works/nostr-quick-start-guide/","url":"https://blog.cxplay.org/works/nostr-quick-start-guide/","title":"欢迎加入 Nostr, 这是一份快速入门指南.","content_html":"<blockquote>\n<p>本文已经合并至: <a href=\"https://join.nostr.moe\">join.nostr.moe</a></p>\n</blockquote>\n<h2 id=\"前言\"><a href=\"#前言\"></a>前言</h2>\n<p>如果你还不知道什么是 Nostr, 或者你已经使用过了 Fediverse 相关应用, 那么可以尝试阅读以下文章深入了解这两者的区别和优缺点:</p>\n<blockquote>\n<p><a href=\"/works/why-dont-i-recommend-use-nostr-and-fediverse/\">为什么我不推荐 Nostr 和 Fediverse? | CXPLAY World</a></p>\n</blockquote>\n<p>简而言之, Nostr 是相比联邦制为基础的 Fediverse 而言去中心化更加彻底的协议, 联邦制的社交网络也包括了后来新秀 Bluesky.</p>\n<p>但是, 虽然两年过去了, 协议的社交生态已经接近完善, 这里还是有不得不对想要尝试的读者说的一些「丑话」, 好节约部分读者的时间:</p>\n<ol>\n<li>Nostr 协议和协议生态受到比特币资金的直接资助, 内循环经济和总体共识也是比特币为首.</li>\n<li>Nostr 是个真正的分布式去中心化社交网络, 相比中心化和联邦式社交网络, 你已经发布的内容的传播方式并不完全受你控制, 包括删除操作.</li>\n<li>密钥对的使用难度仅仅比 GPG 稍低一些, 并且 Nostr 不存在现行广泛应用的密钥吊销和子密钥协定, 需要你有良好的密钥管理和安全习惯.</li>\n<li>Nostr 协议的各个实现议定(NIP)不使用开源协议, 而是默认直接置于公有领域.</li>\n</ol>\n<p>本指南均在桌面环境下进行指导.</p>\n<h2 id=\"创建密钥对-账户\"><a href=\"#创建密钥对-账户\"></a>创建密钥对 (账户)</h2>\n<p>在 Nostr 中, &quot;账户&quot; 的概念已经被为公钥和私钥取代, 如果你使用过 GPG, 那么就不难理解其背后的用意, Nostr 就是使用密钥对进行签署和加密的通讯协议, 不过 Nostr 使用的算法是比特币在内的加密货币使用的算法 secp256k1. 如果你没有接触过非对称加密, 类比一下既是: 公钥等于唯一账户名, 私钥等于账户密码, 而这里的账户名和密码永远唯一且无法被修改.</p>\n<p>确保你使用的设备是安全的. 使用浏览器隐私模式打开网页工具: <a href=\"https://nak.nostr.com/\">https://nak.nostr.com/</a></p>\n<blockquote>\n<p>如果有更高的安全要求, 建议使用一次性的离线虚拟机将工具链导入后离线操作, 直到操作到加密私钥导出.</p>\n</blockquote>\n<p>点击 &quot;generate keypair&quot; 生成一对密钥:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_152319.webp\" alt=\"生成密钥\" loading='lazy'></p>\n<p>在 (2) 标记框中所显示的内容为:</p>\n<ol>\n<li><strong>private key (hex)</strong>: 十六进制的私钥, 通常用于机器内运算使用.</li>\n<li><strong>nsec</strong>: 编码后的私钥, 具有明显的 <code>nsec</code> 头部便于人类识别.</li>\n<li><strong>public key (hex)</strong>: 十六进制的公钥, 通常用于机器内运算使用.</li>\n<li><strong>npub</strong>: 编码后公钥, 具有明显的 <code>npub</code> 头部便于人类识别.</li>\n<li><strong>nprofile</strong>: 另一种编码后的公钥, 主要用于指向用户的资料, 所以使用了 <code>nprofile</code> 为头部.</li>\n</ol>\n<p>这五部分中, 只需要将 <strong>nsec</strong> 作为密码, <strong>npub</strong> 作为账户名保存在密码管理器中即可. 但要注意, nsec 永远无法被修改, 一旦泄露将永远丢失账户的掌控权, 请将其作为比密码还要重要的信息保存起来.</p>\n<p>保存完毕后关闭隐私标签页, 用剪贴板传递 nsec 之后注意清空剪贴板记录.</p>\n<h2 id=\"安装密钥管理器\"><a href=\"#安装密钥管理器\"></a>安装密钥管理器</h2>\n<ol>\n<li><strong>Chrome 或类 Chromium:</strong> <a href=\"https://chromewebstore.google.com/detail/nos2x/kpgefcfmnafjgpblomihpgmejjdanjjp\">nos2x - Chrome 应用商店</a></li>\n<li><strong>Firefox</strong>: <a href=\"https://addons.mozilla.org/en-US/firefox/addon/nos2x-fox/\">nos2x-fox – Get this Extension for 🦊 Firefox (en-US)</a></li>\n</ol>\n<blockquote>\n<p>本文使用类 Chromium 浏览器的 nos2x 作为演示.</p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_153834.webp\" alt=\"安装 nos2x\" loading='lazy'></p>\n<p>右键 nos2x 的图标打开扩展选项:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_153944.webp\" alt=\"打开选项\" loading='lazy'></p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_154029.webp\" alt=\"nos2x 选项页面\" loading='lazy'></p>\n<p>在 private key 下的输入框输入你刚刚生成的 nsec 然后保存</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_154431.webp\" alt=\"保存私钥配置\" loading='lazy'></p>\n<p>点击 show key encrypted 按钮:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_154554.webp\" alt=\"显示加密后的私钥按钮\" loading='lazy'></p>\n<p>在出现的 password 输入框输入一个<strong>只有你脑子里记住且永远不会记在其他地方包括密码管理器的密码作为 PIN</strong>, 支持数字字母, 建议至少四位及以上.</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_154639.webp\" alt=\"输入加密私钥的 PIN\" loading='lazy'></p>\n<p>输入完成后点击 encrypt and show key, 显示被对称加密后的私钥:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_155240.webp\" alt=\"显示加密后的私钥\" loading='lazy'></p>\n<p>此时被显示出来的以 <strong>ncryptsec</strong> 开头出现在 private key 输入框中和下方二维码中的私钥既是使用你输入的密码加密后的私钥, 将这个 ncryptsec 作为一个新的密码保存在密码管理器中即可. 此时建议将 nsec 单独保存在一个独立的密码库中, 之后如果要传递私钥, 尽量让这个 ncryptsec 作为包装后的私钥进行传递.</p>\n<p>此后需要记住, 永远不要将私钥包括包装后的私钥交给你无法完全确认风险的地方, 永远优先使用可信的密钥管理器来管理密钥.</p>\n<h2 id=\"创建用户资料\"><a href=\"#创建用户资料\"></a>创建用户资料</h2>\n<p>密钥对如果没有任何事件与之关联则没有任何价值, 所以现在需要为这对密钥创建一个 Nostr 网络中的用户资料.</p>\n<blockquote>\n<p>不建议使用 nos2x 中提示的那个外部资料编辑器.</p>\n</blockquote>\n<p>打开独立的用户资料管理器: <a href=\"https://metadata.nostr.com/\">https://metadata.nostr.com/</a></p>\n<p>点击 Load My Profile 弹出密钥管理器授权请求:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_160520.webp\" alt=\"授权读取公钥\" loading='lazy'></p>\n<p>这个网站会请求 <strong>read your public key</strong> 权限, 也就是 &quot;读取你的公钥&quot; 权限, 用于查找你的用户资料, 点击 authorize just this 授权这次读取.</p>\n<p>此时网站会进行查找, 会提示 <strong>No Profile Events Found</strong>, 这是意料之中的, 因为我们刚刚创建了一个全新的密钥对.</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_160656.webp\" alt=\"找不到用户资料\" loading='lazy'></p>\n<p>点击上方导航栏的 <strong>Metadata</strong> 进入用户资料编辑器:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_160822.webp\" alt=\"Metadata\" loading='lazy'></p>\n<p>填写用户资料表单:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_161052.webp\" alt=\"填写表单\" loading='lazy'></p>\n<ol>\n<li><strong>name</strong>: 你想要的任何名字, 这会显示在你今后的用户资料上, 建议填写.</li>\n<li><strong>nip05</strong>: 域名身份标签, 这里暂时不填写.</li>\n<li><strong>about</strong>: 关于你的描述, 建议填写.</li>\n<li><strong>picture</strong>: 你的头像, 需要输入一张图片的外链, 建议填写. (实在找不到, 可以先去 <a href=\"https://nostr.build/freeview/?k=img\">nostr.build</a> 随便右键复制一张图片的链接过来先用着)</li>\n<li><strong>banner</strong>: 资料页中显示的横幅背景, 暂时先不填写.</li>\n<li><strong>lud06 (LNURL)</strong>: 闪电网络的收款 URL, 已经是过时的字段了, 不填写.</li>\n<li><strong>lud16</strong>: 闪电网络的收款地址, 暂时先不填写.</li>\n</ol>\n<blockquote>\n<p>所有的字段都是可选的, 也可以随时修改.</p>\n</blockquote>\n<p>填写完成后点击 Save 保存你的用户资料.</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_161850.webp\" alt=\"填写示例\" loading='lazy'></p>\n<p>此时密钥 nos2x 会弹出授权提示签署你的第一个事件:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_161956.webp\" alt=\"签署用户资料事件\" loading='lazy'></p>\n<p>点击 authorize just this 允许本次签署.</p>\n<p>注意, 此时这份用户资料仅仅只是签署了之后保存在了网站缓存里面, 还没有真正传递到 Nostr 网络上, 请继续完成下面的中继配置设置.</p>\n<h2 id=\"配置中继\"><a href=\"#配置中继\"></a>配置中继</h2>\n<p>同样是 <a href=\"http://metadata.nostr.com\">metadata.nostr.com</a>, 签署你的用户资料后能够预览到一部分资料.</p>\n<p>此时点击导航栏的 Relays 进入中继配置:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_162414.webp\" alt=\"进入中继配置\" loading='lazy'></p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_162450.webp\" alt=\"中继配置界面\" loading='lazy'></p>\n<p>不用管 read 和 write 选项, 直接在输入框中输入以下几个推荐中继, 一个中继输入后点击 Add 再添加一行:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_162637.webp\" alt=\"输入中继示例\" loading='lazy'></p>\n<ol>\n<li><code>wss://relay.damus.io/</code></li>\n<li><code>wss://relay.nostr.band/</code></li>\n<li><code>wss://nostr-relay.app/</code></li>\n<li><code>wss://nos.lol/</code></li>\n<li><code>wss://purplerelay.com/</code></li>\n</ol>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_163317.webp\" alt=\"添加上方的中继示例\" loading='lazy'></p>\n<p>然后点击 Save, 保存配置, 网站会请求密钥管理器 nos2x 签署事件</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_163432.webp\" alt=\"签署事件\" loading='lazy'></p>\n<p>点击 authorize just this 允许本次签署, 向这些中继发布中继配置元数据.</p>\n<p>此时建议重复上一节的用户资料操作, 再一次完善你的用户资料后发布到这些你设置的中继中, 接下来开始正式进入 Nostr 网络了.</p>\n<h2 id=\"使用客户端\"><a href=\"#使用客户端\"></a>使用客户端</h2>\n<p>Nostr 有非常非常多的客户端, 这里将从最基础的社交媒体客户端开始.</p>\n<p>打开 Nostr.moe 社区客户端: <a href=\"https://nostr.moe/\">https://nostr.moe/</a></p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_163924.webp\" alt=\"\" loading='lazy'></p>\n<p>点击左下角的 &quot;登录&quot; 按钮, 弹出登录弹窗, 选择 &quot;浏览器插件登录&quot;:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_164051.webp\" alt=\"使用浏览器插件登录\" loading='lazy'></p>\n<p>此时会 nos2x 弹出 <strong>read your public key</strong> 授权请求弹窗, 点击 authorize just this 允许本次授权, 如果你以后想要自动授权使用公钥登录, 那么就点击 authorize forever.</p>\n<blockquote>\n<p>任何人都可以读取你的公钥, 读取公钥也不会损害账户.</p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_164345.webp\" alt=\"授权读取公钥\" loading='lazy'></p>\n<p>授权完成后你就成功登录到 Nostr.moe 社区了, 左下角不出意外会显示出你的头像和用户名:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_164515.webp\" alt=\"你的用户资料已经成功发布到 Nostr 网络\" loading='lazy'></p>\n<p>点击一下就可以弹出二级窗口, 选择 &quot;个人资料&quot; 就可以看到你设置的所有资料了:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_164710.webp\" alt=\"查看自己的用户资料\" loading='lazy'></p>\n<h2 id=\"发布帖子\"><a href=\"#发布帖子\"></a>发布帖子</h2>\n<p>还是在 Nostr.moe 社区, 点击左侧边栏的 &quot;发布笔记&quot; 开始编辑你的第一条正式帖子:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_164853.webp\" alt=\"编辑帖子\" loading='lazy'></p>\n<p>随便写点什么, 可以传图片上去:</p>\n<blockquote>\n<p>Nostr 发图片的方式就是插入图片链接.</p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_165202.webp\" alt=\"插入图片\" loading='lazy'></p>\n<p>点击 &quot;发布笔记&quot; 请求 nos2x 授权签署:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_165304.webp\" alt=\"开始签署事件\" loading='lazy'></p>\n<p>点击 authorize just this 允许本次授权, 如果你以后想要在 Nostr.moe 自动授权签署这类笔记的发布, 那么就点击 authorize forever.</p>\n<blockquote>\n<p>如果你收到了神秘事件(你还不知道的授权类型)的授权请求, 请首选考虑拒绝(reject), 如果多次来骚扰你, 直接点击 reject *** forever 即可.</p>\n</blockquote>\n<p>发布之后, 右下角会提示你已经成功发布, 但你会发现, 笔记还是没有出现在你的视野中:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_165501.webp\" alt=\"我笔记呢?\" loading='lazy'></p>\n<p>不要惊恐不要失望, 点击你显示资料页栏目中的 &quot;重新加载笔记&quot; 看看:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_165604.webp\" alt=\"我真的是初音未来\" loading='lazy'></p>\n<p>笔记出现在了你的资料中, 说明笔记成功发布出去了, 笔记右下角的计数器就表示在哪些中继(服务器)中找到了你的笔记.</p>\n<h2 id=\"尽情探索\"><a href=\"#尽情探索\"></a>尽情探索</h2>\n<p>欢迎加入 Nostr! 不管你是否打算在此停留多久.</p>\n<p>探索原则:</p>\n<ol>\n<li>永远不要授权你不知道实际用途的事件的签署请求, 也不要允许客户端签署你不想要的任何事件, 比如突然请求更新你的用户资料等.</li>\n<li>永远不要将你的任何形式的私钥暴露给其他任何人.</li>\n<li>永远不要在任何客户端中直接使用私钥, 如果它要求你只能输入私钥使用, 请放弃这个客户端.</li>\n</ol>\n<p>你可能需要像二次确认用户资料一样, 你还需要确认一遍你的中继配置是否完整:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_170555.webp\" alt=\"确认你的中继配置\" loading='lazy'></p>\n<p>不出意料的话, 这里很可能是空的:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_170640.webp\" alt=\"查看客户端实际读取到的中继配置\" loading='lazy'></p>\n<p>所以你需要将<a href=\"#%E9%85%8D%E7%BD%AE%E4%B8%AD%E7%BB%A7\">配置中继</a>小节的那些中继再次输入到客户端进行 &quot;添加&quot;.</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/nostr-quick-start-guide/2025-04-04_170919.webp\" alt=\"二次确认中继配置\" loading='lazy'></p>\n<p>这样才能确保你的基础连接是没有问题的, 除了 &quot;探索原则&quot;, 这里还有几条重要提示, 是由于分布式网络带来的额外特性:</p>\n<ol>\n<li>你的用户资料(kind:0)和中继配置(kind:10002)是最重要的账户元数据, 需要看好它们, 并让它们尽可能地传播地越广越好.</li>\n<li>Nostr 作为社交网络运行时, 无法做到 &quot;私密账户&quot; 或 &quot;带锁账户&quot; 这种东西, 你的消息内容没有准备好为每个已知的受众进行不对称加密, 公共消息永远无法做到权限控制.</li>\n<li>Nostr 被签署并广播的消息不存在 &quot;永远删除&quot;, 一旦被签署并广播, 消息就无法被完全撤回. Nostr 的删除更像是声明为 &quot;无效&quot;, 当然也只有消息公钥对应的私钥才能签署声明这种 &quot;无效&quot; 状态.</li>\n<li>Nostr 的关注始终是单向同意的, 所以被关注者永远无法获知自己确切的粉丝数量.</li>\n<li>Nostr 帖子的发布者无法主动审查帖子关联线程中的消息, 包括作者在内的所有人都只能选择隐藏.</li>\n</ol>\n<h2 id=\"加入-nostr-moe-社区\"><a href=\"#加入-nostr-moe-社区\"></a>加入 Nostr.moe 社区</h2>\n<p>Nostr.moe 是笔者托管的一个中继器和客户端组成的小型社区, 中继器用于存储受邀用户的消息, 客户端实际上只是充当过滤器的消息视图. 这种社区状态类似于 Torrent 索引, 而非固定的中心化及联邦式社交中的单一实例. 得益于 Nostr 分布式的优势, 用户可以随意切换客户端和服务器也不用担心和自己熟悉的人失去联系, 也不会因为客户端和服务器被关闭而无法再参与网络和丢失自己的消息.</p>\n<p>Nostr.moe 旨在聚集 Nostr 网络中的 ACG 爱好者, 即 <strong>A</strong>nime(动漫), <strong>C</strong>omic(漫画), <strong>G</strong>ame(游戏), 现在一般还会带上单独的 <strong>N</strong>ovel(小说). 这是种维持社区氛围的共识, 而非单一的 &quot;二次元&quot; 限定的话题, 是以小众爱好维持大众社交状态的一个意向.</p>\n<p><strong>加入社区, 你可以获得:</strong></p>\n<ol>\n<li><code>wss://relay.nostr.moe/</code> 中继的写入权限, 使消息出现在社区默认视图中.</li>\n<li><code>example@nostr.moe</code> 格式的 NIP-05 域名标签.</li>\n</ol>\n<p><strong>加入社区, 你需要同意或认同以下规则:</strong></p>\n<ol>\n<li>社区管理员, 主要客户端开发者, 服务端中继开发者包含中国人或华裔.</li>\n<li>认同 ACG 或 ACGN 文化.</li>\n<li>这是公开社区, 是网络上的公共场所, 只是一群人相对坐得更近了一点而已. 因此不能在社区中做公共场合被认为是不体面的事情(比如宣扬仇恨言论, 民族主义, 言语骚扰, 人身攻击, 以及挑拨离间在内的特别是键盘政治等<strong>你不想承担舆情引导和言论责任的事情</strong>等), 因此内容的受众年龄等级也被限定在全年龄. 玩梗玩抽象请适度, 钓鱼反串的风险会更高.</li>\n<li>社区支持言论自由, 协议也天然具有抗审查特性, 但社区<strong>不提供匿名表达和言论责任规避</strong>服务.</li>\n<li>遵守你的籍贯国和人身所处地的法律.</li>\n<li>遵守我们的基础设施供应商和域名注册局的服务条款.</li>\n<li>你需要作为人类来发布人造的消息内容, 而不是依靠自动化或 AIGC 来阻塞时间线.</li>\n<li>你所转发, 引用和回复的消息也会进入社区消息视图之中, 所以这类消息依然受到规则制约. 不过目前点赞和表情回应不包含在社区消息视图中, 但管理员建议: 做敏感事情的时候应该去使用其他不在社区成员名单中的密钥对, 更不要让两对密钥对的消息发生关联.</li>\n</ol>\n<p><strong>申请加入社区的方式:</strong></p>\n<ol>\n<li>\n<p>向常规客户端的开源仓库目录中的 <code>.well-known</code> 目录发起 PR 请求, 将你的<strong>十六进制公钥</strong>按照格式添加到 <code>nostr.json</code> 文件中.</p>\n<blockquote>\n<p><a href=\"https://github.com/cxplay/jumble-pub/blob/nostr.moe/public/.well-known/nostr.json\">https://github.com/cxplay/jumble-pub/blob/nostr.moe/public/.well-known/nostr.json</a></p>\n</blockquote>\n</li>\n<li>\n<p>联系到<a href=\"https://cx.ms/\">管理员</a>, 发送公钥和域名标签用户名申请加入.</p>\n</li>\n</ol>\n<p><strong>审核流程:</strong></p>\n<ol>\n<li>如果你不是管理员在网络上的 &quot;熟人&quot;, 那么你需要发送自己的网络主页(博客或其他)证明自己不是追求匿名而选择 Nostr.moe 的「神秘人」或「隐私怪」, 又或者是为了开小号而来的人. 请确保管理员能够认定你能够符合社区规则, 否则审核的第一步就会举步维艰.</li>\n<li>由于客户端和服务端都不会也无法设置不透明的基于消息内容的审查措施(如关键词), 因此在违反规则不听劝阻的情况下, 管理员为了维护社区秩序只能撤回社区成员的对社区中继的写权限, 并在社区中继中移除关联公钥的所有消息记录, 还有与之关联的域名标签.</li>\n<li>当权限添加完毕后对应的公钥会被<a href=\"https://nostr.moe/users/npub1jl40evdcgwx4d54rxzwxltcg4esmucv2vhy8k63f24y75ae6c2wsdmuk5w\">管理员账户</a>提及, 这表示你已经成功加入社区, 获得了社区中继的写入权限和域名标签认证.</li>\n</ol>\n<p><strong>其他说明:</strong></p>\n<ol>\n<li>社区成员的 NIP-05 域名标签主要用作公钥目录使用, 并不强制要求社区成员必须在自己的用户资料中填写它. 如果你不喜欢或者你有自己的想用的标签, 不管你有没有加入社区, 都可以随意使用.</li>\n<li>社区中继的写权限是与消息 JSON 中的公钥对应, 其他引用社区成员消息的消息由于消息中的公钥不对应, 无法进入社区中继, 同样的成员消息中引用的其他非成员的消息主体也实际上不会进入社区中继.</li>\n<li>管理员是人科人族人属的真正的人, 也要生活学习和工作.</li>\n<li>管理员虽然认同 ACG 文化但没有变成 &quot;二次元萌萌人&quot;, 使用 <code>.moe</code> 作为域名也是恰好得到了域名, 也没有成为 &quot;少数群体&quot; 和 &quot;小众爱好者&quot;, 更没有明显的甚至极左或极右的政治立场.</li>\n<li>管理员是生理男性, 现在也还是.</li>\n</ol>\n","content_text":"本文已经合并至: join.nostr.moe 前言 如果你还不知道什么是 Nostr, 或者你已经使用过了 Fediverse 相关应用, 那么可以尝试阅读以下文章深入了解这两者的区别和优缺点: 为什么我不推荐 Nostr 和 Fediverse? | CXPLAY World 简而言之, Nostr 是相比联邦制为基础的 Fediverse 而言去中心化更加彻底的协议, 联邦制的社交网络也包括了后来新秀 Bluesky. 但是, 虽然两年过去了, 协议的社交生态已经接近完善, 这里还是有不得不对想要尝试的读者说的一些「丑话」, 好节约部分读者的时间: Nostr 协议和协议生态受到比特币资金的直接资助, 内循环经济和总体共识也是比特币为首. Nostr 是个真正的分布式去中心化社交网络, 相比中心化和联邦式社交网络, 你已经发布的内容的传播方式并不完全受你控制, 包括删除操作. 密钥对的使用难度仅仅比 GPG 稍低一些, 并且 Nostr 不存在现行广泛应用的密钥吊销和子密钥协定, 需要你有良好的密钥管理和安全习惯. Nostr 协议的各个实现议定(NIP)不使用开源协议, 而是默认直接置于公有领域. 本指南均在桌面环境下进行指导. 创建密钥对 (账户) 在 Nostr 中, &quot;账户&quot; 的概念已经被为公钥和私钥取代, 如果你使用过 GPG, 那么就不难理解其背后的用意, Nostr 就是使用密钥对进行签署和加密的通讯协议, 不过 Nostr 使用的算法是比特币在内的加密货币使用的算法 secp256k1. 如果你没有接触过非对称加密, 类比一下既是: 公钥等于唯一账户名, 私钥等于账户密码, 而这里的账户名和密码永远唯一且无法被修改. 确保你使用的设备是安全的. 使用浏览器隐私模式打开网页工具: https://nak.nostr.com/ 如果有更高的安全要求, 建议使用一次性的离线虚拟机将工具链导入后离线操作, 直到操作到加密私钥导出. 点击 &quot;generate keypair&quot; 生成一对密钥: 在 (2) 标记框中所显示的内容为: private key (hex): 十六进制的私钥, 通常用于机器内运算使用. nsec: 编码后的私钥, 具有明显的 nsec 头部便于人类识别. public key (hex): 十六进制的公钥, 通常用于机器内运算使用. npub: 编码后公钥, 具有明显的 npub 头部便于人类识别. nprofile: 另一种编码后的公钥, 主要用于指向用户的资料, 所以使用了 nprofile 为头部. 这五部分中, 只需要将 nsec 作为密码, npub 作为账户名保存在密码管理器中即可. 但要注意, nsec 永远无法被修改, 一旦泄露将永远丢失账户的掌控权, 请将其作为比密码还要重要的信息保存起来. 保存完毕后关闭隐私标签页, 用剪贴板传递 nsec 之后注意清空剪贴板记录. 安装密钥管理器 Chrome 或类 Chromium: nos2x - Chrome 应用商店 Firefox: nos2x-fox – Get this Extension for 🦊 Firefox (en-US) 本文使用类 Chromium 浏览器的 nos2x 作为演示. 右键 nos2x 的图标打开扩展选项: 在 private key 下的输入框输入你刚刚生成的 nsec 然后保存 点击 show key encrypted 按钮: 在出现的 password 输入框输入一个只有你脑子里记住且永远不会记在其他地方包括密码管理器的密码作为 PIN, 支持数字字母, 建议至少四位及以上. 输入完成后点击 encrypt and show key, 显示被对称加密后的私钥: 此时被显示出来的以 ncryptsec 开头出现在 private key 输入框中和下方二维码中的私钥既是使用你输入的密码加密后的私钥, 将这个 ncryptsec 作为一个新的密码保存在密码管理器中即可. 此时建议将 nsec 单独保存在一个独立的密码库中, 之后如果要传递私钥, 尽量让这个 ncryptsec 作为包装后的私钥进行传递. 此后需要记住, 永远不要将私钥包括包装后的私钥交给你无法完全确认风险的地方, 永远优先使用可信的密钥管理器来管理密钥. 创建用户资料 密钥对如果没有任何事件与之关联则没有任何价值, 所以现在需要为这对密钥创建一个 Nostr 网络中的用户资料. 不建议使用 nos2x 中提示的那个外部资料编辑器. 打开独立的用户资料管理器: https://metadata.nostr.com/ 点击 Load My Profile 弹出密钥管理器授权请求: 这个网站会请求 read your public key 权限, 也就是 &quot;读取你的公钥&quot; 权限, 用于查找你的用户资料, 点击 authorize just this 授权这次读取. 此时网站会进行查找, 会提示 No Profile Events Found, 这是意料之中的, 因为我们刚刚创建了一个全新的密钥对. 点击上方导航栏的 Metadata 进入用户资料编辑器: 填写用户资料表单: name: 你想要的任何名字, 这会显示在你今后的用户资料上, 建议填写. nip05: 域名身份标签, 这里暂时不填写. about: 关于你的描述, 建议填写. picture: 你的头像, 需要输入一张图片的外链, 建议填写. (实在找不到, 可以先去 nostr.build 随便右键复制一张图片的链接过来先用着) banner: 资料页中显示的横幅背景, 暂时先不填写. lud06 (LNURL): 闪电网络的收款 URL, 已经是过时的字段了, 不填写. lud16: 闪电网络的收款地址, 暂时先不填写. 所有的字段都是可选的, 也可以随时修改. 填写完成后点击 Save 保存你的用户资料. 此时密钥 nos2x 会弹出授权提示签署你的第一个事件: 点击 authorize just this 允许本次签署. 注意, 此时这份用户资料仅仅只是签署了之后保存在了网站缓存里面, 还没有真正传递到 Nostr 网络上, 请继续完成下面的中继配置设置. 配置中继 同样是 metadata.nostr.com, 签署你的用户资料后能够预览到一部分资料. 此时点击导航栏的 Relays 进入中继配置: 不用管 read 和 write 选项, 直接在输入框中输入以下几个推荐中继, 一个中继输入后点击 Add 再添加一行: wss://relay.damus.io/ wss://relay.nostr.band/ wss://nostr-relay.app/ wss://nos.lol/ wss://purplerelay.com/ 然后点击 Save, 保存配置, 网站会请求密钥管理器 nos2x 签署事件 点击 authorize just this 允许本次签署, 向这些中继发布中继配置元数据. 此时建议重复上一节的用户资料操作, 再一次完善你的用户资料后发布到这些你设置的中继中, 接下来开始正式进入 Nostr 网络了. 使用客户端 Nostr 有非常非常多的客户端, 这里将从最基础的社交媒体客户端开始. 打开 Nostr.moe 社区客户端: https://nostr.moe/ 点击左下角的 &quot;登录&quot; 按钮, 弹出登录弹窗, 选择 &quot;浏览器插件登录&quot;: 此时会 nos2x 弹出 read your public key 授权请求弹窗, 点击 authorize just this 允许本次授权, 如果你以后想要自动授权使用公钥登录, 那么就点击 authorize forever. 任何人都可以读取你的公钥, 读取公钥也不会损害账户. 授权完成后你就成功登录到 Nostr.moe 社区了, 左下角不出意外会显示出你的头像和用户名: 点击一下就可以弹出二级窗口, 选择 &quot;个人资料&quot; 就可以看到你设置的所有资料了: 发布帖子 还是在 Nostr.moe 社区, 点击左侧边栏的 &quot;发布笔记&quot; 开始编辑你的第一条正式帖子: 随便写点什么, 可以传图片上去: Nostr 发图片的方式就是插入图片链接. 点击 &quot;发布笔记&quot; 请求 nos2x 授权签署: 点击 authorize just this 允许本次授权, 如果你以后想要在 Nostr.moe 自动授权签署这类笔记的发布, 那么就点击 authorize forever. 如果你收到了神秘事件(你还不知道的授权类型)的授权请求, 请首选考虑拒绝(reject), 如果多次来骚扰你, 直接点击 reject *** forever 即可. 发布之后, 右下角会提示你已经成功发布, 但你会发现, 笔记还是没有出现在你的视野中: 不要惊恐不要失望, 点击你显示资料页栏目中的 &quot;重新加载笔记&quot; 看看: 笔记出现在了你的资料中, 说明笔记成功发布出去了, 笔记右下角的计数器就表示在哪些中继(服务器)中找到了你的笔记. 尽情探索 欢迎加入 Nostr! 不管你是否打算在此停留多久. 探索原则: 永远不要授权你不知道实际用途的事件的签署请求, 也不要允许客户端签署你不想要的任何事件, 比如突然请求更新你的用户资料等. 永远不要将你的任何形式的私钥暴露给其他任何人. 永远不要在任何客户端中直接使用私钥, 如果它要求你只能输入私钥使用, 请放弃这个客户端. 你可能需要像二次确认用户资料一样, 你还需要确认一遍你的中继配置是否完整: 不出意料的话, 这里很可能是空的: 所以你需要将配置中继小节的那些中继再次输入到客户端进行 &quot;添加&quot;. 这样才能确保你的基础连接是没有问题的, 除了 &quot;探索原则&quot;, 这里还有几条重要提示, 是由于分布式网络带来的额外特性: 你的用户资料(kind:0)和中继配置(kind:10002)是最重要的账户元数据, 需要看好它们, 并让它们尽可能地传播地越广越好. Nostr 作为社交网络运行时, 无法做到 &quot;私密账户&quot; 或 &quot;带锁账户&quot; 这种东西, 你的消息内容没有准备好为每个已知的受众进行不对称加密, 公共消息永远无法做到权限控制. Nostr 被签署并广播的消息不存在 &quot;永远删除&quot;, 一旦被签署并广播, 消息就无法被完全撤回. Nostr 的删除更像是声明为 &quot;无效&quot;, 当然也只有消息公钥对应的私钥才能签署声明这种 &quot;无效&quot; 状态. Nostr 的关注始终是单向同意的, 所以被关注者永远无法获知自己确切的粉丝数量. Nostr 帖子的发布者无法主动审查帖子关联线程中的消息, 包括作者在内的所有人都只能选择隐藏. 加入 Nostr.moe 社区 Nostr.moe 是笔者托管的一个中继器和客户端组成的小型社区, 中继器用于存储受邀用户的消息, 客户端实际上只是充当过滤器的消息视图. 这种社区状态类似于 Torrent 索引, 而非固定的中心化及联邦式社交中的单一实例. 得益于 Nostr 分布式的优势, 用户可以随意切换客户端和服务器也不用担心和自己熟悉的人失去联系, 也不会因为客户端和服务器被关闭而无法再参与网络和丢失自己的消息. Nostr.moe 旨在聚集 Nostr 网络中的 ACG 爱好者, 即 Anime(动漫), Comic(漫画), Game(游戏), 现在一般还会带上单独的 Novel(小说). 这是种维持社区氛围的共识, 而非单一的 &quot;二次元&quot; 限定的话题, 是以小众爱好维持大众社交状态的一个意向. 加入社区, 你可以获得: wss://relay.nostr.moe/ 中继的写入权限, 使消息出现在社区默认视图中. example@nostr.moe 格式的 NIP-05 域名标签. 加入社区, 你需要同意或认同以下规则: 社区管理员, 主要客户端开发者, 服务端中继开发者包含中国人或华裔. 认同 ACG 或 ACGN 文化. 这是公开社区, 是网络上的公共场所, 只是一群人相对坐得更近了一点而已. 因此不能在社区中做公共场合被认为是不体面的事情(比如宣扬仇恨言论, 民族主义, 言语骚扰, 人身攻击, 以及挑拨离间在内的特别是键盘政治等你不想承担舆情引导和言论责任的事情等), 因此内容的受众年龄等级也被限定在全年龄. 玩梗玩抽象请适度, 钓鱼反串的风险会更高. 社区支持言论自由, 协议也天然具有抗审查特性, 但社区不提供匿名表达和言论责任规避服务. 遵守你的籍贯国和人身所处地的法律. 遵守我们的基础设施供应商和域名注册局的服务条款. 你需要作为人类来发布人造的消息内容, 而不是依靠自动化或 AIGC 来阻塞时间线. 你所转发, 引用和回复的消息也会进入社区消息视图之中, 所以这类消息依然受到规则制约. 不过目前点赞和表情回应不包含在社区消息视图中, 但管理员建议: 做敏感事情的时候应该去使用其他不在社区成员名单中的密钥对, 更不要让两对密钥对的消息发生关联. 申请加入社区的方式: 向常规客户端的开源仓库目录中的 .well-known 目录发起 PR 请求, 将你的十六进制公钥按照格式添加到 nostr.json 文件中. https://github.com/cxplay/jumble-pub/blob/nostr.moe/public/.well-known/nostr.json 联系到管理员, 发送公钥和域名标签用户名申请加入. 审核流程: 如果你不是管理员在网络上的 &quot;熟人&quot;, 那么你需要发送自己的网络主页(博客或其他)证明自己不是追求匿名而选择 Nostr.moe 的「神秘人」或「隐私怪」, 又或者是为了开小号而来的人. 请确保管理员能够认定你能够符合社区规则, 否则审核的第一步就会举步维艰. 由于客户端和服务端都不会也无法设置不透明的基于消息内容的审查措施(如关键词), 因此在违反规则不听劝阻的情况下, 管理员为了维护社区秩序只能撤回社区成员的对社区中继的写权限, 并在社区中继中移除关联公钥的所有消息记录, 还有与之关联的域名标签. 当权限添加完毕后对应的公钥会被管理员账户提及, 这表示你已经成功加入社区, 获得了社区中继的写入权限和域名标签认证. 其他说明: 社区成员的 NIP-05 域名标签主要用作公钥目录使用, 并不强制要求社区成员必须在自己的用户资料中填写它. 如果你不喜欢或者你有自己的想用的标签, 不管你有没有加入社区, 都可以随意使用. 社区中继的写权限是与消息 JSON 中的公钥对应, 其他引用社区成员消息的消息由于消息中的公钥不对应, 无法进入社区中继, 同样的成员消息中引用的其他非成员的消息主体也实际上不会进入社区中继. 管理员是人科人族人属的真正的人, 也要生活学习和工作. 管理员虽然认同 ACG 文化但没有变成 &quot;二次元萌萌人&quot;, 使用 .moe 作为域名也是恰好得到了域名, 也没有成为 &quot;少数群体&quot; 和 &quot;小众爱好者&quot;, 更没有明显的甚至极左或极右的政治立场. 管理员是生理男性, 现在也还是.","summary":"本文已经合并至: join.nostr.moe 前言 如果你还不知道什么是 Nostr, 或者你已经使用过了 Fediverse 相关应用, 那么可以尝试阅读以下文章深入了解这两者的区别和优缺点: 为什么我不推荐 Nostr 和 Fediverse? | CXPLAY World 简而言之, Nostr 是相比联邦制为基础的 Fediverse 而言去中心化更加彻底的协议, 联邦制的社交网络也包括了后来新秀 Bluesky. 但是, 虽然两年过去了, 协议的社交生态已经接近完善, 这里还是有不得不对想要尝试的读者说的一些「丑话」, 好节约部分读者的时间: Nostr 协议和协议生态受到比特币资金的直接资助, 内循环经济和总体共识也是比特币为首. Nostr 是个真正的分布式去中心化社交网络, 相比中心化和联邦式社交网络, 你已经发布的内容的传播方式并不完全受你控制, 包括删除操作. 密钥对的使用难度仅仅比 GPG 稍低一些, 并且 Nostr 不存在现行广泛应用的密钥吊销和子密钥协定, 需要你有良好的密钥管理和安全习惯. Nostr 协议的各个实现议定(NIP)不使用开源协议, 而是默认直接置于公有领域. 本指南均在桌面环境下进行指导. 创建密钥对 (账户) 在 Nostr 中, &quot;账户&quot; 的概念已经被为公钥和私钥取代, 如果你使用过 GPG, 那么就不难理解其背后的用意, Nostr 就是使用密钥对进行签署和加密的通讯协议, 不过 Nostr 使用的算法是比特币在内的加密货币使用的算法 secp256k1. 如果你没有接触过非对称加密, 类比一下既是: 公钥等于唯一账户名, 私钥等于账户密码, 而这里的账户名和密码永远唯一且无法被修改. 确保你使用的设备是安全的. 使用浏览器隐私模式打开网页工具: https://nak.nostr.com/ 如果有更高的安全要求, 建议使用一次性的离线虚拟机将工具链导入后离线操作, 直到操作到加密私钥导出. 点击 &quot;generate keypair&quot; 生成一对密钥: 在 (2) 标记框中所显示的内容为: private key (hex): 十六进制的私钥, 通常用于机器内运算使用. nsec: 编码后的私钥, 具有明显的 nsec 头部便于人类识别. public key (hex): 十六进制的公钥, 通常用于机器内运算使用. npub: 编码后公钥, 具有明显的 npub 头部便于人类识别. nprofile: 另一种编码后的公钥, 主要用于指向用户的资料, 所以使用了 nprofile 为头部. 这五部分中, 只需要将 nsec 作为密码, npub 作为账户名保存在密码管理器中即可. 但要注意, nsec 永远无法被修改, 一旦泄露将永远丢失账户的掌控权, 请将其作为比密码还要重要的信息保存起来. 保存完毕后关闭隐私标签页, 用剪贴板传递 nsec 之后注意清空剪贴板记录. 安装密钥管理器 Chrome 或类 Chromium: nos2x - Chrome 应用商店 Firefox: nos2x-fox – Get this Extension for 🦊 Firefox (en-US) 本文使用类 Chromium 浏览器的 nos2x 作为演示. 右键 nos2x 的图标打开扩展选项: 在 private key 下的输入框输入你刚刚生成的 nsec 然后保存 点击 show key encrypted 按钮: 在出现的 password 输入框输入一个只有你脑子里记住且永远不会记在其他地方包括密码管理器的密码作为 PIN, 支持数字字母, 建议至少四位及以上. 输入完成后点击 encrypt and show key, 显示被对称加密后的私钥: 此时被显示出来的以 ncryptsec 开头出现在 private key 输入框中和下方二维码中的私钥既是使用你输入的密码加密后的私钥, 将这个 ncryptsec 作为一个新的密码保存在密码管理器中即可. 此时建议将 nsec 单独保存在一个独立的密码库中, 之后如果要传递私钥, 尽量让这个 ncryptsec 作为包装后的私钥进行传递. 此后需要记住, 永远不要将私钥包括包装后的私钥交给你无法完全确认风险的地方, 永远优先使用可信的密钥管理器来管理密钥. 创建用户资料 密钥对如果没有任何事件与之关联则没有任何价值, 所以现在需要为这对密钥创建一个 Nostr 网络中的用户资料. 不建议使用 nos2x 中提示的那个外部资料编辑器. 打开独立的用户资料管理器: https://metadata.nostr.com/ 点击 Load My Profile 弹出密钥管理器授权请求: 这个网站会请求 read your public key 权限, 也就是 &quot;读取你的公钥&quot; 权限, 用于查找你的用户资料, 点击 authorize just this 授权这次读取. 此时网站会进行查找, 会提示 No Profile Events Found, 这是意料之中的, 因为我们刚刚创建了一个全新的密钥对. 点击上方导航栏的 Metadata 进入用户资料编辑器: 填写用户资料表单: name: 你想要的任何名字, 这会显示在你今后的用户资料上, 建议填写. nip05: 域名身份标签, 这里暂时不填写. about: 关于你的描述, 建议填写. picture: 你的头像, 需要输入一张图片的外链, 建议填写. (实在找不到, 可以先去 nostr.build 随便右键复制一张图片的链接过来先用着) banner: 资料页中显示的横幅背景, 暂时先不填写. lud06 (LNURL): 闪电网络的收款 URL, 已经是过时的字段了, 不填写. lud16: 闪电网络的收款地址, 暂时先不填写. 所有的字段都是可选的, 也可以随时修改. 填写完成后点击 Save 保存你的用户资料. 此时密钥 nos2x 会弹出授权提示签署你的第一个事件: 点击 authorize just this 允许本次签署. 注意, 此时这份用户资料仅仅只是签署了之后保存在了网站缓存里面, 还没有真正传递到 Nostr 网络上, 请继续完成下面的中继配置设置. 配置中继 同样是 metadata.nostr.com, 签署你的用户资料后能够预览到一部分资料. 此时点击导航栏的 Relays 进入中继配置: 不用管 read 和 write 选项, 直接在输入框中输入以下几个推荐中继, 一个中继输入后点击 Add 再添加一行: wss://relay.damus.io/ wss://relay.nostr.band/ wss://nostr-relay.app/ wss://nos.lol/ wss://purplerelay.com/ 然后点击 Save, 保存配置, 网站会请求密钥管理器 nos2x 签署事件 点击 authorize just this 允许本次签署, 向这些中继发布中继配置元数据. 此时建议重复上一节的用户资料操作, 再一次完善你的用户资料后发布到这些你设置的中继中, 接下来开始正式进入 Nostr 网络了. 使用客户端 Nostr 有非常非常多的客户端, 这里将从最基础的社交媒体客户端开始. 打开 Nostr.moe 社区客户端: https://nostr.moe/ 点击左下角的 &quot;登录&quot; 按钮, 弹出登录弹窗, 选择 &quot;浏览器插件登录&quot;: 此时会 nos2x 弹出 read your public key 授权请求弹窗, 点击 authorize just this 允许本次授权, 如果你以后想要自动授权使用公钥登录, 那么就点击 authorize forever. 任何人都可以读取你的公钥, 读取公钥也不会损害账户. 授权完成后你就成功登录到 Nostr.moe 社区了, 左下角不出意外会显示出你的头像和用户名: 点击一下就可以弹出二级窗口, 选择 &quot;个人资料&quot; 就可以看到你设置的所有资料了: 发布帖子 还是在 Nostr.moe 社区, 点击左侧边栏的 &quot;发布笔记&quot; 开始编辑你的第一条正式帖子: 随便写点什么, 可以传图片上去: Nostr 发图片的方式就是插入图片链接. 点击 &quot;发布笔记&quot; 请求 nos2x 授权签署: 点击 authorize just this 允许本次授权, 如果你以后想要在 Nostr.moe 自动授权签署这类笔记的发布, 那么就点击 authorize forever. 如果你收到了神秘事件(你还不知道的授权类型)的授权请求, 请首选考虑拒绝(reject), 如果多次来骚扰你, 直接点击 reject *** forever 即可. 发布之后, 右下角会提示你已经成功发布, 但你会发现, 笔记还是没有出现在你的视野中: 不要惊恐不要失望, 点击你显示资料页栏目中的 &quot;重新加载笔记&quot; 看看: 笔记出现在了你的资料中, 说明笔记成功发布出去了, 笔记右下角的计数器就表示在哪些中继(服务器)中找到了你的笔记. 尽情探索 欢迎加入 Nostr! 不管你是否打算在此停留多久. 探索原则: 永远不要授权你不知道实际用途的事件的签署请求, 也不要允许客户端签署你不想要的任何事件, 比如突然请求更新你的用户资料等. 永远不要将你的任何形式的私钥暴露给其他任何人. 永远不要在任何客户端中直接使用私钥, 如果它要求你只能输入私钥使用, 请放弃这个客户端. 你可能需要像二次确认用户资料一样, 你还需要确认一遍你的中继配置是否完整: 不出意料的话, 这里很可能是空的: 所以你需要将配置中继小节的那些中继再次输入到客户端进行 &quot;添加&quot;. 这样才能确保你的基础连接是没有问题的, 除了 &quot;探索原则&quot;, 这里还有几条重要提示, 是由于分布式网络带来的额外特性: 你的用户资料(kind:0)和中继配置(kind:10002)是最重要的账户元数据, 需要看好它们, 并让它们尽可能地传播地越广越好. Nostr 作为社交网络运行时, 无法做到 &quot;私密账户&quot; 或 &quot;带锁账户&quot; 这种东西, 你的消息内容没有准备好为每个已知的受众进行不对称加密, 公共消息永远无法做到权限控制. Nostr 被签署并广播的消息不存在 &quot;永远删除&quot;, 一旦被签署并广播, 消息就无法被完全撤回. Nostr 的删除更像是声明为 &quot;无效&quot;, 当然也只有消息公钥对应的私钥才能签署声明这种 &quot;无效&quot; 状态. Nostr 的关注始终是单向同意的, 所以被关注者永远无法获知自己确切的粉丝数量. Nostr 帖子的发布者无法主动审查帖子关联线程中的消息, 包括作者在内的所有人都只能选择隐藏. 加入 Nostr.moe 社区 Nostr.moe 是笔者托管的一个中继器和客户端组成的小型社区, 中继器用于存储受邀用户的消息, 客户端实际上只是充当过滤器的消息视图. 这种社区状态类似于 Torrent 索引, 而非固定的中心化及联邦式社交中的单一实例. 得益于 Nostr 分布式的优势, 用户可以随意切换客户端和服务器也不用担心和自己熟悉的人失去联系, 也不会因为客户端和服务器被关闭而无法再参与网络和丢失自己的消息. Nostr.moe 旨在聚集 Nostr 网络中的 ACG 爱好者, 即 Anime(动漫), Comic(漫画), Game(游戏), 现在一般还会带上单独的 Novel(小说). 这是种维持社区氛围的共识, 而非单一的 &quot;二次元&quot; 限定的话题, 是以小众爱好维持大众社交状态的一个意向. 加入社区, 你可以获得: wss://relay.nostr.moe/ 中继的写入权限, 使消息出现在社区默认视图中. example@nostr.moe 格式的 NIP-05 域名标签. 加入社区, 你需要同意或认同以下规则: 社区管理员, 主要客户端开发者, 服务端中继开发者包含中国人或华裔. 认同 ACG 或 ACGN 文化. 这是公开社区, 是网络上的公共场所, 只是一群人相对坐得更近了一点而已. 因此不能在社区中做公共场合被认为是不体面的事情(比如宣扬仇恨言论, 民族主义, 言语骚扰, 人身攻击, 以及挑拨离间在内的特别是键盘政治等你不想承担舆情引导和言论责任的事情等), 因此内容的受众年龄等级也被限定在全年龄. 玩梗玩抽象请适度, 钓鱼反串的风险会更高. 社区支持言论自由, 协议也天然具有抗审查特性, 但社区不提供匿名表达和言论责任规避服务. 遵守你的籍贯国和人身所处地的法律. 遵守我们的基础设施供应商和域名注册局的服务条款. 你需要作为人类来发布人造的消息内容, 而不是依靠自动化或 AIGC 来阻塞时间线. 你所转发, 引用和回复的消息也会进入社区消息视图之中, 所以这类消息依然受到规则制约. 不过目前点赞和表情回应不包含在社区消息视图中, 但管理员建议: 做敏感事情的时候应该去使用其他不在社区成员名单中的密钥对, 更不要让两对密钥对的消息发生关联. 申请加入社区的方式: 向常规客户端的开源仓库目录中的 .well-known 目录发起 PR 请求, 将你的十六进制公钥按照格式添加到 nostr.json 文件中. https://github.com/cxplay/jumble-pub/blob/nostr.moe/public/.well-known/nostr.json 联系到管理员, 发送公钥和域名标签用户名申请加入. 审核流程: 如果你不是管理员在网络上的 &quot;熟人&quot;, 那么你需要发送自己的网络主页(博客或其他)证明自己不是追求匿名而选择 Nostr.moe 的「神秘人」或「隐私怪」, 又或者是为了开小号而来的人. 请确保管理员能够认定你能够符合社区规则, 否则审核的第一步就会举步维艰. 由于客户端和服务端都不会也无法设置不透明的基于消息内容的审查措施(如关键词), 因此在违反规则不听劝阻的情况下, 管理员为了维护社区秩序只能撤回社区成员的对社区中继的写权限, 并在社区中继中移除关联公钥的所有消息记录, 还有与之关联的域名标签. 当权限添加完毕后对应的公钥会被管理员账户提及, 这表示你已经成功加入社区, 获得了社区中继的写入权限和域名标签认证. 其他说明: 社区成员的 NIP-05 域名标签主要用作公钥目录使用, 并不强制要求社区成员必须在自己的用户资料中填写它. 如果你不喜欢或者你有自己的想用的标签, 不管你有没有加入社区, 都可以随意使用. 社区中继的写权限是与消息 JSON 中的公钥对应, 其他引用社区成员消息的消息由于消息中的公钥不对应, 无法进入社区中继, 同样的成员消息中引用的其他非成员的消息主体也实际上不会进入社区中继. 管理员是人科人族人属的真正的人, 也要生活学习和工作. 管理员虽然认同 ACG 文化但没有变成 &quot;二次元萌萌人&quot;, 使用 .moe 作为域名也是恰好得到了域名, 也没有成为 &quot;少数群体&quot; 和 &quot;小众爱好者&quot;, 更没有明显的甚至极左或极右的政治立场. 管理员是生理男性, 现在也还是.","date_published":"2025-04-04T13:18:00.000Z","tags":["授言册","Nostr","社交媒体"]},{"id":"https://blog.cxplay.org/works/how-to-choose-browser-and-ad-blocker-in-the-mv3-era/","url":"https://blog.cxplay.org/works/how-to-choose-browser-and-ad-blocker-in-the-mv3-era/","title":"MV3 时代下的浏览器和广告拦截器选择路线图","content_html":"<h2 id=\"图表\"><a href=\"#图表\"></a>图表</h2>\n<p><img src=\"https://i.cxplay.org/u/1/blog/how-to-choose-browser-and-ad-blocker-in-the-mv3-era/browser-and-ad-blocker-in-the-mv3-era-v1.webp\" alt=\"路线图\" loading='lazy'></p>\n<blockquote>\n<p><strong>原图:</strong> <a href=\"https://i.cxplay.org/u/1/blog/how-to-choose-browser-and-ad-blocker-in-the-mv3-era/browser-and-ad-blocker-in-the-mv3-era-v1.webp\">browser-and-ad-blocker-in-the-mv3-era-v1.webp</a></p>\n</blockquote>\n<p>包含:</p>\n<ol>\n<li>AdGuard Desktop</li>\n<li>Cromite</li>\n<li>猫眼浏览器 (Catsxp)</li>\n<li>Vivaldi Browser</li>\n<li>Microsoft Edge</li>\n<li>Brave Browser</li>\n<li>ungoogled-chromium</li>\n<li>Firefox</li>\n<li>Floorp</li>\n<li>Zen Browser</li>\n<li>LibreWolf</li>\n</ol>\n<h2 id=\"说明\"><a href=\"#说明\"></a>说明</h2>\n<p>对于所有类 Chromium 浏览器, 没有任何一个能够保证在脱离 Google 的情况下维护 Chromium 的 MV2 底层代码库, 即使是 Microsoft 也没有这种打算.</p>\n<p>一般来说, 只要不是 Google 主动在 Chromium 代码中移除 MV2 代码或者做出破坏性更新, 那么其他非 Google 的浏览器皮肤/UI/包装/修补/设计公司或团队出品的类 Chromium 也没有过多理由和 Google 站在激进的 MV3 执行策略战线上. 但同样, 不同的类 Chromium 浏览器在 MV3 时代的 MV2 支持策略支持上也并不都是相同的, 因为 Google 的策略已经进入到 Chromium 主线中, 如果不主动干预那么还是会直接继承 Google 的意图.</p>\n<p>图表可能会发生更新, 请以本文内的最新图表为准.</p>\n","content_text":"图表 原图: browser-and-ad-blocker-in-the-mv3-era-v1.webp 包含: AdGuard Desktop Cromite 猫眼浏览器 (Catsxp) Vivaldi Browser Microsoft Edge Brave Browser ungoogled-chromium Firefox Floorp Zen Browser LibreWolf 说明 对于所有类 Chromium 浏览器, 没有任何一个能够保证在脱离 Google 的情况下维护 Chromium 的 MV2 底层代码库, 即使是 Microsoft 也没有这种打算. 一般来说, 只要不是 Google 主动在 Chromium 代码中移除 MV2 代码或者做出破坏性更新, 那么其他非 Google 的浏览器皮肤/UI/包装/修补/设计公司或团队出品的类 Chromium 也没有过多理由和 Google 站在激进的 MV3 执行策略战线上. 但同样, 不同的类 Chromium 浏览器在 MV3 时代的 MV2 支持策略支持上也并不都是相同的, 因为 Google 的策略已经进入到 Chromium 主线中, 如果不主动干预那么还是会直接继承 Google 的意图. 图表可能会发生更新, 请以本文内的最新图表为准.","summary":"图表 原图: browser-and-ad-blocker-in-the-mv3-era-v1.webp 包含: AdGuard Desktop Cromite 猫眼浏览器 (Catsxp) Vivaldi Browser Microsoft Edge Brave Browser ungoogled-chromium Firefox Floorp Zen Browser LibreWolf 说明 对于所有类 Chromium 浏览器, 没有任何一个能够保证在脱离 Google 的情况下维护 Chromium 的 MV2 底层代码库, 即使是 Microsoft 也没有这种打算. 一般来说, 只要不是 Google 主动在 Chromium 代码中移除 MV2 代码或者做出破坏性更新, 那么其他非 Google 的浏览器皮肤/UI/包装/修补/设计公司或团队出品的类 Chromium 也没有过多理由和 Google 站在激进的 MV3 执行策略战线上. 但同样, 不同的类 Chromium 浏览器在 MV3 时代的 MV2 支持策略支持上也并不都是相同的, 因为 Google 的策略已经进入到 Chromium 主线中, 如果不主动干预那么还是会直接继承 Google 的意图. 图表可能会发生更新, 请以本文内的最新图表为准.","date_published":"2025-03-23T17:51:10.000Z","tags":["资料库"]},{"id":"https://blog.cxplay.org/works/avalanche/","url":"https://blog.cxplay.org/works/avalanche/","title":"雪崩","content_html":"<h2 id=\"序\"><a href=\"#序\"></a>序</h2>\n<p>天色没有好转, 还是阴沉沉的一片, 灰暗的天空看不到云之间的空隙, 好像只是绵密的浓雾.</p>\n<p>雨滴从浓雾中落下, 从液态瞬间变为固态, 来不及和其他的水滴一起停留, 变成了随风飘荡的雪花落下来. 这里的风总是会在雪花落下时吹起, 雨滴从来不会如愿落到它们想要到达的地方.</p>\n<p>有的雨滴在抱怨风让它们变成了雪, 有的雨滴在抱怨浓雾把它们变成了雪, 还有的雨滴在抱怨已经在地面的雪让它们也不得不变成雪. 它们不情愿地积聚在一起, 无论如何, 它们都想要再次飞舞, 或再次变成完美的雪花, 或变成晶莹剔透的水滴, 或变成一粒尘埃.</p>\n<h2 id=\"正文\"><a href=\"#正文\"></a>正文</h2>\n<p>从浓雾中睁开眼, 灰暗的周围到处都是同伴, 但似乎被困在了一个透明的外壳里面. 有东西在不停地推动着我到处飘动, 同伴们飞快地擦肩而过, 能够听见有有人在笑也有人在哭, 这里奇怪的背景音都是这些声音在快速地移动中破碎形成的, 和这些浓厚的雾搭配起来就能让人感到烦躁.</p>\n<p>&quot;离开...&quot;, &quot;离开...这...&quot;, &quot;出... 离开...&quot;, &quot;在... 这里...&quot;, &quot;走... 不....&quot;</p>\n<p>灰暗背景中的噪音在脑中和思维交织, 变成了断断续续地呢喃之声. 是的, 一定是, 要走, 一定要离开这里.</p>\n<p>从浓雾中向下, 冰晶开始在周围生长, 我的伙伴们也长出了千奇百怪的形状, 多么美丽的外表, 值得目光停留好一阵子, 不过已经停留不了多久了, 我们正在止不住地向下坠落.</p>\n<p>这是第一次从浓雾中离开, 千千万万的伙伴都有不同的外观, 在难得一见的光线下折射出多么美妙的光影, 令人陶醉. 再也没有东西推着我们前进, 我们只需要轻轻地向下坠落, 旋转着, 或者是躺在风里. 即使是狂风也忍不住赞美我们的美丽, 是否有那么一瞬间的念头出现过, 为了这一些的美好应该就此停留? 但这是做不到的, 冰晶仍然在生长, 越来越重的外壳无法让这一切停留.</p>\n<p>在空中的时候, 雪花似乎在兴奋地讨论这下面的光景, 下面是多么热闹, 多么奇异, 那么多的同伴们在那里一定是更加美丽的地方. 想要去山脊上, 想要去树枝上, 想要去红色的那片悬崖上, 还有的想要钻进斗篷们的兜帽里. 幻想过无数种千奇百怪的地方, 笑着完成了最后的那一层外壳的生长, 终于能够如愿到达了这里.</p>\n<p>但没有一片雪花落到了自己想要的地方, 至少身边的同伴都不见了. 在这片光洁的雪原上, 风并不如雾中那么猛烈, 也没有那么吵闹, 这里实在是安静地太不正常了. 雪花在呼喊同伴, 但即使身边全部都是同类, 但没有一片雪花回应它, 所有的雪花都以千奇百怪的方式停在其他雪花的身上, 旋转着, 或是躺在雪里. 上面依旧有雪花落下, 也有的直接压在自己身上, 呼唤就在眼前的雪花也没有任何回应, 就像是来自两个世界的水滴变成的雪花一样.</p>\n<p>越来越多的雪花堆积起来, 已经看不到外面的光景了. 稳定下来后, 能感受到身上的同伴越来越多了, 明显的压迫感越来越强, 但依旧没有任何回应, 就像都是虚假的雪花一样, 不知是从什么地方批量制造出来的, 反正不是雾里的, 雪花如此笃定的认为.</p>\n<p>半夜, 也许是半夜, 因为已经不知道过去了多久, 也看不到一丝光线. 静悄悄的周围突然有了奇怪的声音, 这不是雪花能够发出的声音, 就像两颗水滴被风推着相撞发出的爆裂声, 身上的假雪花也在慢慢地移动, 还以为是它们复活了, 结果发现是有东西在推着它们移动. 奇怪的声音原来是来自雪花破碎的声音, 厚重的冰晶外壳挤压破碎发出了爆裂声, 雪花正在失去曾经引以为傲的美丽外表, 同伴也在互相碰撞中变成了丑陋的形状, 互相粘附在一起, 完全不是雪花该有的样子.</p>\n<p>终于, 持续不断的爆裂声被一个更大的声音盖过了. 这是一个明显不同的声音, 不可能是碎裂的冰晶能发出的, 雪花想起了浓雾中的狂风相撞的声音. 身上的丑陋的假雪花开始更加快速地移动, 被带着也连翻了无数个身, 身下的同伴和身上的同伴再也分不清, 不知道翻了多少圈, 已经无法判断上和下, 雪花和同伴们也变成了一模一样的丑陋的假雪花. 雪花翻滚着, 随着沉默的同伴一起被推动着往一个方向前进, 直到一瞬间, 雪花听见了有东西在呼唤, 居然是假雪花们, 褪去了厚重外壳的雪花露出了奇怪的核心, 也终于能听到其他雪花的声音, 是厚重的外壳挡住了雪花的声音.</p>\n<p>但都太晚了, 就和浓雾中一样, 雪花们被强大的力量推动着不断翻滚, 根本来不及停留, 破碎的雪花互相摩擦, 撞击, 就和浓雾中的声音一样. 雪花被卷起来, 一瞬间看到了刺眼的阳光但立马就被盖在同伴的底下, 爆裂的声音依旧在持续. 雪花的声音, 雪花破碎的声音和更广泛存在的爆鸣声混杂在一起, 雪原上从来没有如此吵闹过. 听着同伴破碎的声音, 就好像安眠曲一样, 雪花的安眠曲.</p>\n<p>不知翻滚了多久, 也不知道有多少雪花被抛起来和阳光混合在一起之后. 爆鸣声终于停下了, 雪花也变成了曾经只有薄薄一层外壳的样子. 雪花和一些随机的同伴躺在了一块石头上, 阳光正在让最后一层外壳褪去, 雪花就再也不是雪花了, 是各式各样的尘埃, 灰色的, 黑色的, 透明的. 石头上的雪花终于变成了灰尘, 和同伴们粘连在一起依附在这块石头上.</p>\n<p>直到下一阵狂风的到来, 雪花, 或者是尘埃可能还是会选择再次飞舞在空中.</p>\n<p>但是尘埃还能再次变成雪花吗?</p>\n<h2 id=\"后记\"><a href=\"#后记\"></a>后记</h2>\n<p>浓雾总是伴随着雪, 这是这里的斗篷们长久以来得出的经验. 斗篷都知道雾和云是有区别的, 但这里的地势太高, 浓雾和云已经没有太多可区分的特征了, 这里只有雾, 白色的雾, 灰色的雾.</p>\n<p>可以这么说, 斗篷们从来就没见过云, 大多数斗篷聚集的地方终年都下着雪, 也没有斗篷真正向往雾背后的天空, 那里的阳光太刺眼, 比起靠着不稳定的光源, 斗篷们更喜欢需要的时候升起一堆篝火, 即使大多时候要用只剩空壳的斗篷作为燃料.</p>\n","content_text":"序 天色没有好转, 还是阴沉沉的一片, 灰暗的天空看不到云之间的空隙, 好像只是绵密的浓雾. 雨滴从浓雾中落下, 从液态瞬间变为固态, 来不及和其他的水滴一起停留, 变成了随风飘荡的雪花落下来. 这里的风总是会在雪花落下时吹起, 雨滴从来不会如愿落到它们想要到达的地方. 有的雨滴在抱怨风让它们变成了雪, 有的雨滴在抱怨浓雾把它们变成了雪, 还有的雨滴在抱怨已经在地面的雪让它们也不得不变成雪. 它们不情愿地积聚在一起, 无论如何, 它们都想要再次飞舞, 或再次变成完美的雪花, 或变成晶莹剔透的水滴, 或变成一粒尘埃. 正文 从浓雾中睁开眼, 灰暗的周围到处都是同伴, 但似乎被困在了一个透明的外壳里面. 有东西在不停地推动着我到处飘动, 同伴们飞快地擦肩而过, 能够听见有有人在笑也有人在哭, 这里奇怪的背景音都是这些声音在快速地移动中破碎形成的, 和这些浓厚的雾搭配起来就能让人感到烦躁. &quot;离开...&quot;, &quot;离开...这...&quot;, &quot;出... 离开...&quot;, &quot;在... 这里...&quot;, &quot;走... 不....&quot; 灰暗背景中的噪音在脑中和思维交织, 变成了断断续续地呢喃之声. 是的, 一定是, 要走, 一定要离开这里. 从浓雾中向下, 冰晶开始在周围生长, 我的伙伴们也长出了千奇百怪的形状, 多么美丽的外表, 值得目光停留好一阵子, 不过已经停留不了多久了, 我们正在止不住地向下坠落. 这是第一次从浓雾中离开, 千千万万的伙伴都有不同的外观, 在难得一见的光线下折射出多么美妙的光影, 令人陶醉. 再也没有东西推着我们前进, 我们只需要轻轻地向下坠落, 旋转着, 或者是躺在风里. 即使是狂风也忍不住赞美我们的美丽, 是否有那么一瞬间的念头出现过, 为了这一些的美好应该就此停留? 但这是做不到的, 冰晶仍然在生长, 越来越重的外壳无法让这一切停留. 在空中的时候, 雪花似乎在兴奋地讨论这下面的光景, 下面是多么热闹, 多么奇异, 那么多的同伴们在那里一定是更加美丽的地方. 想要去山脊上, 想要去树枝上, 想要去红色的那片悬崖上, 还有的想要钻进斗篷们的兜帽里. 幻想过无数种千奇百怪的地方, 笑着完成了最后的那一层外壳的生长, 终于能够如愿到达了这里. 但没有一片雪花落到了自己想要的地方, 至少身边的同伴都不见了. 在这片光洁的雪原上, 风并不如雾中那么猛烈, 也没有那么吵闹, 这里实在是安静地太不正常了. 雪花在呼喊同伴, 但即使身边全部都是同类, 但没有一片雪花回应它, 所有的雪花都以千奇百怪的方式停在其他雪花的身上, 旋转着, 或是躺在雪里. 上面依旧有雪花落下, 也有的直接压在自己身上, 呼唤就在眼前的雪花也没有任何回应, 就像是来自两个世界的水滴变成的雪花一样. 越来越多的雪花堆积起来, 已经看不到外面的光景了. 稳定下来后, 能感受到身上的同伴越来越多了, 明显的压迫感越来越强, 但依旧没有任何回应, 就像都是虚假的雪花一样, 不知是从什么地方批量制造出来的, 反正不是雾里的, 雪花如此笃定的认为. 半夜, 也许是半夜, 因为已经不知道过去了多久, 也看不到一丝光线. 静悄悄的周围突然有了奇怪的声音, 这不是雪花能够发出的声音, 就像两颗水滴被风推着相撞发出的爆裂声, 身上的假雪花也在慢慢地移动, 还以为是它们复活了, 结果发现是有东西在推着它们移动. 奇怪的声音原来是来自雪花破碎的声音, 厚重的冰晶外壳挤压破碎发出了爆裂声, 雪花正在失去曾经引以为傲的美丽外表, 同伴也在互相碰撞中变成了丑陋的形状, 互相粘附在一起, 完全不是雪花该有的样子. 终于, 持续不断的爆裂声被一个更大的声音盖过了. 这是一个明显不同的声音, 不可能是碎裂的冰晶能发出的, 雪花想起了浓雾中的狂风相撞的声音. 身上的丑陋的假雪花开始更加快速地移动, 被带着也连翻了无数个身, 身下的同伴和身上的同伴再也分不清, 不知道翻了多少圈, 已经无法判断上和下, 雪花和同伴们也变成了一模一样的丑陋的假雪花. 雪花翻滚着, 随着沉默的同伴一起被推动着往一个方向前进, 直到一瞬间, 雪花听见了有东西在呼唤, 居然是假雪花们, 褪去了厚重外壳的雪花露出了奇怪的核心, 也终于能听到其他雪花的声音, 是厚重的外壳挡住了雪花的声音. 但都太晚了, 就和浓雾中一样, 雪花们被强大的力量推动着不断翻滚, 根本来不及停留, 破碎的雪花互相摩擦, 撞击, 就和浓雾中的声音一样. 雪花被卷起来, 一瞬间看到了刺眼的阳光但立马就被盖在同伴的底下, 爆裂的声音依旧在持续. 雪花的声音, 雪花破碎的声音和更广泛存在的爆鸣声混杂在一起, 雪原上从来没有如此吵闹过. 听着同伴破碎的声音, 就好像安眠曲一样, 雪花的安眠曲. 不知翻滚了多久, 也不知道有多少雪花被抛起来和阳光混合在一起之后. 爆鸣声终于停下了, 雪花也变成了曾经只有薄薄一层外壳的样子. 雪花和一些随机的同伴躺在了一块石头上, 阳光正在让最后一层外壳褪去, 雪花就再也不是雪花了, 是各式各样的尘埃, 灰色的, 黑色的, 透明的. 石头上的雪花终于变成了灰尘, 和同伴们粘连在一起依附在这块石头上. 直到下一阵狂风的到来, 雪花, 或者是尘埃可能还是会选择再次飞舞在空中. 但是尘埃还能再次变成雪花吗? 后记 浓雾总是伴随着雪, 这是这里的斗篷们长久以来得出的经验. 斗篷都知道雾和云是有区别的, 但这里的地势太高, 浓雾和云已经没有太多可区分的特征了, 这里只有雾, 白色的雾, 灰色的雾. 可以这么说, 斗篷们从来就没见过云, 大多数斗篷聚集的地方终年都下着雪, 也没有斗篷真正向往雾背后的天空, 那里的阳光太刺眼, 比起靠着不稳定的光源, 斗篷们更喜欢需要的时候升起一堆篝火, 即使大多时候要用只剩空壳的斗篷作为燃料.","summary":"序 天色没有好转, 还是阴沉沉的一片, 灰暗的天空看不到云之间的空隙, 好像只是绵密的浓雾. 雨滴从浓雾中落下, 从液态瞬间变为固态, 来不及和其他的水滴一起停留, 变成了随风飘荡的雪花落下来. 这里的风总是会在雪花落下时吹起, 雨滴从来不会如愿落到它们想要到达的地方. 有的雨滴在抱怨风让它们变成了雪, 有的雨滴在抱怨浓雾把它们变成了雪, 还有的雨滴在抱怨已经在地面的雪让它们也不得不变成雪. 它们不情愿地积聚在一起, 无论如何, 它们都想要再次飞舞, 或再次变成完美的雪花, 或变成晶莹剔透的水滴, 或变成一粒尘埃. 正文 从浓雾中睁开眼, 灰暗的周围到处都是同伴, 但似乎被困在了一个透明的外壳里面. 有东西在不停地推动着我到处飘动, 同伴们飞快地擦肩而过, 能够听见有有人在笑也有人在哭, 这里奇怪的背景音都是这些声音在快速地移动中破碎形成的, 和这些浓厚的雾搭配起来就能让人感到烦躁. &quot;离开...&quot;, &quot;离开...这...&quot;, &quot;出... 离开...&quot;, &quot;在... 这里...&quot;, &quot;走... 不....&quot; 灰暗背景中的噪音在脑中和思维交织, 变成了断断续续地呢喃之声. 是的, 一定是, 要走, 一定要离开这里. 从浓雾中向下, 冰晶开始在周围生长, 我的伙伴们也长出了千奇百怪的形状, 多么美丽的外表, 值得目光停留好一阵子, 不过已经停留不了多久了, 我们正在止不住地向下坠落. 这是第一次从浓雾中离开, 千千万万的伙伴都有不同的外观, 在难得一见的光线下折射出多么美妙的光影, 令人陶醉. 再也没有东西推着我们前进, 我们只需要轻轻地向下坠落, 旋转着, 或者是躺在风里. 即使是狂风也忍不住赞美我们的美丽, 是否有那么一瞬间的念头出现过, 为了这一些的美好应该就此停留? 但这是做不到的, 冰晶仍然在生长, 越来越重的外壳无法让这一切停留. 在空中的时候, 雪花似乎在兴奋地讨论这下面的光景, 下面是多么热闹, 多么奇异, 那么多的同伴们在那里一定是更加美丽的地方. 想要去山脊上, 想要去树枝上, 想要去红色的那片悬崖上, 还有的想要钻进斗篷们的兜帽里. 幻想过无数种千奇百怪的地方, 笑着完成了最后的那一层外壳的生长, 终于能够如愿到达了这里. 但没有一片雪花落到了自己想要的地方, 至少身边的同伴都不见了. 在这片光洁的雪原上, 风并不如雾中那么猛烈, 也没有那么吵闹, 这里实在是安静地太不正常了. 雪花在呼喊同伴, 但即使身边全部都是同类, 但没有一片雪花回应它, 所有的雪花都以千奇百怪的方式停在其他雪花的身上, 旋转着, 或是躺在雪里. 上面依旧有雪花落下, 也有的直接压在自己身上, 呼唤就在眼前的雪花也没有任何回应, 就像是来自两个世界的水滴变成的雪花一样. 越来越多的雪花堆积起来, 已经看不到外面的光景了. 稳定下来后, 能感受到身上的同伴越来越多了, 明显的压迫感越来越强, 但依旧没有任何回应, 就像都是虚假的雪花一样, 不知是从什么地方批量制造出来的, 反正不是雾里的, 雪花如此笃定的认为. 半夜, 也许是半夜, 因为已经不知道过去了多久, 也看不到一丝光线. 静悄悄的周围突然有了奇怪的声音, 这不是雪花能够发出的声音, 就像两颗水滴被风推着相撞发出的爆裂声, 身上的假雪花也在慢慢地移动, 还以为是它们复活了, 结果发现是有东西在推着它们移动. 奇怪的声音原来是来自雪花破碎的声音, 厚重的冰晶外壳挤压破碎发出了爆裂声, 雪花正在失去曾经引以为傲的美丽外表, 同伴也在互相碰撞中变成了丑陋的形状, 互相粘附在一起, 完全不是雪花该有的样子. 终于, 持续不断的爆裂声被一个更大的声音盖过了. 这是一个明显不同的声音, 不可能是碎裂的冰晶能发出的, 雪花想起了浓雾中的狂风相撞的声音. 身上的丑陋的假雪花开始更加快速地移动, 被带着也连翻了无数个身, 身下的同伴和身上的同伴再也分不清, 不知道翻了多少圈, 已经无法判断上和下, 雪花和同伴们也变成了一模一样的丑陋的假雪花. 雪花翻滚着, 随着沉默的同伴一起被推动着往一个方向前进, 直到一瞬间, 雪花听见了有东西在呼唤, 居然是假雪花们, 褪去了厚重外壳的雪花露出了奇怪的核心, 也终于能听到其他雪花的声音, 是厚重的外壳挡住了雪花的声音. 但都太晚了, 就和浓雾中一样, 雪花们被强大的力量推动着不断翻滚, 根本来不及停留, 破碎的雪花互相摩擦, 撞击, 就和浓雾中的声音一样. 雪花被卷起来, 一瞬间看到了刺眼的阳光但立马就被盖在同伴的底下, 爆裂的声音依旧在持续. 雪花的声音, 雪花破碎的声音和更广泛存在的爆鸣声混杂在一起, 雪原上从来没有如此吵闹过. 听着同伴破碎的声音, 就好像安眠曲一样, 雪花的安眠曲. 不知翻滚了多久, 也不知道有多少雪花被抛起来和阳光混合在一起之后. 爆鸣声终于停下了, 雪花也变成了曾经只有薄薄一层外壳的样子. 雪花和一些随机的同伴躺在了一块石头上, 阳光正在让最后一层外壳褪去, 雪花就再也不是雪花了, 是各式各样的尘埃, 灰色的, 黑色的, 透明的. 石头上的雪花终于变成了灰尘, 和同伴们粘连在一起依附在这块石头上. 直到下一阵狂风的到来, 雪花, 或者是尘埃可能还是会选择再次飞舞在空中. 但是尘埃还能再次变成雪花吗? 后记 浓雾总是伴随着雪, 这是这里的斗篷们长久以来得出的经验. 斗篷都知道雾和云是有区别的, 但这里的地势太高, 浓雾和云已经没有太多可区分的特征了, 这里只有雾, 白色的雾, 灰色的雾. 可以这么说, 斗篷们从来就没见过云, 大多数斗篷聚集的地方终年都下着雪, 也没有斗篷真正向往雾背后的天空, 那里的阳光太刺眼, 比起靠着不稳定的光源, 斗篷们更喜欢需要的时候升起一堆篝火, 即使大多时候要用只剩空壳的斗篷作为燃料.","date_published":"2025-03-16T12:32:00.000Z","tags":["密语瓶"]},{"id":"https://blog.cxplay.org/works/what-makes-a-good-email-domain-name/","url":"https://blog.cxplay.org/works/what-makes-a-good-email-domain-name/","title":"什么样的电子邮件域名才是「好」的?","content_html":"<p>电子邮件可能是全世界除了移动电话后最为融入现实生活的的通讯手段, 尽管在一些移动互联网优先发展的国家可能即时通讯看起来已经全面取代电子邮件, 但实际上由于电子邮件全世界公认的法律地位和正式性, 越是正式和重要的交流需要就会越优先选择电子邮件, 这是即时通讯无法取代的.</p>\n<p>无法忽视的当然就是地区文化习惯. 在中国大陆, 人们相比拉丁字母更喜欢用阿拉伯数字来标志自己的通讯方式, 包括最常用的 163, 126, 189 邮箱都是以数字为顶级域名名称, 甚至后来允许用户注册以自己手机号为用户名的邮箱地址. 直到现在, 纯数字的 Gmail 和 Outlook 这些主流国际邮件账号往往会比同样甚至更短位数的双拼单词账户更受国内用户欢迎. 最后就是用户基数巨大的 QQ 邮箱, 它默认使用纯数字的 QQ 号码作为邮箱用户名, 也在一段时间, 追求短位的「QQ 靓号」也是受众多网民追捧的玩具, 而也由于大多 QQ 邮箱用户都直接使用默认的 QQ 号码用户名, 也因此到后来被人嫌弃.</p>\n<p>但是如果你现在切换身份为一个想要经营电子邮件服务的新兴服务商或者是想拥有自己邮局的爱好者, 迫切需要找到一个域名用来作为首选账户注册域名. 如果不仅考虑中国大陆在内的特殊习惯, 那么应该如何挑选一个合适的域名从头开始建立印象? 按照我的个人经验, 以下五点可供读者参考.</p>\n<ol>\n<li>\n<p><strong>人们大多希望得到「看起来就知道是邮箱」域名.</strong><br>\n虽然电子邮件的标志应该是 &quot;@&quot;, 但由于它在邮件地址中所占空间反而是最为固定和不起眼的, 所以实际上人们更在意的是域名, 并且如果你没有身为邮件服务的影响力, 那么最好要让人们脑子内自动出现 &quot;这是一个邮件地址, 不要怀疑&quot; 的想法.</p>\n</li>\n<li>\n<p><strong>泛用的「体面」电子邮件顶级域名选择是极少的.</strong><br>\n大多数习惯性和互联网保持距离的人实际上在他们脑海里对于 &quot;域名&quot; 这个概念基本就等同于 .com, 即使在如今 New gTLD 泛滥的现在也是如此. 当你报出你的电子邮件地址, 如果不提示那么实际上人们都会默认你的顶级域名位于 .com 之下, 就算如果真的不是最多可能只会有 .net 和 .org 两种脑内思维预设.</p>\n</li>\n<li>\n<p><strong>便于人类读写是最重要的, 人们希望能够随时说出和写出自己的邮件地址而不会迟疑更不希望进一步解释.</strong><br>\nGoogle 不会给用户选择 <code>google.com</code> 作为邮件地址或许是考虑了公司品牌形象, 但 Microsoft 停止为用户提供 <code>msn.com</code> 和 <code>live.com</code> 转为全面使用 <code>outlook.com</code> 则就是最好的例子, 因为 &quot;Outlook&quot; 比 &quot;MSN&quot; 更便于人类读写, 即使它更长了; 并且除了发音, 还需要考虑含义是否正面, 至少也是处于中性的位置, 没有普通人会愿意从自己口里随时说出 &quot;<code>pissmail.com</code>&quot; 和 &quot;<code>cock.li</code>&quot; 这些奇怪的「键盘邮箱」<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup>. 于是作为中性词语的 <code>live.com</code> 最后也被 Microsoft 重塑邮件品牌的时候忽略掉了, 而现在也不再为新用户提供这个地址.</p>\n</li>\n<li>\n<p><strong>短小精悍是次要考虑的事情, 但不可以忽略.</strong><br>\n想象一下如果 Google 现在为用户提供新的邮件别名域 <code>googlemail.com</code>, Microsoft 重新为用户提供 <code>live.com</code>, 会有多少新用户会选择这些 &quot;新&quot; 地址域名? 可以肯定的是, 没有人会摆着 <code>gmail.com</code> 不用而去选择 <code>googlemail.com</code>, 而大多数人又会重新选择 <code>live.com</code> 而不是 <code>outlook.com</code>. 因为他们都足够「短小精悍」, 域名的短带来的好处是仅次于良好的标志品牌和含义的, 大部分人都会在体面的情况下选择更短的域名, 因为它相比于更长的选择更便于手写和键盘输入, 所以这是必须要考虑的地方.</p>\n</li>\n<li>\n<p><strong>标志性过强基本决定了人们对它第一印象中的用途.</strong><br>\n也许在今天没有人说出 <code>gmail.com</code> 和 <code>outlook.com</code> 会感到对方很专业, 毕竟全世界人都在用它们. 而当有人说出 <code>proton.me</code> 甚至 <code>riseup.net</code> 的时候, 肯定会被当作是 &quot;互联网高手&quot;, 或者是 &quot;隐私怪&quot;, &quot;互联网原教旨主义者&quot;. 有人不喜欢被人贴上这种看起来是称赞的标签, 所以反而不会选择这些由于其服务特殊性造成的标志性过强的邮件地址, 他们宁愿平淡一点甚至会去选择 <code>mail.com</code>.</p>\n</li>\n</ol>\n<h2 id=\"封面\"><a href=\"#封面\"></a>封面</h2>\n<p><img src=\"https://images.unsplash.com/photo-1596526131083-e8c633c948d2?q=80&amp;w=2574&amp;auto=format&amp;fit=crop&amp;ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\" alt=\"封面\" loading='lazy'></p>\n<blockquote>\n<p>Photo by <a href=\"https://unsplash.com/@brett_jordan?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash\">Brett Jordan</a> on <a href=\"https://unsplash.com/photos/blue-and-white-logo-guessing-game-LPZy4da9aRo?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash\">Unsplash</a></p>\n</blockquote>\n<h2 id=\"注释\"><a href=\"#注释\"></a>注释</h2>\n<p>原文首次发布在: <a href=\"https://t.me/postmasterzh/4\">邮件人频道 - Telegram</a></p>\n<blockquote>\n<p>这是个有关电子邮件的消息频道. 希望能够强化中文 Postmaster 们的交流和联系, 欢迎进群讨论.</p>\n</blockquote>\n<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>我喜欢把这类难以说出口或者根本不会出现在书面条件下邮箱称之为「键盘邮箱」. <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n","content_text":"电子邮件可能是全世界除了移动电话后最为融入现实生活的的通讯手段, 尽管在一些移动互联网优先发展的国家可能即时通讯看起来已经全面取代电子邮件, 但实际上由于电子邮件全世界公认的法律地位和正式性, 越是正式和重要的交流需要就会越优先选择电子邮件, 这是即时通讯无法取代的. 无法忽视的当然就是地区文化习惯. 在中国大陆, 人们相比拉丁字母更喜欢用阿拉伯数字来标志自己的通讯方式, 包括最常用的 163, 126, 189 邮箱都是以数字为顶级域名名称, 甚至后来允许用户注册以自己手机号为用户名的邮箱地址. 直到现在, 纯数字的 Gmail 和 Outlook 这些主流国际邮件账号往往会比同样甚至更短位数的双拼单词账户更受国内用户欢迎. 最后就是用户基数巨大的 QQ 邮箱, 它默认使用纯数字的 QQ 号码作为邮箱用户名, 也在一段时间, 追求短位的「QQ 靓号」也是受众多网民追捧的玩具, 而也由于大多 QQ 邮箱用户都直接使用默认的 QQ 号码用户名, 也因此到后来被人嫌弃. 但是如果你现在切换身份为一个想要经营电子邮件服务的新兴服务商或者是想拥有自己邮局的爱好者, 迫切需要找到一个域名用来作为首选账户注册域名. 如果不仅考虑中国大陆在内的特殊习惯, 那么应该如何挑选一个合适的域名从头开始建立印象? 按照我的个人经验, 以下五点可供读者参考. 人们大多希望得到「看起来就知道是邮箱」域名. 虽然电子邮件的标志应该是 &quot;@&quot;, 但由于它在邮件地址中所占空间反而是最为固定和不起眼的, 所以实际上人们更在意的是域名, 并且如果你没有身为邮件服务的影响力, 那么最好要让人们脑子内自动出现 &quot;这是一个邮件地址, 不要怀疑&quot; 的想法. 泛用的「体面」电子邮件顶级域名选择是极少的. 大多数习惯性和互联网保持距离的人实际上在他们脑海里对于 &quot;域名&quot; 这个概念基本就等同于 .com, 即使在如今 New gTLD 泛滥的现在也是如此. 当你报出你的电子邮件地址, 如果不提示那么实际上人们都会默认你的顶级域名位于 .com 之下, 就算如果真的不是最多可能只会有 .net 和 .org 两种脑内思维预设. 便于人类读写是最重要的, 人们希望能够随时说出和写出自己的邮件地址而不会迟疑更不希望进一步解释. Google 不会给用户选择 google.com 作为邮件地址或许是考虑了公司品牌形象, 但 Microsoft 停止为用户提供 msn.com 和 live.com 转为全面使用 outlook.com 则就是最好的例子, 因为 &quot;Outlook&quot; 比 &quot;MSN&quot; 更便于人类读写, 即使它更长了; 并且除了发音, 还需要考虑含义是否正面, 至少也是处于中性的位置, 没有普通人会愿意从自己口里随时说出 &quot;pissmail.com&quot; 和 &quot;cock.li&quot; 这些奇怪的「键盘邮箱」[1]. 于是作为中性词语的 live.com 最后也被 Microsoft 重塑邮件品牌的时候忽略掉了, 而现在也不再为新用户提供这个地址. 短小精悍是次要考虑的事情, 但不可以忽略. 想象一下如果 Google 现在为用户提供新的邮件别名域 googlemail.com, Microsoft 重新为用户提供 live.com, 会有多少新用户会选择这些 &quot;新&quot; 地址域名? 可以肯定的是, 没有人会摆着 gmail.com 不用而去选择 googlemail.com, 而大多数人又会重新选择 live.com 而不是 outlook.com. 因为他们都足够「短小精悍」, 域名的短带来的好处是仅次于良好的标志品牌和含义的, 大部分人都会在体面的情况下选择更短的域名, 因为它相比于更长的选择更便于手写和键盘输入, 所以这是必须要考虑的地方. 标志性过强基本决定了人们对它第一印象中的用途. 也许在今天没有人说出 gmail.com 和 outlook.com 会感到对方很专业, 毕竟全世界人都在用它们. 而当有人说出 proton.me 甚至 riseup.net 的时候, 肯定会被当作是 &quot;互联网高手&quot;, 或者是 &quot;隐私怪&quot;, &quot;互联网原教旨主义者&quot;. 有人不喜欢被人贴上这种看起来是称赞的标签, 所以反而不会选择这些由于其服务特殊性造成的标志性过强的邮件地址, 他们宁愿平淡一点甚至会去选择 mail.com. 封面 Photo by Brett Jordan on Unsplash 注释 原文首次发布在: 邮件人频道 - Telegram 这是个有关电子邮件的消息频道. 希望能够强化中文 Postmaster 们的交流和联系, 欢迎进群讨论. 我喜欢把这类难以说出口或者根本不会出现在书面条件下邮箱称之为「键盘邮箱」. ↩︎","summary":"电子邮件可能是全世界除了移动电话后最为融入现实生活的的通讯手段, 尽管在一些移动互联网优先发展的国家可能即时通讯看起来已经全面取代电子邮件, 但实际上由于电子邮件全世界公认的法律地位和正式性, 越是正式和重要的交流需要就会越优先选择电子邮件, 这是即时通讯无法取代的. 无法忽视的当然就是地区文化习惯. 在中国大陆, 人们相比拉丁字母更喜欢用阿拉伯数字来标志自己的通讯方式, 包括最常用的 163, 126, 189 邮箱都是以数字为顶级域名名称, 甚至后来允许用户注册以自己手机号为用户名的邮箱地址. 直到现在, 纯数字的 Gmail 和 Outlook 这些主流国际邮件账号往往会比同样甚至更短位数的双拼单词账户更受国内用户欢迎. 最后就是用户基数巨大的 QQ 邮箱, 它默认使用纯数字的 QQ 号码作为邮箱用户名, 也在一段时间, 追求短位的「QQ 靓号」也是受众多网民追捧的玩具, 而也由于大多 QQ 邮箱用户都直接使用默认的 QQ 号码用户名, 也因此到后来被人嫌弃. 但是如果你现在切换身份为一个想要经营电子邮件服务的新兴服务商或者是想拥有自己邮局的爱好者, 迫切需要找到一个域名用来作为首选账户注册域名. 如果不仅考虑中国大陆在内的特殊习惯, 那么应该如何挑选一个合适的域名从头开始建立印象? 按照我的个人经验, 以下五点可供读者参考. 人们大多希望得到「看起来就知道是邮箱」域名. 虽然电子邮件的标志应该是 &quot;@&quot;, 但由于它在邮件地址中所占空间反而是最为固定和不起眼的, 所以实际上人们更在意的是域名, 并且如果你没有身为邮件服务的影响力, 那么最好要让人们脑子内自动出现 &quot;这是一个邮件地址, 不要怀疑&quot; 的想法. 泛用的「体面」电子邮件顶级域名选择是极少的. 大多数习惯性和互联网保持距离的人实际上在他们脑海里对于 &quot;域名&quot; 这个概念基本就等同于 .com, 即使在如今 New gTLD 泛滥的现在也是如此. 当你报出你的电子邮件地址, 如果不提示那么实际上人们都会默认你的顶级域名位于 .com 之下, 就算如果真的不是最多可能只会有 .net 和 .org 两种脑内思维预设. 便于人类读写是最重要的, 人们希望能够随时说出和写出自己的邮件地址而不会迟疑更不希望进一步解释. Google 不会给用户选择 google.com 作为邮件地址或许是考虑了公司品牌形象, 但 Microsoft 停止为用户提供 msn.com 和 live.com 转为全面使用 outlook.com 则就是最好的例子, 因为 &quot;Outlook&quot; 比 &quot;MSN&quot; 更便于人类读写, 即使它更长了; 并且除了发音, 还需要考虑含义是否正面, 至少也是处于中性的位置, 没有普通人会愿意从自己口里随时说出 &quot;pissmail.com&quot; 和 &quot;cock.li&quot; 这些奇怪的「键盘邮箱」[1]. 于是作为中性词语的 live.com 最后也被 Microsoft 重塑邮件品牌的时候忽略掉了, 而现在也不再为新用户提供这个地址. 短小精悍是次要考虑的事情, 但不可以忽略. 想象一下如果 Google 现在为用户提供新的邮件别名域 googlemail.com, Microsoft 重新为用户提供 live.com, 会有多少新用户会选择这些 &quot;新&quot; 地址域名? 可以肯定的是, 没有人会摆着 gmail.com 不用而去选择 googlemail.com, 而大多数人又会重新选择 live.com 而不是 outlook.com. 因为他们都足够「短小精悍」, 域名的短带来的好处是仅次于良好的标志品牌和含义的, 大部分人都会在体面的情况下选择更短的域名, 因为它相比于更长的选择更便于手写和键盘输入, 所以这是必须要考虑的地方. 标志性过强基本决定了人们对它第一印象中的用途. 也许在今天没有人说出 gmail.com 和 outlook.com 会感到对方很专业, 毕竟全世界人都在用它们. 而当有人说出 proton.me 甚至 riseup.net 的时候, 肯定会被当作是 &quot;互联网高手&quot;, 或者是 &quot;隐私怪&quot;, &quot;互联网原教旨主义者&quot;. 有人不喜欢被人贴上这种看起来是称赞的标签, 所以反而不会选择这些由于其服务特殊性造成的标志性过强的邮件地址, 他们宁愿平淡一点甚至会去选择 mail.com. 封面 Photo by Brett Jordan on Unsplash 注释 原文首次发布在: 邮件人频道 - Telegram 这是个有关电子邮件的消息频道. 希望能够强化中文 Postmaster 们的交流和联系, 欢迎进群讨论. 我喜欢把这类难以说出口或者根本不会出现在书面条件下邮箱称之为「键盘邮箱」. ↩︎","date_published":"2025-01-30T08:37:13.000Z","tags":["资料库","Email","电子邮件","域名"]},{"id":"https://blog.cxplay.org/works/copyright-or-left-please/","url":"https://blog.cxplay.org/works/copyright-or-left-please/","title":"如果你在乎你网上的内容, 请为它们附上版权声明.","content_html":"<p><strong>如果你在乎你网上的内容, 请为它们附上版权声明. 如果你在共享你的内容, 请表明你的意图. 否则不要抱怨别人为何不按你的意愿使用, 因为你从没有表明过它.</strong></p>\n<p>同样身为创作者, 但还没有能自诩 &quot;艺术家&quot; 的程度, 从自己生产内容然后公开的开始就是希望被別人看到自己的作品, 并且要让别人知道「这是我创造的东西」, 然后才会有原创, 抄袭和借鉴的争论.\n我是从最开始也是从 UGC 平台上逐渐转移到拥有自己 &quot;平台&quot; (从博客开始) 的人, 当时只为了追求所谓「自由」, 自己想写什么就写什么, 这是我的博客凭什么你来指指点点? 然后逐渐意识到当自己的身份从创作者用户过渡到创作者平台, 必须要考虑的事情就会变多, 这也是权利和义务的无条件对等结果, 我自己一个人就要成为平台. 到这时, 能对我指指点点人只会变得更多, 体量只会更大, 范围也会扩大到全世界, 因为这是互联网. 那么生活在在 UGC 平台的人难道就没有这个烦恼吗? 不是的, 只不过是平台已经帮我做了决定, 因为我必须同意他们的使用政策和隐私协议我才能使用, 包括其中顺带同意的版权声明.</p>\n<p>作为小到自己都不想称之为一个 &quot;平台&quot; 的独立博客, 也要用自己身为平台应该要做的事情, 我的博客用户是谁? 是所有能够访问到我的博客的人, 机器人甚至伪装为人的机器人.</p>\n<hr>\n<p>所以我需要:</p>\n<ul>\n<li>\n<p>如果我用了 Google Analytics 而我如果要面向的用户当地存在个人数据法, 那就要加上一个 cookie 知情确认通知.</p>\n</li>\n<li>\n<p>如果有机器人来我的博客, 而我不想让它们进来, 那我应该声明 robots.txt.</p>\n</li>\n<li>\n<p>如果我的用户, 我的读者希望能够轻松自如地帮我分享内容而不用时时刻刻都向我发消息确认转发请求, 那么我应该声明版权许可, 那至少也是 CC-BY 的等级.</p>\n</li>\n<li>\n<p>如果我不希望我的内容在沒有许可的情况下被复制, 被重新演绎, 被用作商业用途盈利; 要么实行事后责任制, 请一个版权律师和版权机器人帮我给这些讨厌的东西发律师函, 发给对方的 ASN 管理员, DNS 解析服务器管理员, 域名管理局或者其他所有为它提供基础设施服务的服务商, 期盼他们能够遵守「自己的」法律.</p>\n</li>\n<li>\n<p>如果我不希望某些用户访问我的博客, 我需要使用 WAF 屏蔽他们.</p>\n</li>\n</ul>\n<p>但可惜, 这互联网上最著名的版权法案 DMCA 也存在 &quot;合理使用&quot; 裁定, 各国各地对互联网著作权的处理也不尽相同, 如此大费周章并不能就让所有我想要不能使用我内容的人放弃使用我的内容. 那么真的没有办法了?</p>\n<p>没有问题, 还可以同时实行事前责任制, 因为我还有 DRM, 也就是数字版权管理. 我能自己购买, 租用甚至自己开发一套版权管理系统, 只有在我的平台上才能看到我的内容, 别人想要复制我的内容会变得无比艰难, 但也只止步于 &quot;无比艰难&quot; 而已.</p>\n<p>我作为平台, 需要这么努力吗? 或者说有必要这么麻烦吗? 手段的升级只会消耗更多的时间和金钱, 我只是一个小小的独立博客, 我只能用上 CC 和 robots.txt, 最多给内容加点水印. 我只是想保护我的内容而已, OpenAI 一众很可能已经在不知不觉中掠夺过我的东西了, 治不了大公司还治不了你吗?!</p>\n<p>恭喜你, 你已经拥有成为一个平台的觉悟了.</p>\n<h2 id=\"说点实际的\"><a href=\"#说点实际的\"></a>说点实际的</h2>\n<p>我能在此如此大放厥词完全因为我实际拥有这个博客, 不用担心我会因为一两句话就破坏某些平台的狗屁 &quot;社区守则&quot; 乃至它们左右摇摆的政治立场, 没有别的意思, 这里的「政治」只不过是对于这些平台在社会中所扮演角色的简称.</p>\n<p>如果你同意我说的, 那么下面是作为多个「独立平台」管理员<strong>对平台管理员的一些建议</strong>:</p>\n<ol>\n<li><strong>如果你愿意为你的内容负责, 请至少为你的独立平台附上版权声明, 哪怕是在页脚加一个 &quot;Copyright © CC-BY&quot; 甚至 &quot;Copyright ©  All rights reserved&quot;.</strong> 当然前提是你的内容全部出自你的手, 或者你的平台有其他用户并且他们同意你的声明.</li>\n<li><strong>如果你希望或者不希望被机器人或者某些机器人自动抓取内容, 请为你的独立平台添加 robots.txt.</strong> 所有的机器人都能声称自己是真实的用户代理(User-Agent), 在如今的互联网上, 所有人都默认在没有 robots.txt 声明的情况下机器人可以随意进出你的平台, 尝试获取你的平台内容.</li>\n</ol>\n<p>如果你已经是平台内的用户了, 或者你的独立平台需要使用其他平台的内容, <strong>以下对于内容创作者的建议</strong>:</p>\n<ol>\n<li><strong>不要尝试使用任何没有版权声明的平台里的实际内容.</strong> 它们比 &quot;保留所有权利&quot; 甚至带有 DRM 的内容更加不确定, 因为它们的创作者不愿意主动表露自己对他人使用自己内容的意图. 除非你愿意到处查找内容创作者或者平台的联系方式, 然后联系上他们请求使用他们的东西. 当然, 直接不使用实际内容就行了, 你可以引用来源乃至完全重新演绎它们, 就像 ChatGPT 一样.</li>\n<li><strong>好好阅读平台的版权声明, 使用许可和隐私政策, 大多数时候你的东西是不是你的取决于平台而不是你, 甚至包括你的隐私.</strong> 实际上, 我们处于社会化状态下是被动着去使用某些平台, 要么你说服别人或者强迫别人去使用你想用的平台, 而这又对于追求「人人平等」的现代社会是不可接受的, 除非这种对等关系被打破. 知悉这些条款并且在乎自己内容的创作者能够控制自己可以在这里产生什么东西, 或者是在平台上借助自己的内容和平台达成交易换取自己想要的东西.</li>\n</ol>\n<h2 id=\"结语\"><a href=\"#结语\"></a>结语</h2>\n<p>创作者或者是艺术家的世界对于版权这种事情看起来很在乎, 但实际上没有几个人是亲自去执行的, 大多都是依附于创作平台或者版权公司, 让它们代行自己的权利, 让自己能够专心于创作, 然后拿到自己想要的.</p>\n<p>然而在计算机和互联网融合的世界, 构建这个数字世界的 &quot;艺术家&quot; 们早就已经发起了一场颠覆这种局面的政治运动, 名字叫作 &quot;开源&quot;, 赋予开源权利的许可叫做 &quot;开源许可&quot;, 成就他们理想的叫做 &quot;自由软件&quot;, 自由软件基金会和 GPL 许可证由此诞生, 始于 1989 年.</p>\n<p>而现实世界的艺术家们呢? 他们创作文学, 绘画, 音乐乃至影片在互联网上获得全世界范围的传播, 但可惜依旧遵守着老一套的规矩, 把自己的作品交给平台, 交给公司管理. 自由软件基金会诞生后的 12 年, 知识共享(Creative Commons, CC)才出现在互联网. 那么在这之前的 12 年间, 互联网上的艺术家们生产的内容难道都是默认公共领域的吗? 我想更多是即使想要保留部分权利但根本没有意识到要声明自己的意图.</p>\n<p>而二十多年后的今天, 依旧如此. &quot;书呆子&quot; 程序员无人不知开源, 知道自己的创造的东西需要让别人知道自己的作品能够被如何使用, 即使是 &quot;Copyleft&quot;, 是 &quot;All rights reversed&quot; 放弃了全部权利, 也是知道自己一开始就有权利可以对自己的东西这么做.</p>\n<p>而那些迷失在意识洪流中的疯狂艺术家们, 对待自己的作品如何被别人使用上还是模棱两可, 暧昧不清. 即使是 CC 和 robots.txt 也都是可有可无地充满艺术感, 他们确实在乎自己的作品, 但是更在乎自己.</p>\n<blockquote>\n<p>PS: 本文属一时兴起一笔写完, 可能有很多奇怪的地方, 如果需要转载, 请首先遵守本站/账户的版权许可. 欢迎指正和纠错.</p>\n</blockquote>\n<h2 id=\"封面\"><a href=\"#封面\"></a>封面</h2>\n<p><img src=\"https://images.unsplash.com/photo-1455390582262-044cdead277a?q=80&amp;w=2573&amp;auto=format&amp;fit=crop&amp;ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\" alt=\"本文封面\" loading='lazy'></p>\n<blockquote>\n<p>Photo by <a href=\"https://unsplash.com/@aaronburden?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash\">Aaron Burden</a> on <a href=\"https://unsplash.com/photos/fountain-pen-on-black-lined-paper-y02jEX_B0O0?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash\">Unsplash</a></p>\n</blockquote>\n","content_text":"如果你在乎你网上的内容, 请为它们附上版权声明. 如果你在共享你的内容, 请表明你的意图. 否则不要抱怨别人为何不按你的意愿使用, 因为你从没有表明过它. 同样身为创作者, 但还没有能自诩 &quot;艺术家&quot; 的程度, 从自己生产内容然后公开的开始就是希望被別人看到自己的作品, 并且要让别人知道「这是我创造的东西」, 然后才会有原创, 抄袭和借鉴的争论. 我是从最开始也是从 UGC 平台上逐渐转移到拥有自己 &quot;平台&quot; (从博客开始) 的人, 当时只为了追求所谓「自由」, 自己想写什么就写什么, 这是我的博客凭什么你来指指点点? 然后逐渐意识到当自己的身份从创作者用户过渡到创作者平台, 必须要考虑的事情就会变多, 这也是权利和义务的无条件对等结果, 我自己一个人就要成为平台. 到这时, 能对我指指点点人只会变得更多, 体量只会更大, 范围也会扩大到全世界, 因为这是互联网. 那么生活在在 UGC 平台的人难道就没有这个烦恼吗? 不是的, 只不过是平台已经帮我做了决定, 因为我必须同意他们的使用政策和隐私协议我才能使用, 包括其中顺带同意的版权声明. 作为小到自己都不想称之为一个 &quot;平台&quot; 的独立博客, 也要用自己身为平台应该要做的事情, 我的博客用户是谁? 是所有能够访问到我的博客的人, 机器人甚至伪装为人的机器人. 所以我需要: 如果我用了 Google Analytics 而我如果要面向的用户当地存在个人数据法, 那就要加上一个 cookie 知情确认通知. 如果有机器人来我的博客, 而我不想让它们进来, 那我应该声明 robots.txt. 如果我的用户, 我的读者希望能够轻松自如地帮我分享内容而不用时时刻刻都向我发消息确认转发请求, 那么我应该声明版权许可, 那至少也是 CC-BY 的等级. 如果我不希望我的内容在沒有许可的情况下被复制, 被重新演绎, 被用作商业用途盈利; 要么实行事后责任制, 请一个版权律师和版权机器人帮我给这些讨厌的东西发律师函, 发给对方的 ASN 管理员, DNS 解析服务器管理员, 域名管理局或者其他所有为它提供基础设施服务的服务商, 期盼他们能够遵守「自己的」法律. 如果我不希望某些用户访问我的博客, 我需要使用 WAF 屏蔽他们. 但可惜, 这互联网上最著名的版权法案 DMCA 也存在 &quot;合理使用&quot; 裁定, 各国各地对互联网著作权的处理也不尽相同, 如此大费周章并不能就让所有我想要不能使用我内容的人放弃使用我的内容. 那么真的没有办法了? 没有问题, 还可以同时实行事前责任制, 因为我还有 DRM, 也就是数字版权管理. 我能自己购买, 租用甚至自己开发一套版权管理系统, 只有在我的平台上才能看到我的内容, 别人想要复制我的内容会变得无比艰难, 但也只止步于 &quot;无比艰难&quot; 而已. 我作为平台, 需要这么努力吗? 或者说有必要这么麻烦吗? 手段的升级只会消耗更多的时间和金钱, 我只是一个小小的独立博客, 我只能用上 CC 和 robots.txt, 最多给内容加点水印. 我只是想保护我的内容而已, OpenAI 一众很可能已经在不知不觉中掠夺过我的东西了, 治不了大公司还治不了你吗?! 恭喜你, 你已经拥有成为一个平台的觉悟了. 说点实际的 我能在此如此大放厥词完全因为我实际拥有这个博客, 不用担心我会因为一两句话就破坏某些平台的狗屁 &quot;社区守则&quot; 乃至它们左右摇摆的政治立场, 没有别的意思, 这里的「政治」只不过是对于这些平台在社会中所扮演角色的简称. 如果你同意我说的, 那么下面是作为多个「独立平台」管理员对平台管理员的一些建议: 如果你愿意为你的内容负责, 请至少为你的独立平台附上版权声明, 哪怕是在页脚加一个 &quot;Copyright © CC-BY&quot; 甚至 &quot;Copyright © All rights reserved&quot;. 当然前提是你的内容全部出自你的手, 或者你的平台有其他用户并且他们同意你的声明. 如果你希望或者不希望被机器人或者某些机器人自动抓取内容, 请为你的独立平台添加 robots.txt. 所有的机器人都能声称自己是真实的用户代理(User-Agent), 在如今的互联网上, 所有人都默认在没有 robots.txt 声明的情况下机器人可以随意进出你的平台, 尝试获取你的平台内容. 如果你已经是平台内的用户了, 或者你的独立平台需要使用其他平台的内容, 以下对于内容创作者的建议: 不要尝试使用任何没有版权声明的平台里的实际内容. 它们比 &quot;保留所有权利&quot; 甚至带有 DRM 的内容更加不确定, 因为它们的创作者不愿意主动表露自己对他人使用自己内容的意图. 除非你愿意到处查找内容创作者或者平台的联系方式, 然后联系上他们请求使用他们的东西. 当然, 直接不使用实际内容就行了, 你可以引用来源乃至完全重新演绎它们, 就像 ChatGPT 一样. 好好阅读平台的版权声明, 使用许可和隐私政策, 大多数时候你的东西是不是你的取决于平台而不是你, 甚至包括你的隐私. 实际上, 我们处于社会化状态下是被动着去使用某些平台, 要么你说服别人或者强迫别人去使用你想用的平台, 而这又对于追求「人人平等」的现代社会是不可接受的, 除非这种对等关系被打破. 知悉这些条款并且在乎自己内容的创作者能够控制自己可以在这里产生什么东西, 或者是在平台上借助自己的内容和平台达成交易换取自己想要的东西. 结语 创作者或者是艺术家的世界对于版权这种事情看起来很在乎, 但实际上没有几个人是亲自去执行的, 大多都是依附于创作平台或者版权公司, 让它们代行自己的权利, 让自己能够专心于创作, 然后拿到自己想要的. 然而在计算机和互联网融合的世界, 构建这个数字世界的 &quot;艺术家&quot; 们早就已经发起了一场颠覆这种局面的政治运动, 名字叫作 &quot;开源&quot;, 赋予开源权利的许可叫做 &quot;开源许可&quot;, 成就他们理想的叫做 &quot;自由软件&quot;, 自由软件基金会和 GPL 许可证由此诞生, 始于 1989 年. 而现实世界的艺术家们呢? 他们创作文学, 绘画, 音乐乃至影片在互联网上获得全世界范围的传播, 但可惜依旧遵守着老一套的规矩, 把自己的作品交给平台, 交给公司管理. 自由软件基金会诞生后的 12 年, 知识共享(Creative Commons, CC)才出现在互联网. 那么在这之前的 12 年间, 互联网上的艺术家们生产的内容难道都是默认公共领域的吗? 我想更多是即使想要保留部分权利但根本没有意识到要声明自己的意图. 而二十多年后的今天, 依旧如此. &quot;书呆子&quot; 程序员无人不知开源, 知道自己的创造的东西需要让别人知道自己的作品能够被如何使用, 即使是 &quot;Copyleft&quot;, 是 &quot;All rights reversed&quot; 放弃了全部权利, 也是知道自己一开始就有权利可以对自己的东西这么做. 而那些迷失在意识洪流中的疯狂艺术家们, 对待自己的作品如何被别人使用上还是模棱两可, 暧昧不清. 即使是 CC 和 robots.txt 也都是可有可无地充满艺术感, 他们确实在乎自己的作品, 但是更在乎自己. PS: 本文属一时兴起一笔写完, 可能有很多奇怪的地方, 如果需要转载, 请首先遵守本站/账户的版权许可. 欢迎指正和纠错. 封面 Photo by Aaron Burden on Unsplash","summary":"如果你在乎你网上的内容, 请为它们附上版权声明. 如果你在共享你的内容, 请表明你的意图. 否则不要抱怨别人为何不按你的意愿使用, 因为你从没有表明过它. 同样身为创作者, 但还没有能自诩 &quot;艺术家&quot; 的程度, 从自己生产内容然后公开的开始就是希望被別人看到自己的作品, 并且要让别人知道「这是我创造的东西」, 然后才会有原创, 抄袭和借鉴的争论. 我是从最开始也是从 UGC 平台上逐渐转移到拥有自己 &quot;平台&quot; (从博客开始) 的人, 当时只为了追求所谓「自由」, 自己想写什么就写什么, 这是我的博客凭什么你来指指点点? 然后逐渐意识到当自己的身份从创作者用户过渡到创作者平台, 必须要考虑的事情就会变多, 这也是权利和义务的无条件对等结果, 我自己一个人就要成为平台. 到这时, 能对我指指点点人只会变得更多, 体量只会更大, 范围也会扩大到全世界, 因为这是互联网. 那么生活在在 UGC 平台的人难道就没有这个烦恼吗? 不是的, 只不过是平台已经帮我做了决定, 因为我必须同意他们的使用政策和隐私协议我才能使用, 包括其中顺带同意的版权声明. 作为小到自己都不想称之为一个 &quot;平台&quot; 的独立博客, 也要用自己身为平台应该要做的事情, 我的博客用户是谁? 是所有能够访问到我的博客的人, 机器人甚至伪装为人的机器人. 所以我需要: 如果我用了 Google Analytics 而我如果要面向的用户当地存在个人数据法, 那就要加上一个 cookie 知情确认通知. 如果有机器人来我的博客, 而我不想让它们进来, 那我应该声明 robots.txt. 如果我的用户, 我的读者希望能够轻松自如地帮我分享内容而不用时时刻刻都向我发消息确认转发请求, 那么我应该声明版权许可, 那至少也是 CC-BY 的等级. 如果我不希望我的内容在沒有许可的情况下被复制, 被重新演绎, 被用作商业用途盈利; 要么实行事后责任制, 请一个版权律师和版权机器人帮我给这些讨厌的东西发律师函, 发给对方的 ASN 管理员, DNS 解析服务器管理员, 域名管理局或者其他所有为它提供基础设施服务的服务商, 期盼他们能够遵守「自己的」法律. 如果我不希望某些用户访问我的博客, 我需要使用 WAF 屏蔽他们. 但可惜, 这互联网上最著名的版权法案 DMCA 也存在 &quot;合理使用&quot; 裁定, 各国各地对互联网著作权的处理也不尽相同, 如此大费周章并不能就让所有我想要不能使用我内容的人放弃使用我的内容. 那么真的没有办法了? 没有问题, 还可以同时实行事前责任制, 因为我还有 DRM, 也就是数字版权管理. 我能自己购买, 租用甚至自己开发一套版权管理系统, 只有在我的平台上才能看到我的内容, 别人想要复制我的内容会变得无比艰难, 但也只止步于 &quot;无比艰难&quot; 而已. 我作为平台, 需要这么努力吗? 或者说有必要这么麻烦吗? 手段的升级只会消耗更多的时间和金钱, 我只是一个小小的独立博客, 我只能用上 CC 和 robots.txt, 最多给内容加点水印. 我只是想保护我的内容而已, OpenAI 一众很可能已经在不知不觉中掠夺过我的东西了, 治不了大公司还治不了你吗?! 恭喜你, 你已经拥有成为一个平台的觉悟了. 说点实际的 我能在此如此大放厥词完全因为我实际拥有这个博客, 不用担心我会因为一两句话就破坏某些平台的狗屁 &quot;社区守则&quot; 乃至它们左右摇摆的政治立场, 没有别的意思, 这里的「政治」只不过是对于这些平台在社会中所扮演角色的简称. 如果你同意我说的, 那么下面是作为多个「独立平台」管理员对平台管理员的一些建议: 如果你愿意为你的内容负责, 请至少为你的独立平台附上版权声明, 哪怕是在页脚加一个 &quot;Copyright © CC-BY&quot; 甚至 &quot;Copyright © All rights reserved&quot;. 当然前提是你的内容全部出自你的手, 或者你的平台有其他用户并且他们同意你的声明. 如果你希望或者不希望被机器人或者某些机器人自动抓取内容, 请为你的独立平台添加 robots.txt. 所有的机器人都能声称自己是真实的用户代理(User-Agent), 在如今的互联网上, 所有人都默认在没有 robots.txt 声明的情况下机器人可以随意进出你的平台, 尝试获取你的平台内容. 如果你已经是平台内的用户了, 或者你的独立平台需要使用其他平台的内容, 以下对于内容创作者的建议: 不要尝试使用任何没有版权声明的平台里的实际内容. 它们比 &quot;保留所有权利&quot; 甚至带有 DRM 的内容更加不确定, 因为它们的创作者不愿意主动表露自己对他人使用自己内容的意图. 除非你愿意到处查找内容创作者或者平台的联系方式, 然后联系上他们请求使用他们的东西. 当然, 直接不使用实际内容就行了, 你可以引用来源乃至完全重新演绎它们, 就像 ChatGPT 一样. 好好阅读平台的版权声明, 使用许可和隐私政策, 大多数时候你的东西是不是你的取决于平台而不是你, 甚至包括你的隐私. 实际上, 我们处于社会化状态下是被动着去使用某些平台, 要么你说服别人或者强迫别人去使用你想用的平台, 而这又对于追求「人人平等」的现代社会是不可接受的, 除非这种对等关系被打破. 知悉这些条款并且在乎自己内容的创作者能够控制自己可以在这里产生什么东西, 或者是在平台上借助自己的内容和平台达成交易换取自己想要的东西. 结语 创作者或者是艺术家的世界对于版权这种事情看起来很在乎, 但实际上没有几个人是亲自去执行的, 大多都是依附于创作平台或者版权公司, 让它们代行自己的权利, 让自己能够专心于创作, 然后拿到自己想要的. 然而在计算机和互联网融合的世界, 构建这个数字世界的 &quot;艺术家&quot; 们早就已经发起了一场颠覆这种局面的政治运动, 名字叫作 &quot;开源&quot;, 赋予开源权利的许可叫做 &quot;开源许可&quot;, 成就他们理想的叫做 &quot;自由软件&quot;, 自由软件基金会和 GPL 许可证由此诞生, 始于 1989 年. 而现实世界的艺术家们呢? 他们创作文学, 绘画, 音乐乃至影片在互联网上获得全世界范围的传播, 但可惜依旧遵守着老一套的规矩, 把自己的作品交给平台, 交给公司管理. 自由软件基金会诞生后的 12 年, 知识共享(Creative Commons, CC)才出现在互联网. 那么在这之前的 12 年间, 互联网上的艺术家们生产的内容难道都是默认公共领域的吗? 我想更多是即使想要保留部分权利但根本没有意识到要声明自己的意图. 而二十多年后的今天, 依旧如此. &quot;书呆子&quot; 程序员无人不知开源, 知道自己的创造的东西需要让别人知道自己的作品能够被如何使用, 即使是 &quot;Copyleft&quot;, 是 &quot;All rights reversed&quot; 放弃了全部权利, 也是知道自己一开始就有权利可以对自己的东西这么做. 而那些迷失在意识洪流中的疯狂艺术家们, 对待自己的作品如何被别人使用上还是模棱两可, 暧昧不清. 即使是 CC 和 robots.txt 也都是可有可无地充满艺术感, 他们确实在乎自己的作品, 但是更在乎自己. PS: 本文属一时兴起一笔写完, 可能有很多奇怪的地方, 如果需要转载, 请首先遵守本站/账户的版权许可. 欢迎指正和纠错. 封面 Photo by Aaron Burden on Unsplash","date_published":"2025-01-23T22:22:48.000Z","tags":["绝界行","版权","开源","创作","艺术"]},{"id":"https://blog.cxplay.org/works/so-you-can-subscribe-my-all-of-public-content-via-rss-or-atom-at-now/","url":"https://blog.cxplay.org/works/so-you-can-subscribe-my-all-of-public-content-via-rss-or-atom-at-now/","title":"所以, 现在可以通过 RSS/Atom 订阅我的所有公开内容","content_html":"<h2 id=\"前言\"><a href=\"#前言\"></a>前言</h2>\n<p>回忆多年前的一些经历, 我发现导致我现在对陈年旧事和人健忘的主要原因是我没有记录的习惯, 而我自己又不擅长记忆没有条理的东西, 如果一件事物没有能成体系地放在我的记忆里, 并且缺乏回忆和重复来对抗遗忘曲线, 那大概最快会在半年之后快速深埋在「记忆宫殿」的深处.</p>\n<blockquote>\n<p>随时记下想法是好的, 定期回顾想法也是必要的. 至少能发现自己一时兴起脑子里冒出来的东西和做梦差不多都有点没头没尾的. 复核自己用文字记录下的念头也至少能查出很多错别字和病句...</p>\n<p>脱离社交网络的几年之间, 我也忘记了很多很多事情, 可能和我的习惯有关. 但我觉得主要问题在于没有记录的地方, 即使真的记下, 后来也因为各种原因被永远遗失了. 整理纸和笔真的不比备份和归档数据简单.</p>\n<p>有些人和事一旦进入到记忆里被 &quot;归档&quot; 后, 如果没有第二次回忆的机会, 那么很可能就会被逐渐埋得越来越深, 这种状态下的记忆, 已经和遗忘没有什么区别了, 就像失忆的人一样. 而我们这些正常人, 只不过是还能记得重要的人和事罢了.</p>\n<p>—— <a href=\"https://ditto.pub/@blog.cxplay.org/posts/adfebe1a0204b5a5ab0f77eeaa34d7d4a2af583cf0a48905dc2bb7cf5587f66f\">https://ditto.pub/@blog.cxplay.org/~</a></p>\n</blockquote>\n<p>这些事情, 并不是真正被遗忘了, 而是已经进入了一种和遗忘没有差别的状态, 日后如果需要这部分记忆还能够通过回忆来找回. 但是可惜, 找回的过程如果没有有价值的「记录点」, 将会是非常费时费力(脑力或是体力)的事情. 如果它是一种技能, 那么找回来的消耗可能很接近甚至大于重新学习一遍的消耗, 特别是「没有成体系的」记忆.</p>\n<p>而建立「记录点」的最好办法实际上就是写作, 于是我会把我自己想要记录的, 并且可以公开的东西全部放在公共网络上, 而作为我日后回忆起完整事件的一个记录点.</p>\n<p>可能会有人说「什么都往网上发, 是不是有暴露癖啊?」. 确实, 我在有这个想法之前, 至少四年之前就深刻地认识到了这个问题, 所以作为读者的你并不用担心我会发什么私密的, 不可告人的东西. 我能公开出来的东西, 我会直接站在能够看到这些内容的人或者机器的角度考虑, 我会发的, 都是我可以给所有人看到的东西, 如果不是所有人能够看到, 但你能直接看到, 那么至少也是能够给你看的, 所以不必抱有这种想法. 但如果你打算把这些东西转发给其他人, 还请和我一样请慎重考虑.</p>\n<p>作为「中文互联网」的一位普通网民, 我希望能够通过这种记录 &quot;流水账&quot; 的方式填充一下匮乏的中文网络, 至于价值和意义? 我不会自诩这些东西能够产生可以被「量化」的价值, 我也不会雇佣别人(或者机器)把这些东西塞到你的眼前, 更不会奢望就此能改善环境或使他人受益; 如果你喜欢, 请随意浏览和互动(包括发表有价值的批评), 如果你不喜欢, 请把我的网址加入到你的广告拦截器中. 最起码的, 我不会用大量毫无价值的 AIGC 来污染我的地盘.</p>\n<h2 id=\"所有-rss-atom-地址\"><a href=\"#所有-rss-atom-地址\"></a>所有 RSS/Atom 地址</h2>\n<p>推荐使用的顺序从上往下排序, 各条目会有单独的使用建议.</p>\n<h3 id=\"博客\"><a href=\"#博客\"></a>博客</h3>\n<blockquote>\n<p><a href=\"https://blog.cxplay.org\">https://blog.cxplay.org</a></p>\n</blockquote>\n<ul>\n<li><strong>Atom Feed</strong>: <a href=\"https://blog.cxplay.org/atom.xml\">https://blog.cxplay.org/atom.xml</a></li>\n<li><strong>RSS2 Feed</strong>: <a href=\"https://blog.cxplay.org/rss2.xml\">https://blog.cxplay.org/rss2.xml</a></li>\n<li><strong>JSON Feed</strong>: <a href=\"https://blog.cxplay.org/feed.json\">https://blog.cxplay.org/feed.json</a></li>\n</ul>\n<p>全文输出, 阅读体验优秀, 推荐刷新频率: 1 天.</p>\n<p>博客是我用来记录基本成体系的内容, 文章字数大多在 1 千字以上, 部分超过 1 万字.</p>\n<p>使用 <a href=\"https://hexo.io/\">Hexo 博客框架</a>, <a href=\"https://github.com/jerryc127/hexo-theme-butterfly\">Butterfly 主题</a>, 以及经过自定义调整适应于主题样式的 Feed 生成插件.</p>\n<h3 id=\"剪贴\"><a href=\"#剪贴\"></a>剪贴</h3>\n<blockquote>\n<ul>\n<li><strong>主站</strong>: <a href=\"https://clip.cx.ms/\">https://clip.cx.ms/</a></li>\n<li><strong>Nostr</strong>: <a href=\"https://ditto.pub/@clip.cx.ms\">https://ditto.pub/@clip.cx.ms</a></li>\n</ul>\n</blockquote>\n<ul>\n<li><strong>主站 Atom Feed</strong> : <a href=\"https://clip.cx.ms/feed\">https://clip.cx.ms/feed</a></li>\n<li><strong>Nostr Atom Feed</strong>: <a href=\"https://nostr.cxplay.org/npub18dcx3xj3zg457k8kxkfmr03a0ltjhsq046tnhdq097m5ms0r284sc0sy5z.rss?cache=public\">https://nostr.cxplay.org/npub18dcx3xj3zg457k8kxkfmr03a0ltjhsq046tnhdq097m5ms0r284sc0sy5z.rss?cache=public</a></li>\n</ul>\n<p>全文输出, 阅读体验良好(极少部分条目输出可能不完整), 推荐刷新频率: 1 小时.</p>\n<p>剪贴是我用来剪藏网络文章和代码片段的地方, 还会包含一些速记. 文章字数大多在 800 字左右. 主站用于长篇剪藏, Nostr 账户用于短篇文章剪藏.</p>\n<p>使用自托管 <a href=\"https://github.com/craigary/nobelium\">Nobelium</a> 服务端渲染生成, <a href=\"https://www.notion.so/\">Notion</a> 作为 CMS, 以及经过自定义的 Feed 生成组件. Nostr 部分使用 <a href=\"https://github.com/fiatjaf/njump\">njump</a> 自托管, 以及经过自定义的 Feed 输出组件.</p>\n<h3 id=\"笔记\"><a href=\"#笔记\"></a>笔记</h3>\n<blockquote>\n<p><a href=\"https://sir.social/@cxplay\">https://sir.social/@cxplay</a></p>\n</blockquote>\n<ul>\n<li>Fediverse 地址: <code>cxplay@sir.social</code></li>\n<li><strong>RSS Feed</strong>: <a href=\"https://sir.social/@cxplay/feed.rss\">https://sir.social/@cxplay/feed.rss</a></li>\n</ul>\n<p>全文输出, 阅读体验良好, 推荐刷新频率: 6 小时.</p>\n<p>笔记是我用来记录短平快但又相比社交媒体贴文更具有记录价值的消息, 功能性是首要的目的, 内容量并不会超过剪贴和博客. 要注意的是, RSS Feed 只包含主帖, 不会包含点赞, 回复和转发.</p>\n<p>这个账户也会作为一部分社交账户功能来使用, 主要与 Fediverse 居民进行互动.</p>\n<p>使用 <a href=\"https://gotosocial.org/\">GoToSocial</a> 自托管.</p>\n<h3 id=\"社交\"><a href=\"#社交\"></a>社交</h3>\n<blockquote>\n<p><a href=\"https://cx.ms/nostr\">https://cx.ms/nostr</a></p>\n</blockquote>\n<ul>\n<li><strong>Atom Feed</strong>: <a href=\"https://nostr.cxplay.org/npub1gd8e0xfkylc7v8c5a6hkpj4gelwwcy99jt90lqjseqjj2t253s2s6ch58h.rss?cache=public\">https://nostr.cxplay.org/npub1gd8e0xfkylc7v8c5a6hkpj4gelwwcy99jt90lqjseqjj2t253s2s6ch58h.rss?cache=public</a></li>\n</ul>\n<p>全文输出, 阅读体验一般, 推荐刷新频率: 1 小时.</p>\n<blockquote>\n<p>由于存在较多的实验性问题, 不再推荐使用这个 Feed. 建议直接订阅频道以间接获取社交内容.</p>\n</blockquote>\n<p>Nostr 是我目前首选使用的社交网络, 因为它相比 Fediverse 更适合个人托管, 并且依然能够依靠「桥」与 Fediverse 互联. 这里主要记录我的日常吐槽, 所以内容十分碎片化, 实话说我不太推荐正常人用 RSS 阅读器浏览社交媒体的时间线.</p>\n<p>使用 <a href=\"https://github.com/fiatjaf/njump\">njump</a> 自托管, 以及经过自定义的 Feed 输出组件.</p>\n<h3 id=\"频道\"><a href=\"#频道\"></a>频道</h3>\n<blockquote>\n<ul>\n<li>\n<p><a href=\"https://t.me/cxplayworld\">https://t.me/cxplayworld</a></p>\n</li>\n<li>\n<p><a href=\"https://channel.cx.ms/\">https://channel.cx.ms/</a></p>\n</li>\n</ul>\n</blockquote>\n<ul>\n<li><strong>Atom Feed</strong>: <a href=\"https://rsshub.app/telegram/channel/cxplayworld\">https://rsshub.app/telegram/channel/cxplayworld</a></li>\n<li><strong>RSS Feed</strong>: <a href=\"https://channel.cx.ms/rss.xml\">https://channel.cx.ms/rss.xml</a></li>\n</ul>\n<p>全文输出(仅频道消息), 阅读体验良好, 推荐刷新频率: 1 小时.</p>\n<p>Telegram 频道目前是聚合了以上所有我的 Feed 推送的频道, 所以你也可以直接订阅频道直接获得所有内容的推送, 但是 Telegram 频道并不适合长篇文章的发布, 所以对于博客和剪贴只会推送链接(预览)和标题.</p>\n<p>由 <a href=\"https://rsshub.app/\">RSSHub</a> 提供 Atom Feed; 自托管 <a href=\"https://github.com/ccbikai/BroadcastChannel\">BroadcastChannel</a> 提供 RSS2 Feed.</p>\n<h2 id=\"后记\"><a href=\"#后记\"></a>后记</h2>\n<p>实际上如果你是 Telegram 的用户, 我更推荐你直接订阅 Telegram 频道, 作为一个即时消息应用来说, 它的体验十分优秀.</p>\n<p>如果你是阅读器用户, 我更推荐你仅订阅博客, 剪贴和笔记. 社交网络中的东西过于碎片化, 应该将它放在每天的社交事务中单独处理, 使用 Fediverse 或 Nostr 账户关注的话, 阅读和互动会更加方便, 这也是社交网络原本的目的.</p>\n","content_text":"前言 回忆多年前的一些经历, 我发现导致我现在对陈年旧事和人健忘的主要原因是我没有记录的习惯, 而我自己又不擅长记忆没有条理的东西, 如果一件事物没有能成体系地放在我的记忆里, 并且缺乏回忆和重复来对抗遗忘曲线, 那大概最快会在半年之后快速深埋在「记忆宫殿」的深处. 随时记下想法是好的, 定期回顾想法也是必要的. 至少能发现自己一时兴起脑子里冒出来的东西和做梦差不多都有点没头没尾的. 复核自己用文字记录下的念头也至少能查出很多错别字和病句... 脱离社交网络的几年之间, 我也忘记了很多很多事情, 可能和我的习惯有关. 但我觉得主要问题在于没有记录的地方, 即使真的记下, 后来也因为各种原因被永远遗失了. 整理纸和笔真的不比备份和归档数据简单. 有些人和事一旦进入到记忆里被 &quot;归档&quot; 后, 如果没有第二次回忆的机会, 那么很可能就会被逐渐埋得越来越深, 这种状态下的记忆, 已经和遗忘没有什么区别了, 就像失忆的人一样. 而我们这些正常人, 只不过是还能记得重要的人和事罢了. —— https://ditto.pub/@blog.cxplay.org/~ 这些事情, 并不是真正被遗忘了, 而是已经进入了一种和遗忘没有差别的状态, 日后如果需要这部分记忆还能够通过回忆来找回. 但是可惜, 找回的过程如果没有有价值的「记录点」, 将会是非常费时费力(脑力或是体力)的事情. 如果它是一种技能, 那么找回来的消耗可能很接近甚至大于重新学习一遍的消耗, 特别是「没有成体系的」记忆. 而建立「记录点」的最好办法实际上就是写作, 于是我会把我自己想要记录的, 并且可以公开的东西全部放在公共网络上, 而作为我日后回忆起完整事件的一个记录点. 可能会有人说「什么都往网上发, 是不是有暴露癖啊?」. 确实, 我在有这个想法之前, 至少四年之前就深刻地认识到了这个问题, 所以作为读者的你并不用担心我会发什么私密的, 不可告人的东西. 我能公开出来的东西, 我会直接站在能够看到这些内容的人或者机器的角度考虑, 我会发的, 都是我可以给所有人看到的东西, 如果不是所有人能够看到, 但你能直接看到, 那么至少也是能够给你看的, 所以不必抱有这种想法. 但如果你打算把这些东西转发给其他人, 还请和我一样请慎重考虑. 作为「中文互联网」的一位普通网民, 我希望能够通过这种记录 &quot;流水账&quot; 的方式填充一下匮乏的中文网络, 至于价值和意义? 我不会自诩这些东西能够产生可以被「量化」的价值, 我也不会雇佣别人(或者机器)把这些东西塞到你的眼前, 更不会奢望就此能改善环境或使他人受益; 如果你喜欢, 请随意浏览和互动(包括发表有价值的批评), 如果你不喜欢, 请把我的网址加入到你的广告拦截器中. 最起码的, 我不会用大量毫无价值的 AIGC 来污染我的地盘. 所有 RSS/Atom 地址 推荐使用的顺序从上往下排序, 各条目会有单独的使用建议. 博客 https://blog.cxplay.org Atom Feed: https://blog.cxplay.org/atom.xml RSS2 Feed: https://blog.cxplay.org/rss2.xml JSON Feed: https://blog.cxplay.org/feed.json 全文输出, 阅读体验优秀, 推荐刷新频率: 1 天. 博客是我用来记录基本成体系的内容, 文章字数大多在 1 千字以上, 部分超过 1 万字. 使用 Hexo 博客框架, Butterfly 主题, 以及经过自定义调整适应于主题样式的 Feed 生成插件. 剪贴 主站: https://clip.cx.ms/ Nostr: https://ditto.pub/@clip.cx.ms 主站 Atom Feed : https://clip.cx.ms/feed Nostr Atom Feed: https://nostr.cxplay.org/npub18dcx3xj3zg457k8kxkfmr03a0ltjhsq046tnhdq097m5ms0r284sc0sy5z.rss?cache=public 全文输出, 阅读体验良好(极少部分条目输出可能不完整), 推荐刷新频率: 1 小时. 剪贴是我用来剪藏网络文章和代码片段的地方, 还会包含一些速记. 文章字数大多在 800 字左右. 主站用于长篇剪藏, Nostr 账户用于短篇文章剪藏. 使用自托管 Nobelium 服务端渲染生成, Notion 作为 CMS, 以及经过自定义的 Feed 生成组件. Nostr 部分使用 njump 自托管, 以及经过自定义的 Feed 输出组件. 笔记 https://sir.social/@cxplay Fediverse 地址: cxplay@sir.social RSS Feed: https://sir.social/@cxplay/feed.rss 全文输出, 阅读体验良好, 推荐刷新频率: 6 小时. 笔记是我用来记录短平快但又相比社交媒体贴文更具有记录价值的消息, 功能性是首要的目的, 内容量并不会超过剪贴和博客. 要注意的是, RSS Feed 只包含主帖, 不会包含点赞, 回复和转发. 这个账户也会作为一部分社交账户功能来使用, 主要与 Fediverse 居民进行互动. 使用 GoToSocial 自托管. 社交 https://cx.ms/nostr Atom Feed: https://nostr.cxplay.org/npub1gd8e0xfkylc7v8c5a6hkpj4gelwwcy99jt90lqjseqjj2t253s2s6ch58h.rss?cache=public 全文输出, 阅读体验一般, 推荐刷新频率: 1 小时. 由于存在较多的实验性问题, 不再推荐使用这个 Feed. 建议直接订阅频道以间接获取社交内容. Nostr 是我目前首选使用的社交网络, 因为它相比 Fediverse 更适合个人托管, 并且依然能够依靠「桥」与 Fediverse 互联. 这里主要记录我的日常吐槽, 所以内容十分碎片化, 实话说我不太推荐正常人用 RSS 阅读器浏览社交媒体的时间线. 使用 njump 自托管, 以及经过自定义的 Feed 输出组件. 频道 https://t.me/cxplayworld https://channel.cx.ms/ Atom Feed: https://rsshub.app/telegram/channel/cxplayworld RSS Feed: https://channel.cx.ms/rss.xml 全文输出(仅频道消息), 阅读体验良好, 推荐刷新频率: 1 小时. Telegram 频道目前是聚合了以上所有我的 Feed 推送的频道, 所以你也可以直接订阅频道直接获得所有内容的推送, 但是 Telegram 频道并不适合长篇文章的发布, 所以对于博客和剪贴只会推送链接(预览)和标题. 由 RSSHub 提供 Atom Feed; 自托管 BroadcastChannel 提供 RSS2 Feed. 后记 实际上如果你是 Telegram 的用户, 我更推荐你直接订阅 Telegram 频道, 作为一个即时消息应用来说, 它的体验十分优秀. 如果你是阅读器用户, 我更推荐你仅订阅博客, 剪贴和笔记. 社交网络中的东西过于碎片化, 应该将它放在每天的社交事务中单独处理, 使用 Fediverse 或 Nostr 账户关注的话, 阅读和互动会更加方便, 这也是社交网络原本的目的.","summary":"前言 回忆多年前的一些经历, 我发现导致我现在对陈年旧事和人健忘的主要原因是我没有记录的习惯, 而我自己又不擅长记忆没有条理的东西, 如果一件事物没有能成体系地放在我的记忆里, 并且缺乏回忆和重复来对抗遗忘曲线, 那大概最快会在半年之后快速深埋在「记忆宫殿」的深处. 随时记下想法是好的, 定期回顾想法也是必要的. 至少能发现自己一时兴起脑子里冒出来的东西和做梦差不多都有点没头没尾的. 复核自己用文字记录下的念头也至少能查出很多错别字和病句... 脱离社交网络的几年之间, 我也忘记了很多很多事情, 可能和我的习惯有关. 但我觉得主要问题在于没有记录的地方, 即使真的记下, 后来也因为各种原因被永远遗失了. 整理纸和笔真的不比备份和归档数据简单. 有些人和事一旦进入到记忆里被 &quot;归档&quot; 后, 如果没有第二次回忆的机会, 那么很可能就会被逐渐埋得越来越深, 这种状态下的记忆, 已经和遗忘没有什么区别了, 就像失忆的人一样. 而我们这些正常人, 只不过是还能记得重要的人和事罢了. —— https://ditto.pub/@blog.cxplay.org/~ 这些事情, 并不是真正被遗忘了, 而是已经进入了一种和遗忘没有差别的状态, 日后如果需要这部分记忆还能够通过回忆来找回. 但是可惜, 找回的过程如果没有有价值的「记录点」, 将会是非常费时费力(脑力或是体力)的事情. 如果它是一种技能, 那么找回来的消耗可能很接近甚至大于重新学习一遍的消耗, 特别是「没有成体系的」记忆. 而建立「记录点」的最好办法实际上就是写作, 于是我会把我自己想要记录的, 并且可以公开的东西全部放在公共网络上, 而作为我日后回忆起完整事件的一个记录点. 可能会有人说「什么都往网上发, 是不是有暴露癖啊?」. 确实, 我在有这个想法之前, 至少四年之前就深刻地认识到了这个问题, 所以作为读者的你并不用担心我会发什么私密的, 不可告人的东西. 我能公开出来的东西, 我会直接站在能够看到这些内容的人或者机器的角度考虑, 我会发的, 都是我可以给所有人看到的东西, 如果不是所有人能够看到, 但你能直接看到, 那么至少也是能够给你看的, 所以不必抱有这种想法. 但如果你打算把这些东西转发给其他人, 还请和我一样请慎重考虑. 作为「中文互联网」的一位普通网民, 我希望能够通过这种记录 &quot;流水账&quot; 的方式填充一下匮乏的中文网络, 至于价值和意义? 我不会自诩这些东西能够产生可以被「量化」的价值, 我也不会雇佣别人(或者机器)把这些东西塞到你的眼前, 更不会奢望就此能改善环境或使他人受益; 如果你喜欢, 请随意浏览和互动(包括发表有价值的批评), 如果你不喜欢, 请把我的网址加入到你的广告拦截器中. 最起码的, 我不会用大量毫无价值的 AIGC 来污染我的地盘. 所有 RSS/Atom 地址 推荐使用的顺序从上往下排序, 各条目会有单独的使用建议. 博客 https://blog.cxplay.org Atom Feed: https://blog.cxplay.org/atom.xml RSS2 Feed: https://blog.cxplay.org/rss2.xml JSON Feed: https://blog.cxplay.org/feed.json 全文输出, 阅读体验优秀, 推荐刷新频率: 1 天. 博客是我用来记录基本成体系的内容, 文章字数大多在 1 千字以上, 部分超过 1 万字. 使用 Hexo 博客框架, Butterfly 主题, 以及经过自定义调整适应于主题样式的 Feed 生成插件. 剪贴 主站: https://clip.cx.ms/ Nostr: https://ditto.pub/@clip.cx.ms 主站 Atom Feed : https://clip.cx.ms/feed Nostr Atom Feed: https://nostr.cxplay.org/npub18dcx3xj3zg457k8kxkfmr03a0ltjhsq046tnhdq097m5ms0r284sc0sy5z.rss?cache=public 全文输出, 阅读体验良好(极少部分条目输出可能不完整), 推荐刷新频率: 1 小时. 剪贴是我用来剪藏网络文章和代码片段的地方, 还会包含一些速记. 文章字数大多在 800 字左右. 主站用于长篇剪藏, Nostr 账户用于短篇文章剪藏. 使用自托管 Nobelium 服务端渲染生成, Notion 作为 CMS, 以及经过自定义的 Feed 生成组件. Nostr 部分使用 njump 自托管, 以及经过自定义的 Feed 输出组件. 笔记 https://sir.social/@cxplay Fediverse 地址: cxplay@sir.social RSS Feed: https://sir.social/@cxplay/feed.rss 全文输出, 阅读体验良好, 推荐刷新频率: 6 小时. 笔记是我用来记录短平快但又相比社交媒体贴文更具有记录价值的消息, 功能性是首要的目的, 内容量并不会超过剪贴和博客. 要注意的是, RSS Feed 只包含主帖, 不会包含点赞, 回复和转发. 这个账户也会作为一部分社交账户功能来使用, 主要与 Fediverse 居民进行互动. 使用 GoToSocial 自托管. 社交 https://cx.ms/nostr Atom Feed: https://nostr.cxplay.org/npub1gd8e0xfkylc7v8c5a6hkpj4gelwwcy99jt90lqjseqjj2t253s2s6ch58h.rss?cache=public 全文输出, 阅读体验一般, 推荐刷新频率: 1 小时. 由于存在较多的实验性问题, 不再推荐使用这个 Feed. 建议直接订阅频道以间接获取社交内容. Nostr 是我目前首选使用的社交网络, 因为它相比 Fediverse 更适合个人托管, 并且依然能够依靠「桥」与 Fediverse 互联. 这里主要记录我的日常吐槽, 所以内容十分碎片化, 实话说我不太推荐正常人用 RSS 阅读器浏览社交媒体的时间线. 使用 njump 自托管, 以及经过自定义的 Feed 输出组件. 频道 https://t.me/cxplayworld https://channel.cx.ms/ Atom Feed: https://rsshub.app/telegram/channel/cxplayworld RSS Feed: https://channel.cx.ms/rss.xml 全文输出(仅频道消息), 阅读体验良好, 推荐刷新频率: 1 小时. Telegram 频道目前是聚合了以上所有我的 Feed 推送的频道, 所以你也可以直接订阅频道直接获得所有内容的推送, 但是 Telegram 频道并不适合长篇文章的发布, 所以对于博客和剪贴只会推送链接(预览)和标题. 由 RSSHub 提供 Atom Feed; 自托管 BroadcastChannel 提供 RSS2 Feed. 后记 实际上如果你是 Telegram 的用户, 我更推荐你直接订阅 Telegram 频道, 作为一个即时消息应用来说, 它的体验十分优秀. 如果你是阅读器用户, 我更推荐你仅订阅博客, 剪贴和笔记. 社交网络中的东西过于碎片化, 应该将它放在每天的社交事务中单独处理, 使用 Fediverse 或 Nostr 账户关注的话, 阅读和互动会更加方便, 这也是社交网络原本的目的.","date_published":"2024-08-26T18:59:00.000Z","tags":["日记本","RSS"]},{"id":"https://blog.cxplay.org/works/avoid-cloudflare-worker-reverse-proxy-abuse-report-by-netcraft/","url":"https://blog.cxplay.org/works/avoid-cloudflare-worker-reverse-proxy-abuse-report-by-netcraft/","title":"关于如何避免 Cloudflare Worker 反向代理被 Netcraft 发 abuse 这件事","content_html":"<h2 id=\"前言\"><a href=\"#前言\"></a>前言</h2>\n<p>经常「滥用」Microsoft 365 和 Cloudflare 的朋友应该都知道, 要用 SharePoint 或者 OneDrive 直接当源储存是非常不稳定的, 有些时候下载速度还非常慢. 于是有人就想到了我们的「赛博菩萨」Cloudflare, 如果再反代源储存一层是不是就更好一点呢? 哎, 还真有一点效果, 但是不多, 起码能够缓解一下 Microsoft Graph API 的压力.</p>\n<p>但是根据 Cloudflare 的 2024 年安全报告:</p>\n<blockquote>\n<p>机器人方面，我们观察到的所有流量中，大约三分之一是自动化的，其中绝大多数（93%）不是来自 Cloudflare 已验证清单中的机器人，可能存在恶意。</p>\n<p>—— <a href=\"https://blog.cloudflare.com/zh-cn/application-security-report-2024-update-zh-cn/\">应用安全报告：2024 年更新</a></p>\n</blockquote>\n<p>这其中就有家「臭名昭著」的公司, 主业就是为自己的客户利用自动化程序扫描互联网上涉嫌仿冒自家客户站点的站点 —— <a href=\"https://www.netcraft.com/\">Netcraft</a>. 从客户角度来说, 这家公司实力强劲, 每天都能解决掉大量涉嫌欺诈的网站, 能够保护自己的顾客不被这些仿冒站点钓鱼攻击, 进而影响自己的业务声誉. 于是恰好 Microsoft 就是 Netcraft 的客户之一, 很多朋友在经过网络上的一些教程建立了自己的 SharePoint 反代之后几乎 100% 会被 Netcraft 照顾(如果没有, 那只是时间问题). 轻则 Cloudflare 把 Worker 端点标记成欺诈, 重则 Cloudflare 账号被封禁.</p>\n<blockquote>\n<p><a href=\"https://www.v2ex.com/t/901304\">Cloudflare 反代导致账户被暂停 - V2EX</a></p>\n</blockquote>\n<p>虽然鄙人很早就知道了这种事情, 并在反代的时候给 Worker 自定义域名端点上了鉴权, 但是今天却突然收到了 Cloudflare 转发来的来自 Netcraft 的举报:</p>\n<blockquote>\n<p><a href=\"https://nostr.cxplay.org/nevent1qqs9kpf2p40me4tpdz9k9n8x3w2054ylj62h39l8pkfn0yespkqdmaszypp5l9uexcnlreslznh27cx24r8aemqs5kfv4luz2ryz2ffd2jxp2y0dl5j\">关于我我用 Cloudflare Worker 反代 SharePoint ...</a></p>\n</blockquote>\n<p><img src=\"https://media.naeu.net/91c6f3042e093b4350e0eff929e21bfadb81cb51f37bc09efd1bc439ad0ee075.webp\" alt=\"一失足成千古恨 (然而并没有)\" loading='lazy'></p>\n<p>看到被举报的端点是默认端点我就懂了, 想都不用想一定是我当时认为万事俱备之后结果忘记关掉默认端点的访问了(草).</p>\n<blockquote>\n<p>计算一下时间, 这个反代端点建立于五个月前, 现在 Netcraft 来光顾其实也算得上是一种「尽职」了.</p>\n</blockquote>\n<p>所以有哪些办法避免自己的反代端点被 Netcraft 扫描然后发 abuse 呢?</p>\n<h2 id=\"端点鉴权\"><a href=\"#端点鉴权\"></a>端点鉴权</h2>\n<p>鉴权是最基本的方法, 可以直接在 Worker 项目的代码里面做, 也可以在 WAF 里面做, 当然也可以两个一起结合用. 主要的原理就是禁止空 <code>referer</code> 和不合法的 <code>referer</code> 标头.</p>\n<p>建议创建 Worker 项目之后参照下面的 <a href=\"#waf\">WAF 章节</a>立刻把默认路由禁用再修改代码.</p>\n<p>因为按照经验, Netcraft 的 abuse 会在你还在 Worker 在线编辑器里面调试项目代码的时候就发来给你, 所以这很可能这是 Microsoft 对请求自己的 <code>referer</code> 标头直接让 Netcraft 进行了扫描.</p>\n<p><img src=\"https://media.naeu.net/a205d223ed9839d05a766479d65ddd4994dd5967f4d7fbdcef5894898a901048.webp\" alt=\"关于写这篇文章的时候建演示 Worker 实例还在调试就收到了举报邮件这件事\" loading='lazy'></p>\n\n<h3 id=\"waf\"><a href=\"#waf\"></a>WAF</h3>\n<p>要用 Cloudflare WAF 首先就必须有自己的域名已经添加到了账号中, 把 Worker 路由到自己的域名之下, 第一步就是禁用默认的 worker.dev 路由:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/avoid-cloudflare-worker-reverse-proxy-abuse-report-by-netcraft/2024-08-13_135008.webp\" alt=\"在默认路由菜单可以关闭默认路由的访问\" loading='lazy'></p>\n<p>第二步就要精确指定自己要反代的路径, 类似这样的:</p>\n<pre class=\"language-text\"><code>https://sharepoint-example.cx.ms/sites/shared/_layouts/*\n</code></pre>\n<p><img src=\"https://i.cxplay.org/u/1/blog/avoid-cloudflare-worker-reverse-proxy-abuse-report-by-netcraft/2024-08-13_134458.webp\" alt=\"准确指定反代路径\" loading='lazy'></p>\n<p>一般来说, 通过这两步就差不多够了, 但为了保险一点你还可以加上第三步 <code>referer</code> 标头鉴权.</p>\n<p>在自定义路由对应的域中添加自定义 WAF 规则:</p>\n<pre class=\"language-text\"><code>(http.host eq &quot;sharepoint-example.cx.ms&quot; and http.referer eq &quot;&quot;) or (http.host eq &quot;sharepoint-example.cx.ms&quot; and not http.referer contains &quot;example.com&quot;) or (http.host eq &quot;sharepoint-example.cx.ms&quot; and not http.referer contains &quot;example.org&quot;)\n</code></pre>\n<p><img src=\"https://i.cxplay.org/u/1/blog/avoid-cloudflare-worker-reverse-proxy-abuse-report-by-netcraft/2024-08-13_140800.webp\" alt=\"阻止空 referrer 以及不来自自己网关的 referrer\" loading='lazy'></p>\n<h3 id=\"worker\"><a href=\"#worker\"></a>Worker</h3>\n<p>当然如果要说「我就是不用自己的域名」, 也是可以的, 毕竟仅仅是要 Worker 反代就要搞定一个域名是有点多此一举了.</p>\n<p>主要还是用 <code>referer</code> 标头在具体的 Worker 入口中做判断, 如果请求不包含你的网关请求来源的 <code>referer</code> 标头, 或者为空, 则直接拒绝响应, 返回 <code>http:403</code>:</p>\n<pre class=\"language-javascript\"><code>const allow_referrer = [&#x27;example.com&#x27;, &#x27;example.org&#x27;]\n\nconst referrer = request.headers.get(&#x27;referer&#x27;);\n\nif (!referrer || !allow_referrer.includes(referrer)) &#123;\n        response = new  Response(&#x27;Access denied: Not allowed referrer.&#x27;, &#123;\n            status: 403\n        &#125;);\n&#125;\n</code></pre>\n<p>把上面的加进目前最流行的那份, 完整的代码就是:</p>\n<pre class=\"language-javascript\"><code>// 你的 SharePoint 域名\nconst upstream = &#x27;*-my.sharepoint.com&#x27;\n\n// 你的 SharePoint 域名 (用于移动端)\nconst upstream_mobile = &#x27;*-my.sharepoint.com&#x27;\n\nconst upstream_path = &#x27;/&#x27;\n\n// 禁止访问的国家代码 (https://en.wikipedia.org/wiki/ISO_3166-1#Codes)\nconst blocked_region = [&#x27;KP&#x27;, &#x27;SY&#x27;, &#x27;PK&#x27;, &#x27;CU&#x27;]\n\n// 禁止访问的 IP 地址\nconst blocked_ip_address = [&#x27;0.0.0.0&#x27;, &#x27;127.0.0.1&#x27;]\n\n// 允许访问的 Referrer 标头\nconst allow_referrer = [&#x27;example.com&#x27;, &#x27;example.org&#x27;]\n\nconst https = true\n\nconst disable_cache = false\n\nconst replace_dict = &#123;\n    &#x27;$upstream&#x27;: &#x27;$custom_domain&#x27;,\n    &#x27;//sunpma.com&#x27;: &#x27;&#x27;\n&#125;\n\naddEventListener(&#x27;fetch&#x27;, event =&gt; &#123;\n    event.respondWith(fetchAndApply(event.request));\n&#125;)\n\nasync function fetchAndApply(request) &#123;\n    const region = request.headers.get(&#x27;cf-ipcountry&#x27;).toUpperCase();\n    const ip_address = request.headers.get(&#x27;cf-connecting-ip&#x27;);\n    const user_agent = request.headers.get(&#x27;user-agent&#x27;);\n    const referrer = request.headers.get(&#x27;referer&#x27;);\n\n    let response = null;\n    let url = new URL(request.url);\n    let url_hostname = url.hostname;\n\n    if (https == true) &#123;\n        url.protocol = &#x27;https:&#x27;;\n    &#125; else &#123;\n        url.protocol = &#x27;http:&#x27;;\n    &#125;\n\n    if (await device_status(user_agent)) &#123;\n        var upstream_domain = upstream;\n    &#125; else &#123;\n        var upstream_domain = upstream_mobile;\n    &#125;\n\n    url.host = upstream_domain;\n    if (url.pathname == &#x27;/&#x27;) &#123;\n        url.pathname = upstream_path;\n    &#125; else &#123;\n        url.pathname = upstream_path + url.pathname;\n    &#125;\n\n    if (blocked_region.includes(region)) &#123;\n        response = new Response(&#x27;Access denied: WorkersProxy is not available in your region yet.&#x27;, &#123;\n            status: 403\n        &#125;);\n    &#125; else if (blocked_ip_address.includes(ip_address)) &#123;\n        response = new Response(&#x27;Access denied: Your IP address is blocked by WorkersProxy.&#x27;, &#123;\n            status: 403\n        &#125;);\n    &#125; else if (!referrer || !allow_referrer.includes(referrer)) &#123;\n        response = new  Response(&#x27;Access denied: Not allowed referrer.&#x27;, &#123;\n            status: 403\n        &#125;);\n    &#125; else &#123;\n        let method = request.method;\n        let request_headers = request.headers;\n        let new_request_headers = new Headers(request_headers);\n\n        new_request_headers.set(&#x27;Host&#x27;, upstream_domain);\n        new_request_headers.set(&#x27;Referer&#x27;, url.protocol + &#x27;//&#x27; + url_hostname);\n\n        let original_response = await fetch(url.href, &#123;\n            method: method,\n            headers: new_request_headers\n        &#125;)\n\n        connection_upgrade = new_request_headers.get(&quot;Upgrade&quot;);\n        if (connection_upgrade &amp;&amp; connection_upgrade.toLowerCase() == &quot;websocket&quot;) &#123;\n            return original_response;\n        &#125;\n\n        let original_response_clone = original_response.clone();\n        let original_text = null;\n        let response_headers = original_response.headers;\n        let new_response_headers = new Headers(response_headers);\n        let status = original_response.status;\n\n        if (disable_cache) &#123;\n            new_response_headers.set(&#x27;Cache-Control&#x27;, &#x27;no-store&#x27;);\n        &#125;\n\n        new_response_headers.set(&#x27;access-control-allow-origin&#x27;, &#x27;*&#x27;);\n        new_response_headers.set(&#x27;access-control-allow-credentials&#x27;, true);\n        new_response_headers.delete(&#x27;content-security-policy&#x27;);\n        new_response_headers.delete(&#x27;content-security-policy-report-only&#x27;);\n        new_response_headers.delete(&#x27;clear-site-data&#x27;);\n\n        if (new_response_headers.get(&quot;x-pjax-url&quot;)) &#123;\n            new_response_headers.set(&quot;x-pjax-url&quot;, response_headers.get(&quot;x-pjax-url&quot;).replace(&quot;//&quot; + upstream_domain, &quot;//&quot; + url_hostname));\n        &#125;\n\n        const content_type = new_response_headers.get(&#x27;content-type&#x27;);\n        if (content_type != null &amp;&amp; content_type.includes(&#x27;text/html&#x27;) &amp;&amp; content_type.includes(&#x27;UTF-8&#x27;)) &#123;\n            original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname);\n        &#125; else &#123;\n            original_text = original_response_clone.body\n        &#125;\n\n        response = new Response(original_text, &#123;\n            status,\n            headers: new_response_headers\n        &#125;)\n    &#125;\n    return response;\n&#125;\n\nasync function replace_response_text(response, upstream_domain, host_name) &#123;\n    let text = await response.text()\n\n    var i, j;\n    for (i in replace_dict) &#123;\n        j = replace_dict[i]\n        if (i == &#x27;$upstream&#x27;) &#123;\n            i = upstream_domain\n        &#125; else if (i == &#x27;$custom_domain&#x27;) &#123;\n            i = host_name\n        &#125;\n\n        if (j == &#x27;$upstream&#x27;) &#123;\n            j = upstream_domain\n        &#125; else if (j == &#x27;$custom_domain&#x27;) &#123;\n            j = host_name\n        &#125;\n\n        let re = new RegExp(i, &#x27;g&#x27;)\n        text = text.replace(re, j);\n    &#125;\n    return text;\n&#125;\n\n\nasync function device_status(user_agent_info) &#123;\n    var agents = [&quot;Android&quot;, &quot;iPhone&quot;, &quot;SymbianOS&quot;, &quot;Windows Phone&quot;, &quot;iPad&quot;, &quot;iPod&quot;];\n    var flag = true;\n    for (var v = 0; v &lt; agents.length; v++) &#123;\n        if (user_agent_info.indexOf(agents[v]) &gt; 0) &#123;\n            flag = false;\n            break;\n        &#125;\n    &#125;\n    return flag;\n&#125;\n</code></pre>\n<h2 id=\"纯-waf-屏蔽-netcraft\"><a href=\"#纯-waf-屏蔽-netcraft\"></a>纯 WAF 屏蔽 Netcraft</h2>\n<blockquote>\n<p>实际并不推荐仅依靠这种办法, 后文会讲到.</p>\n</blockquote>\n<p>这种办法你只能用自定义域名路由 Worker 后才能使用. 一般来说, 扫描网络上的公共站点的机器人都会带有一个 <code>User-Agent</code> 表明自己的身份, 以便于站点管理员识别这部分流量. 实在不济, 机器人也都是用的固定 IP 或固定 ASN 的 IP, 一般就是对应代表了机器人扫描活动的那家公司, 所以一般也只需要在 WAF 中针对 ASN, IP 和 <code>User-Agent</code> 标头进行屏蔽就行了.</p>\n<blockquote>\n<p><a href=\"https://www.nodeseek.com/post-128645-1\">屏蔽违法扫描黑产：Netcraft，导致cf封号元凶</a></p>\n</blockquote>\n<p>Netcraft 作为一个「臭名昭著」的网络安全服务供应商, 自然是有可以查询的 ASN, IP 和部分 <code>User-Agent</code>:</p>\n<ul>\n<li>\n<p>ASN: <a href=\"https://bgp.he.net/AS212329\"><code>AS212329</code></a></p>\n</li>\n<li>\n<p>IP: <a href=\"https://gist.github.com/ozuma/fb21ab0f7143579b1f2794f4af746fb2\">IP address block list from PhishKit.</a></p>\n<pre class=\"language-text\"><code>#  NETCRAFT IP RANGES\n\n194.52.68.0-194.52.68.255\n194.72.238.0-194.72.238.255\n83.138.182.72-83.138.182.79\n83.138.189.96-83.138.189.103\n81.91.240.0-81.91.255.255\n89.36.24.0-89.36.31.255\n83.222.232.216-83.222.232.218\n184.172.0.0-184.173.255.255\n</code></pre>\n</li>\n<li>\n<p><code>User-Agent</code>: <a href=\"https://useragents.io/explore/platforms/unknown/maker/netcraft-ltd-c93\">Netcraft Ltd. | UserAgents.io</a></p>\n<pre class=\"language-text\"><code>Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0; +info@netcraft.com)    \nMozilla/4.0 (compatible; Netcraft Web Server Survey)\nMozilla/5.0 (compatible; NetcraftSurveyAgent/1.0/cc-prepass-https; info@netcraft.com)\nNetcraft SSL Server Survey - contact info@netcraft.com\nNETCRAFT\nMozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Netcraft SSL Server Survey - contact info@netcraft.com)\n</code></pre>\n</li>\n</ul>\n<p>对应写成 Cloudflare 的 WAF 规则就是:</p>\n<pre class=\"language-text\"><code>(ip.geoip.asnum eq 212329) or (ip.src in &#123;194.52.68.0/24 194.72.238.0/24 83.138.182.72/29 83.138.189.96/29 81.91.240.0/24 89.36.24.0/24 83.222.232.216/30 184.172.0.0/16&#125;) or (http.user_agent eq &quot;Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0; +info@netcraft.com)&quot;) or (http.user_agent eq &quot;Mozilla/4.0 (compatible; Netcraft Web Server Survey)&quot;) or (http.user_agent eq &quot;Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0/cc-prepass-https; info@netcraft.com)&quot;) or (http.user_agent eq &quot;Netcraft SSL Server Survey - contact info@netcraft.com&quot;) or (http.user_agent eq &quot;NETCRAFT&quot;) or (http.user_agent eq &quot;Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Netcraft SSL Server Survey - contact info@netcraft.com)&quot;)\n</code></pre>\n<p><img src=\"https://i.cxplay.org/u/1/blog/avoid-cloudflare-worker-reverse-proxy-abuse-report-by-netcraft/2024-08-13_153600.webp\" alt=\"WAF 规则\" loading='lazy'></p>\n<hr>\n<p>看起来全站屏蔽了就完事了? 但是, 我要说但是了.</p>\n<p>Netcraft 虽然是自己会主动用自家的 IP 去扫描站点, &quot;也许&quot; 会给你带上一眼明白的 <code>User-Agent</code> 给你知道这就是 Netcraft 的流量. 但 Netcraft 也是接受众包的一家网络安全服务提供商, 也就是任何人都可以在 Netcraft 举报滥用, 然后 Netcraft 直接发给自己的客户或者网络服务商.</p>\n<p>这些众包成员有大部分都是其他机构, 大大小小的都有:</p>\n<blockquote>\n<p><a href=\"https://report.netcraft.com/stats/leaderboard/top\">Stats - Top Reporters Leaderboard</a></p>\n</blockquote>\n<p>前几天我在我的其他站的防火墙里面就发现了其中的一家众包成员(甚至还是非盈利项目):</p>\n<blockquote>\n<p><a href=\"https://scanner.ducks.party/\">About ducks.party Internet Scanner Bot</a></p>\n</blockquote>\n<p>除掉这些众包成员, 我们也<strong>没办法排除掉人工去检查然后报告给类似 Netcraft 的机构的人的流量</strong>, 所以我的建议依旧是做好最基本的鉴权才是硬道理. 毕竟换个位置思考一下, 如果你是站长, 而你的站突然被人莫名其妙反代了, 甚至<strong>可能</strong>被用来钓鱼你站内的用户, 你会怎么办?</p>\n<p>虽然我们只是反代一个自己的 SharePoint, 一开始就没有打算拿来做钓鱼这类完全欺诈的真滥用, 但是在没有做鉴权之前, 允许反代了整个 SharePoint 根路径直接暴露在公网上, 我们也办法就这样声明自己不是拿来做欺诈用途的, 毕竟在第三方看来, 这就和欺诈没什么两样.</p>\n","content_text":"前言 经常「滥用」Microsoft 365 和 Cloudflare 的朋友应该都知道, 要用 SharePoint 或者 OneDrive 直接当源储存是非常不稳定的, 有些时候下载速度还非常慢. 于是有人就想到了我们的「赛博菩萨」Cloudflare, 如果再反代源储存一层是不是就更好一点呢? 哎, 还真有一点效果, 但是不多, 起码能够缓解一下 Microsoft Graph API 的压力. 但是根据 Cloudflare 的 2024 年安全报告: 机器人方面，我们观察到的所有流量中，大约三分之一是自动化的，其中绝大多数（93%）不是来自 Cloudflare 已验证清单中的机器人，可能存在恶意。 —— 应用安全报告：2024 年更新 这其中就有家「臭名昭著」的公司, 主业就是为自己的客户利用自动化程序扫描互联网上涉嫌仿冒自家客户站点的站点 —— Netcraft. 从客户角度来说, 这家公司实力强劲, 每天都能解决掉大量涉嫌欺诈的网站, 能够保护自己的顾客不被这些仿冒站点钓鱼攻击, 进而影响自己的业务声誉. 于是恰好 Microsoft 就是 Netcraft 的客户之一, 很多朋友在经过网络上的一些教程建立了自己的 SharePoint 反代之后几乎 100% 会被 Netcraft 照顾(如果没有, 那只是时间问题). 轻则 Cloudflare 把 Worker 端点标记成欺诈, 重则 Cloudflare 账号被封禁. Cloudflare 反代导致账户被暂停 - V2EX 虽然鄙人很早就知道了这种事情, 并在反代的时候给 Worker 自定义域名端点上了鉴权, 但是今天却突然收到了 Cloudflare 转发来的来自 Netcraft 的举报: 关于我我用 Cloudflare Worker 反代 SharePoint ... 看到被举报的端点是默认端点我就懂了, 想都不用想一定是我当时认为万事俱备之后结果忘记关掉默认端点的访问了(草). 计算一下时间, 这个反代端点建立于五个月前, 现在 Netcraft 来光顾其实也算得上是一种「尽职」了. 所以有哪些办法避免自己的反代端点被 Netcraft 扫描然后发 abuse 呢? 端点鉴权 鉴权是最基本的方法, 可以直接在 Worker 项目的代码里面做, 也可以在 WAF 里面做, 当然也可以两个一起结合用. 主要的原理就是禁止空 referer 和不合法的 referer 标头. 建议创建 Worker 项目之后参照下面的 WAF 章节立刻把默认路由禁用再修改代码. 因为按照经验, Netcraft 的 abuse 会在你还在 Worker 在线编辑器里面调试项目代码的时候就发来给你, 所以这很可能这是 Microsoft 对请求自己的 referer 标头直接让 Netcraft 进行了扫描. WAF 要用 Cloudflare WAF 首先就必须有自己的域名已经添加到了账号中, 把 Worker 路由到自己的域名之下, 第一步就是禁用默认的 worker.dev 路由: 第二步就要精确指定自己要反代的路径, 类似这样的: 1https://sharepoint-example.cx.ms/sites/shared/_layouts/* 一般来说, 通过这两步就差不多够了, 但为了保险一点你还可以加上第三步 referer 标头鉴权. 在自定义路由对应的域中添加自定义 WAF 规则: 1(http.host eq &quot;sharepoint-example.cx.ms&quot; and http.referer eq &quot;&quot;) or (http.host eq &quot;sharepoint-example.cx.ms&quot; and not http.referer contains &quot;example.com&quot;) or (http.host eq &quot;sharepoint-example.cx.ms&quot; and not http.referer contains &quot;example.org&quot;) Worker 当然如果要说「我就是不用自己的域名」, 也是可以的, 毕竟仅仅是要 Worker 反代就要搞定一个域名是有点多此一举了. 主要还是用 referer 标头在具体的 Worker 入口中做判断, 如果请求不包含你的网关请求来源的 referer 标头, 或者为空, 则直接拒绝响应, 返回 http:403: 123456789const allow_referrer = [&#x27;example.com&#x27;, &#x27;example.org&#x27;]const referrer = request.headers.get(&#x27;referer&#x27;);if (!referrer || !allow_referrer.includes(referrer)) &#123; response = new Response(&#x27;Access denied: Not allowed referrer.&#x27;, &#123; status: 403 &#125;);&#125; 把上面的加进目前最流行的那份, 完整的代码就是: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160// 你的 SharePoint 域名const upstream = &#x27;*-my.sharepoint.com&#x27;// 你的 SharePoint 域名 (用于移动端)const upstream_mobile = &#x27;*-my.sharepoint.com&#x27;const upstream_path = &#x27;/&#x27;// 禁止访问的国家代码 (https://en.wikipedia.org/wiki/ISO_3166-1#Codes)const blocked_region = [&#x27;KP&#x27;, &#x27;SY&#x27;, &#x27;PK&#x27;, &#x27;CU&#x27;]// 禁止访问的 IP 地址const blocked_ip_address = [&#x27;0.0.0.0&#x27;, &#x27;127.0.0.1&#x27;]// 允许访问的 Referrer 标头const allow_referrer = [&#x27;example.com&#x27;, &#x27;example.org&#x27;]const https = trueconst disable_cache = falseconst replace_dict = &#123; &#x27;$upstream&#x27;: &#x27;$custom_domain&#x27;, &#x27;//sunpma.com&#x27;: &#x27;&#x27;&#125;addEventListener(&#x27;fetch&#x27;, event =&gt; &#123; event.respondWith(fetchAndApply(event.request));&#125;)async function fetchAndApply(request) &#123; const region = request.headers.get(&#x27;cf-ipcountry&#x27;).toUpperCase(); const ip_address = request.headers.get(&#x27;cf-connecting-ip&#x27;); const user_agent = request.headers.get(&#x27;user-agent&#x27;); const referrer = request.headers.get(&#x27;referer&#x27;); let response = null; let url = new URL(request.url); let url_hostname = url.hostname; if (https == true) &#123; url.protocol = &#x27;https:&#x27;; &#125; else &#123; url.protocol = &#x27;http:&#x27;; &#125; if (await device_status(user_agent)) &#123; var upstream_domain = upstream; &#125; else &#123; var upstream_domain = upstream_mobile; &#125; url.host = upstream_domain; if (url.pathname == &#x27;/&#x27;) &#123; url.pathname = upstream_path; &#125; else &#123; url.pathname = upstream_path + url.pathname; &#125; if (blocked_region.includes(region)) &#123; response = new Response(&#x27;Access denied: WorkersProxy is not available in your region yet.&#x27;, &#123; status: 403 &#125;); &#125; else if (blocked_ip_address.includes(ip_address)) &#123; response = new Response(&#x27;Access denied: Your IP address is blocked by WorkersProxy.&#x27;, &#123; status: 403 &#125;); &#125; else if (!referrer || !allow_referrer.includes(referrer)) &#123; response = new Response(&#x27;Access denied: Not allowed referrer.&#x27;, &#123; status: 403 &#125;); &#125; else &#123; let method = request.method; let request_headers = request.headers; let new_request_headers = new Headers(request_headers); new_request_headers.set(&#x27;Host&#x27;, upstream_domain); new_request_headers.set(&#x27;Referer&#x27;, url.protocol + &#x27;//&#x27; + url_hostname); let original_response = await fetch(url.href, &#123; method: method, headers: new_request_headers &#125;) connection_upgrade = new_request_headers.get(&quot;Upgrade&quot;); if (connection_upgrade &amp;&amp; connection_upgrade.toLowerCase() == &quot;websocket&quot;) &#123; return original_response; &#125; let original_response_clone = original_response.clone(); let original_text = null; let response_headers = original_response.headers; let new_response_headers = new Headers(response_headers); let status = original_response.status; if (disable_cache) &#123; new_response_headers.set(&#x27;Cache-Control&#x27;, &#x27;no-store&#x27;); &#125; new_response_headers.set(&#x27;access-control-allow-origin&#x27;, &#x27;*&#x27;); new_response_headers.set(&#x27;access-control-allow-credentials&#x27;, true); new_response_headers.delete(&#x27;content-security-policy&#x27;); new_response_headers.delete(&#x27;content-security-policy-report-only&#x27;); new_response_headers.delete(&#x27;clear-site-data&#x27;); if (new_response_headers.get(&quot;x-pjax-url&quot;)) &#123; new_response_headers.set(&quot;x-pjax-url&quot;, response_headers.get(&quot;x-pjax-url&quot;).replace(&quot;//&quot; + upstream_domain, &quot;//&quot; + url_hostname)); &#125; const content_type = new_response_headers.get(&#x27;content-type&#x27;); if (content_type != null &amp;&amp; content_type.includes(&#x27;text/html&#x27;) &amp;&amp; content_type.includes(&#x27;UTF-8&#x27;)) &#123; original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname); &#125; else &#123; original_text = original_response_clone.body &#125; response = new Response(original_text, &#123; status, headers: new_response_headers &#125;) &#125; return response;&#125;async function replace_response_text(response, upstream_domain, host_name) &#123; let text = await response.text() var i, j; for (i in replace_dict) &#123; j = replace_dict[i] if (i == &#x27;$upstream&#x27;) &#123; i = upstream_domain &#125; else if (i == &#x27;$custom_domain&#x27;) &#123; i = host_name &#125; if (j == &#x27;$upstream&#x27;) &#123; j = upstream_domain &#125; else if (j == &#x27;$custom_domain&#x27;) &#123; j = host_name &#125; let re = new RegExp(i, &#x27;g&#x27;) text = text.replace(re, j); &#125; return text;&#125;async function device_status(user_agent_info) &#123; var agents = [&quot;Android&quot;, &quot;iPhone&quot;, &quot;SymbianOS&quot;, &quot;Windows Phone&quot;, &quot;iPad&quot;, &quot;iPod&quot;]; var flag = true; for (var v = 0; v &lt; agents.length; v++) &#123; if (user_agent_info.indexOf(agents[v]) &gt; 0) &#123; flag = false; break; &#125; &#125; return flag;&#125; 纯 WAF 屏蔽 Netcraft 实际并不推荐仅依靠这种办法, 后文会讲到. 这种办法你只能用自定义域名路由 Worker 后才能使用. 一般来说, 扫描网络上的公共站点的机器人都会带有一个 User-Agent 表明自己的身份, 以便于站点管理员识别这部分流量. 实在不济, 机器人也都是用的固定 IP 或固定 ASN 的 IP, 一般就是对应代表了机器人扫描活动的那家公司, 所以一般也只需要在 WAF 中针对 ASN, IP 和 User-Agent 标头进行屏蔽就行了. 屏蔽违法扫描黑产：Netcraft，导致cf封号元凶 Netcraft 作为一个「臭名昭著」的网络安全服务供应商, 自然是有可以查询的 ASN, IP 和部分 User-Agent: ASN: AS212329 IP: IP address block list from PhishKit. 12345678910# NETCRAFT IP RANGES194.52.68.0-194.52.68.255194.72.238.0-194.72.238.25583.138.182.72-83.138.182.7983.138.189.96-83.138.189.10381.91.240.0-81.91.255.25589.36.24.0-89.36.31.25583.222.232.216-83.222.232.218184.172.0.0-184.173.255.255 User-Agent: Netcraft Ltd. | UserAgents.io 123456Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0; +info@netcraft.com) Mozilla/4.0 (compatible; Netcraft Web Server Survey)Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0/cc-prepass-https; info@netcraft.com)Netcraft SSL Server Survey - contact info@netcraft.comNETCRAFTMozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Netcraft SSL Server Survey - contact info@netcraft.com) 对应写成 Cloudflare 的 WAF 规则就是: 1(ip.geoip.asnum eq 212329) or (ip.src in &#123;194.52.68.0/24 194.72.238.0/24 83.138.182.72/29 83.138.189.96/29 81.91.240.0/24 89.36.24.0/24 83.222.232.216/30 184.172.0.0/16&#125;) or (http.user_agent eq &quot;Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0; +info@netcraft.com)&quot;) or (http.user_agent eq &quot;Mozilla/4.0 (compatible; Netcraft Web Server Survey)&quot;) or (http.user_agent eq &quot;Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0/cc-prepass-https; info@netcraft.com)&quot;) or (http.user_agent eq &quot;Netcraft SSL Server Survey - contact info@netcraft.com&quot;) or (http.user_agent eq &quot;NETCRAFT&quot;) or (http.user_agent eq &quot;Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Netcraft SSL Server Survey - contact info@netcraft.com)&quot;) 看起来全站屏蔽了就完事了? 但是, 我要说但是了. Netcraft 虽然是自己会主动用自家的 IP 去扫描站点, &quot;也许&quot; 会给你带上一眼明白的 User-Agent 给你知道这就是 Netcraft 的流量. 但 Netcraft 也是接受众包的一家网络安全服务提供商, 也就是任何人都可以在 Netcraft 举报滥用, 然后 Netcraft 直接发给自己的客户或者网络服务商. 这些众包成员有大部分都是其他机构, 大大小小的都有: Stats - Top Reporters Leaderboard 前几天我在我的其他站的防火墙里面就发现了其中的一家众包成员(甚至还是非盈利项目): About ducks.party Internet Scanner Bot 除掉这些众包成员, 我们也没办法排除掉人工去检查然后报告给类似 Netcraft 的机构的人的流量, 所以我的建议依旧是做好最基本的鉴权才是硬道理. 毕竟换个位置思考一下, 如果你是站长, 而你的站突然被人莫名其妙反代了, 甚至可能被用来钓鱼你站内的用户, 你会怎么办? 虽然我们只是反代一个自己的 SharePoint, 一开始就没有打算拿来做钓鱼这类完全欺诈的真滥用, 但是在没有做鉴权之前, 允许反代了整个 SharePoint 根路径直接暴露在公网上, 我们也办法就这样声明自己不是拿来做欺诈用途的, 毕竟在第三方看来, 这就和欺诈没什么两样.","summary":"前言 经常「滥用」Microsoft 365 和 Cloudflare 的朋友应该都知道, 要用 SharePoint 或者 OneDrive 直接当源储存是非常不稳定的, 有些时候下载速度还非常慢. 于是有人就想到了我们的「赛博菩萨」Cloudflare, 如果再反代源储存一层是不是就更好一点呢? 哎, 还真有一点效果, 但是不多, 起码能够缓解一下 Microsoft Graph API 的压力. 但是根据 Cloudflare 的 2024 年安全报告: 机器人方面，我们观察到的所有流量中，大约三分之一是自动化的，其中绝大多数（93%）不是来自 Cloudflare 已验证清单中的机器人，可能存在恶意。 —— 应用安全报告：2024 年更新 这其中就有家「臭名昭著」的公司, 主业就是为自己的客户利用自动化程序扫描互联网上涉嫌仿冒自家客户站点的站点 —— Netcraft. 从客户角度来说, 这家公司实力强劲, 每天都能解决掉大量涉嫌欺诈的网站, 能够保护自己的顾客不被这些仿冒站点钓鱼攻击, 进而影响自己的业务声誉. 于是恰好 Microsoft 就是 Netcraft 的客户之一, 很多朋友在经过网络上的一些教程建立了自己的 SharePoint 反代之后几乎 100% 会被 Netcraft 照顾(如果没有, 那只是时间问题). 轻则 Cloudflare 把 Worker 端点标记成欺诈, 重则 Cloudflare 账号被封禁. Cloudflare 反代导致账户被暂停 - V2EX 虽然鄙人很早就知道了这种事情, 并在反代的时候给 Worker 自定义域名端点上了鉴权, 但是今天却突然收到了 Cloudflare 转发来的来自 Netcraft 的举报: 关于我我用 Cloudflare Worker 反代 SharePoint ... 看到被举报的端点是默认端点我就懂了, 想都不用想一定是我当时认为万事俱备之后结果忘记关掉默认端点的访问了(草). 计算一下时间, 这个反代端点建立于五个月前, 现在 Netcraft 来光顾其实也算得上是一种「尽职」了. 所以有哪些办法避免自己的反代端点被 Netcraft 扫描然后发 abuse 呢? 端点鉴权 鉴权是最基本的方法, 可以直接在 Worker 项目的代码里面做, 也可以在 WAF 里面做, 当然也可以两个一起结合用. 主要的原理就是禁止空 referer 和不合法的 referer 标头. 建议创建 Worker 项目之后参照下面的 WAF 章节立刻把默认路由禁用再修改代码. 因为按照经验, Netcraft 的 abuse 会在你还在 Worker 在线编辑器里面调试项目代码的时候就发来给你, 所以这很可能这是 Microsoft 对请求自己的 referer 标头直接让 Netcraft 进行了扫描. WAF 要用 Cloudflare WAF 首先就必须有自己的域名已经添加到了账号中, 把 Worker 路由到自己的域名之下, 第一步就是禁用默认的 worker.dev 路由: 第二步就要精确指定自己要反代的路径, 类似这样的: 1https://sharepoint-example.cx.ms/sites/shared/_layouts/* 一般来说, 通过这两步就差不多够了, 但为了保险一点你还可以加上第三步 referer 标头鉴权. 在自定义路由对应的域中添加自定义 WAF 规则: 1(http.host eq &quot;sharepoint-example.cx.ms&quot; and http.referer eq &quot;&quot;) or (http.host eq &quot;sharepoint-example.cx.ms&quot; and not http.referer contains &quot;example.com&quot;) or (http.host eq &quot;sharepoint-example.cx.ms&quot; and not http.referer contains &quot;example.org&quot;) Worker 当然如果要说「我就是不用自己的域名」, 也是可以的, 毕竟仅仅是要 Worker 反代就要搞定一个域名是有点多此一举了. 主要还是用 referer 标头在具体的 Worker 入口中做判断, 如果请求不包含你的网关请求来源的 referer 标头, 或者为空, 则直接拒绝响应, 返回 http:403: 123456789const allow_referrer = [&#x27;example.com&#x27;, &#x27;example.org&#x27;]const referrer = request.headers.get(&#x27;referer&#x27;);if (!referrer || !allow_referrer.includes(referrer)) &#123; response = new Response(&#x27;Access denied: Not allowed referrer.&#x27;, &#123; status: 403 &#125;);&#125; 把上面的加进目前最流行的那份, 完整的代码就是: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160// 你的 SharePoint 域名const upstream = &#x27;*-my.sharepoint.com&#x27;// 你的 SharePoint 域名 (用于移动端)const upstream_mobile = &#x27;*-my.sharepoint.com&#x27;const upstream_path = &#x27;/&#x27;// 禁止访问的国家代码 (https://en.wikipedia.org/wiki/ISO_3166-1#Codes)const blocked_region = [&#x27;KP&#x27;, &#x27;SY&#x27;, &#x27;PK&#x27;, &#x27;CU&#x27;]// 禁止访问的 IP 地址const blocked_ip_address = [&#x27;0.0.0.0&#x27;, &#x27;127.0.0.1&#x27;]// 允许访问的 Referrer 标头const allow_referrer = [&#x27;example.com&#x27;, &#x27;example.org&#x27;]const https = trueconst disable_cache = falseconst replace_dict = &#123; &#x27;$upstream&#x27;: &#x27;$custom_domain&#x27;, &#x27;//sunpma.com&#x27;: &#x27;&#x27;&#125;addEventListener(&#x27;fetch&#x27;, event =&gt; &#123; event.respondWith(fetchAndApply(event.request));&#125;)async function fetchAndApply(request) &#123; const region = request.headers.get(&#x27;cf-ipcountry&#x27;).toUpperCase(); const ip_address = request.headers.get(&#x27;cf-connecting-ip&#x27;); const user_agent = request.headers.get(&#x27;user-agent&#x27;); const referrer = request.headers.get(&#x27;referer&#x27;); let response = null; let url = new URL(request.url); let url_hostname = url.hostname; if (https == true) &#123; url.protocol = &#x27;https:&#x27;; &#125; else &#123; url.protocol = &#x27;http:&#x27;; &#125; if (await device_status(user_agent)) &#123; var upstream_domain = upstream; &#125; else &#123; var upstream_domain = upstream_mobile; &#125; url.host = upstream_domain; if (url.pathname == &#x27;/&#x27;) &#123; url.pathname = upstream_path; &#125; else &#123; url.pathname = upstream_path + url.pathname; &#125; if (blocked_region.includes(region)) &#123; response = new Response(&#x27;Access denied: WorkersProxy is not available in your region yet.&#x27;, &#123; status: 403 &#125;); &#125; else if (blocked_ip_address.includes(ip_address)) &#123; response = new Response(&#x27;Access denied: Your IP address is blocked by WorkersProxy.&#x27;, &#123; status: 403 &#125;); &#125; else if (!referrer || !allow_referrer.includes(referrer)) &#123; response = new Response(&#x27;Access denied: Not allowed referrer.&#x27;, &#123; status: 403 &#125;); &#125; else &#123; let method = request.method; let request_headers = request.headers; let new_request_headers = new Headers(request_headers); new_request_headers.set(&#x27;Host&#x27;, upstream_domain); new_request_headers.set(&#x27;Referer&#x27;, url.protocol + &#x27;//&#x27; + url_hostname); let original_response = await fetch(url.href, &#123; method: method, headers: new_request_headers &#125;) connection_upgrade = new_request_headers.get(&quot;Upgrade&quot;); if (connection_upgrade &amp;&amp; connection_upgrade.toLowerCase() == &quot;websocket&quot;) &#123; return original_response; &#125; let original_response_clone = original_response.clone(); let original_text = null; let response_headers = original_response.headers; let new_response_headers = new Headers(response_headers); let status = original_response.status; if (disable_cache) &#123; new_response_headers.set(&#x27;Cache-Control&#x27;, &#x27;no-store&#x27;); &#125; new_response_headers.set(&#x27;access-control-allow-origin&#x27;, &#x27;*&#x27;); new_response_headers.set(&#x27;access-control-allow-credentials&#x27;, true); new_response_headers.delete(&#x27;content-security-policy&#x27;); new_response_headers.delete(&#x27;content-security-policy-report-only&#x27;); new_response_headers.delete(&#x27;clear-site-data&#x27;); if (new_response_headers.get(&quot;x-pjax-url&quot;)) &#123; new_response_headers.set(&quot;x-pjax-url&quot;, response_headers.get(&quot;x-pjax-url&quot;).replace(&quot;//&quot; + upstream_domain, &quot;//&quot; + url_hostname)); &#125; const content_type = new_response_headers.get(&#x27;content-type&#x27;); if (content_type != null &amp;&amp; content_type.includes(&#x27;text/html&#x27;) &amp;&amp; content_type.includes(&#x27;UTF-8&#x27;)) &#123; original_text = await replace_response_text(original_response_clone, upstream_domain, url_hostname); &#125; else &#123; original_text = original_response_clone.body &#125; response = new Response(original_text, &#123; status, headers: new_response_headers &#125;) &#125; return response;&#125;async function replace_response_text(response, upstream_domain, host_name) &#123; let text = await response.text() var i, j; for (i in replace_dict) &#123; j = replace_dict[i] if (i == &#x27;$upstream&#x27;) &#123; i = upstream_domain &#125; else if (i == &#x27;$custom_domain&#x27;) &#123; i = host_name &#125; if (j == &#x27;$upstream&#x27;) &#123; j = upstream_domain &#125; else if (j == &#x27;$custom_domain&#x27;) &#123; j = host_name &#125; let re = new RegExp(i, &#x27;g&#x27;) text = text.replace(re, j); &#125; return text;&#125;async function device_status(user_agent_info) &#123; var agents = [&quot;Android&quot;, &quot;iPhone&quot;, &quot;SymbianOS&quot;, &quot;Windows Phone&quot;, &quot;iPad&quot;, &quot;iPod&quot;]; var flag = true; for (var v = 0; v &lt; agents.length; v++) &#123; if (user_agent_info.indexOf(agents[v]) &gt; 0) &#123; flag = false; break; &#125; &#125; return flag;&#125; 纯 WAF 屏蔽 Netcraft 实际并不推荐仅依靠这种办法, 后文会讲到. 这种办法你只能用自定义域名路由 Worker 后才能使用. 一般来说, 扫描网络上的公共站点的机器人都会带有一个 User-Agent 表明自己的身份, 以便于站点管理员识别这部分流量. 实在不济, 机器人也都是用的固定 IP 或固定 ASN 的 IP, 一般就是对应代表了机器人扫描活动的那家公司, 所以一般也只需要在 WAF 中针对 ASN, IP 和 User-Agent 标头进行屏蔽就行了. 屏蔽违法扫描黑产：Netcraft，导致cf封号元凶 Netcraft 作为一个「臭名昭著」的网络安全服务供应商, 自然是有可以查询的 ASN, IP 和部分 User-Agent: ASN: AS212329 IP: IP address block list from PhishKit. 12345678910# NETCRAFT IP RANGES194.52.68.0-194.52.68.255194.72.238.0-194.72.238.25583.138.182.72-83.138.182.7983.138.189.96-83.138.189.10381.91.240.0-81.91.255.25589.36.24.0-89.36.31.25583.222.232.216-83.222.232.218184.172.0.0-184.173.255.255 User-Agent: Netcraft Ltd. | UserAgents.io 123456Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0; +info@netcraft.com) Mozilla/4.0 (compatible; Netcraft Web Server Survey)Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0/cc-prepass-https; info@netcraft.com)Netcraft SSL Server Survey - contact info@netcraft.comNETCRAFTMozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Netcraft SSL Server Survey - contact info@netcraft.com) 对应写成 Cloudflare 的 WAF 规则就是: 1(ip.geoip.asnum eq 212329) or (ip.src in &#123;194.52.68.0/24 194.72.238.0/24 83.138.182.72/29 83.138.189.96/29 81.91.240.0/24 89.36.24.0/24 83.222.232.216/30 184.172.0.0/16&#125;) or (http.user_agent eq &quot;Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0; +info@netcraft.com)&quot;) or (http.user_agent eq &quot;Mozilla/4.0 (compatible; Netcraft Web Server Survey)&quot;) or (http.user_agent eq &quot;Mozilla/5.0 (compatible; NetcraftSurveyAgent/1.0/cc-prepass-https; info@netcraft.com)&quot;) or (http.user_agent eq &quot;Netcraft SSL Server Survey - contact info@netcraft.com&quot;) or (http.user_agent eq &quot;NETCRAFT&quot;) or (http.user_agent eq &quot;Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Netcraft SSL Server Survey - contact info@netcraft.com)&quot;) 看起来全站屏蔽了就完事了? 但是, 我要说但是了. Netcraft 虽然是自己会主动用自家的 IP 去扫描站点, &quot;也许&quot; 会给你带上一眼明白的 User-Agent 给你知道这就是 Netcraft 的流量. 但 Netcraft 也是接受众包的一家网络安全服务提供商, 也就是任何人都可以在 Netcraft 举报滥用, 然后 Netcraft 直接发给自己的客户或者网络服务商. 这些众包成员有大部分都是其他机构, 大大小小的都有: Stats - Top Reporters Leaderboard 前几天我在我的其他站的防火墙里面就发现了其中的一家众包成员(甚至还是非盈利项目): About ducks.party Internet Scanner Bot 除掉这些众包成员, 我们也没办法排除掉人工去检查然后报告给类似 Netcraft 的机构的人的流量, 所以我的建议依旧是做好最基本的鉴权才是硬道理. 毕竟换个位置思考一下, 如果你是站长, 而你的站突然被人莫名其妙反代了, 甚至可能被用来钓鱼你站内的用户, 你会怎么办? 虽然我们只是反代一个自己的 SharePoint, 一开始就没有打算拿来做钓鱼这类完全欺诈的真滥用, 但是在没有做鉴权之前, 允许反代了整个 SharePoint 根路径直接暴露在公网上, 我们也办法就这样声明自己不是拿来做欺诈用途的, 毕竟在第三方看来, 这就和欺诈没什么两样.","date_published":"2024-08-13T03:43:53.000Z","tags":["授言册","Cloudflare","Netcraft"]},{"id":"https://blog.cxplay.org/works/use-siliconflow-api-in-selection-translate/","url":"https://blog.cxplay.org/works/use-siliconflow-api-in-selection-translate/","title":"在划词翻译中使用 SiliconCloud API 进行翻译","content_html":"<p>划词翻译是一个老牌的浏览器翻译插件, 至今已经有 11 年历史. 支持国内外主流的 ChatGPT, Gemini, 文心一言, 豆包等 12 个 AI 服务以及 Google, DeepL, 百度等 8 个翻译服务, 可用于全文翻译. 还能对比, 朗读, 复制翻译结果, 能在 PDF 里使用. 支持辅助键, 快捷键, 悬浮取词.</p>\n<p>SiliconCloud (硅基流动) 是一个国内的大语言模型云平台, 提供多种基于开源模型托管的 API 服务, 包括 Qwen, gemma, Llama, stable-diffusion, SDXL, DeepSeek 系列的模型. 提供免费和付费版本模型使用, 当然也能直接和划词翻译插件结合使用.</p>\n<h2 id=\"注册硅基流动账号\"><a href=\"#注册硅基流动账号\"></a>注册硅基流动账号</h2>\n<p>打开硅基流动网站, 选择注册:</p>\n<blockquote>\n<p>现在通过下方的链接注册新账号, 可以立即获得价值两千万的 Token 额度奖励!</p>\n</blockquote>\n<blockquote>\n<p><a href=\"https://url.cx.ms/siliconflow\">SiliconFlow，加速AGI普惠人类</a></p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_011858.webp\" alt=\"注册账号\" loading='lazy'></p>\n<h2 id=\"获取-api-密钥\"><a href=\"#获取-api-密钥\"></a>获取 API 密钥</h2>\n<p>登录到面板后, 点击侧边栏「API 密钥」打开密钥管理界面, 选择「创建新 API 密钥」:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_014832.webp\" alt=\"创建 API 密钥\" loading='lazy'></p>\n<p>创建成功后, 点击密钥部分即可将密钥复制到剪贴板.</p>\n<blockquote>\n<p>请务必保管好你的 API 密钥, 仅提供给在你许可范围内使用的地方使用它.</p>\n</blockquote>\n<h2 id=\"安装划词翻译插件\"><a href=\"#安装划词翻译插件\"></a>安装划词翻译插件</h2>\n<p>按照自己的浏览器类型前往下方对应的地址安装:</p>\n<ul>\n<li><strong>Google Chrome</strong>: <a href=\"https://chromewebstore.google.com/detail/ikhdkkncnoglghljlkmcimlnlhkeamad\">https://chromewebstore.google.com/~</a></li>\n<li><strong>Microsoft Edge</strong>: <a href=\"https://microsoftedge.microsoft.com/addons/detail/oikmahiipjniocckomdccmplodldodja\">https://microsoftedge.microsoft.com/~</a></li>\n<li><strong>Mozilla Firefox</strong>: <a href=\"https://addons.mozilla.org/zh-CN/firefox/addon/hcfy/\">https://addons.mozilla.org/~</a></li>\n</ul>\n<p>当然如果你的浏览器支持 crx 插件但是又无法通过上面的插件商店安装, 可以考虑进行离线安装:</p>\n<blockquote>\n<p><a href=\"https://hcfy.app/docs/installv2/offline\">下载离线包手动安装 | 划词翻译</a></p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_005809.webp\" alt=\"点击「添加至 Chrome」完成安装」\" loading='lazy'></p>\n<p>安装完成后会自动打开帮助网页, 默认情况下首次安装插件会被收纳进扩展程序列表中, 点击按钮打开划词翻译插件的配置页:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_010239.webp\" alt=\"打开配置页面\" loading='lazy'></p>\n<p>在侧边栏找到「服务申请」然后向下滚动找到「硅基流动」, 点击「管理密钥」添加 API 密钥:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_010650.webp\" alt=\"添加「硅基流动」的 API 密钥\" loading='lazy'></p>\n<p>点击「添加密钥」, 在新增的输入框中输入或粘贴你的硅基流动 API 密钥, 点击右上角关闭按钮或者小窗口的以外的位置保存配置:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_011008.webp\" alt=\"输入并保存 API 密钥\" loading='lazy'></p>\n<p>在划词面板启用硅基流动作为翻译服务:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_013730.webp\" alt=\"启用翻译服务\" loading='lazy'></p>\n<h2 id=\"切换使用高参数模型\"><a href=\"#切换使用高参数模型\"></a>切换使用高参数模型</h2>\n<p>默认情况下, 划词翻译插件使用 <code>Qwen/Qwen2-7B-Instruct</code> 模型, 适合一些日常对话翻译:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_012745.webp\" alt=\"切换模型\" loading='lazy'></p>\n<p>如果要用于专业的翻译情景, 可以考虑切换到更高参数的模型, 比如 <code>Qwen/Qwen2-57B-A14B-Instruct</code>, <code>Qwen/Qwen2-72B-Instruct</code>. 将模型代号填入框中即可保存使用.</p>\n<h2 id=\"试试效果\"><a href=\"#试试效果\"></a>试试效果</h2>\n<p>在网页中选中想要翻译的文本, 划词翻译插件的图标就会在光标附近显示, 点击图标即可进行翻译:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_013410.webp\" alt=\"翻译文本段落\" loading='lazy'></p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_013823.webp\" alt=\"窗口翻译\" loading='lazy'></p>\n<p>如果想要全文对照翻译, 可以点击鼠标邮件选中「划词翻译」项, 然后点击「网页全文翻译」:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_014013.webp\" alt=\"网页全文翻译\" loading='lazy'></p>\n<p>由于默认为 Google 翻译, 还需要在侧边栏切换为「硅基流动」作为当前的全文翻译服务, 立刻就能享受完美的双语阅读体验.</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/use-siliconflow-api-in-selection-translate/2024-08-10_014115.webp\" alt=\"切换翻译服务\" loading='lazy'></p>\n<hr>\n<ul>\n<li>Photo by <a href=\"https://unsplash.com/@nadineshaabana?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash\">Nadine Shaabana</a> on <a href=\"https://unsplash.com/photos/white-book-n8D3wsrbOis?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash\">Unsplash</a></li>\n</ul>\n","content_text":"划词翻译是一个老牌的浏览器翻译插件, 至今已经有 11 年历史. 支持国内外主流的 ChatGPT, Gemini, 文心一言, 豆包等 12 个 AI 服务以及 Google, DeepL, 百度等 8 个翻译服务, 可用于全文翻译. 还能对比, 朗读, 复制翻译结果, 能在 PDF 里使用. 支持辅助键, 快捷键, 悬浮取词. SiliconCloud (硅基流动) 是一个国内的大语言模型云平台, 提供多种基于开源模型托管的 API 服务, 包括 Qwen, gemma, Llama, stable-diffusion, SDXL, DeepSeek 系列的模型. 提供免费和付费版本模型使用, 当然也能直接和划词翻译插件结合使用. 注册硅基流动账号 打开硅基流动网站, 选择注册: 现在通过下方的链接注册新账号, 可以立即获得价值两千万的 Token 额度奖励! SiliconFlow，加速AGI普惠人类 获取 API 密钥 登录到面板后, 点击侧边栏「API 密钥」打开密钥管理界面, 选择「创建新 API 密钥」: 创建成功后, 点击密钥部分即可将密钥复制到剪贴板. 请务必保管好你的 API 密钥, 仅提供给在你许可范围内使用的地方使用它. 安装划词翻译插件 按照自己的浏览器类型前往下方对应的地址安装: Google Chrome: https://chromewebstore.google.com/~ Microsoft Edge: https://microsoftedge.microsoft.com/~ Mozilla Firefox: https://addons.mozilla.org/~ 当然如果你的浏览器支持 crx 插件但是又无法通过上面的插件商店安装, 可以考虑进行离线安装: 下载离线包手动安装 | 划词翻译 安装完成后会自动打开帮助网页, 默认情况下首次安装插件会被收纳进扩展程序列表中, 点击按钮打开划词翻译插件的配置页: 在侧边栏找到「服务申请」然后向下滚动找到「硅基流动」, 点击「管理密钥」添加 API 密钥: 点击「添加密钥」, 在新增的输入框中输入或粘贴你的硅基流动 API 密钥, 点击右上角关闭按钮或者小窗口的以外的位置保存配置: 在划词面板启用硅基流动作为翻译服务: 切换使用高参数模型 默认情况下, 划词翻译插件使用 Qwen/Qwen2-7B-Instruct 模型, 适合一些日常对话翻译: 如果要用于专业的翻译情景, 可以考虑切换到更高参数的模型, 比如 Qwen/Qwen2-57B-A14B-Instruct, Qwen/Qwen2-72B-Instruct. 将模型代号填入框中即可保存使用. 试试效果 在网页中选中想要翻译的文本, 划词翻译插件的图标就会在光标附近显示, 点击图标即可进行翻译: 如果想要全文对照翻译, 可以点击鼠标邮件选中「划词翻译」项, 然后点击「网页全文翻译」: 由于默认为 Google 翻译, 还需要在侧边栏切换为「硅基流动」作为当前的全文翻译服务, 立刻就能享受完美的双语阅读体验. Photo by Nadine Shaabana on Unsplash","summary":"划词翻译是一个老牌的浏览器翻译插件, 至今已经有 11 年历史. 支持国内外主流的 ChatGPT, Gemini, 文心一言, 豆包等 12 个 AI 服务以及 Google, DeepL, 百度等 8 个翻译服务, 可用于全文翻译. 还能对比, 朗读, 复制翻译结果, 能在 PDF 里使用. 支持辅助键, 快捷键, 悬浮取词. SiliconCloud (硅基流动) 是一个国内的大语言模型云平台, 提供多种基于开源模型托管的 API 服务, 包括 Qwen, gemma, Llama, stable-diffusion, SDXL, DeepSeek 系列的模型. 提供免费和付费版本模型使用, 当然也能直接和划词翻译插件结合使用. 注册硅基流动账号 打开硅基流动网站, 选择注册: 现在通过下方的链接注册新账号, 可以立即获得价值两千万的 Token 额度奖励! SiliconFlow，加速AGI普惠人类 获取 API 密钥 登录到面板后, 点击侧边栏「API 密钥」打开密钥管理界面, 选择「创建新 API 密钥」: 创建成功后, 点击密钥部分即可将密钥复制到剪贴板. 请务必保管好你的 API 密钥, 仅提供给在你许可范围内使用的地方使用它. 安装划词翻译插件 按照自己的浏览器类型前往下方对应的地址安装: Google Chrome: https://chromewebstore.google.com/~ Microsoft Edge: https://microsoftedge.microsoft.com/~ Mozilla Firefox: https://addons.mozilla.org/~ 当然如果你的浏览器支持 crx 插件但是又无法通过上面的插件商店安装, 可以考虑进行离线安装: 下载离线包手动安装 | 划词翻译 安装完成后会自动打开帮助网页, 默认情况下首次安装插件会被收纳进扩展程序列表中, 点击按钮打开划词翻译插件的配置页: 在侧边栏找到「服务申请」然后向下滚动找到「硅基流动」, 点击「管理密钥」添加 API 密钥: 点击「添加密钥」, 在新增的输入框中输入或粘贴你的硅基流动 API 密钥, 点击右上角关闭按钮或者小窗口的以外的位置保存配置: 在划词面板启用硅基流动作为翻译服务: 切换使用高参数模型 默认情况下, 划词翻译插件使用 Qwen/Qwen2-7B-Instruct 模型, 适合一些日常对话翻译: 如果要用于专业的翻译情景, 可以考虑切换到更高参数的模型, 比如 Qwen/Qwen2-57B-A14B-Instruct, Qwen/Qwen2-72B-Instruct. 将模型代号填入框中即可保存使用. 试试效果 在网页中选中想要翻译的文本, 划词翻译插件的图标就会在光标附近显示, 点击图标即可进行翻译: 如果想要全文对照翻译, 可以点击鼠标邮件选中「划词翻译」项, 然后点击「网页全文翻译」: 由于默认为 Google 翻译, 还需要在侧边栏切换为「硅基流动」作为当前的全文翻译服务, 立刻就能享受完美的双语阅读体验. Photo by Nadine Shaabana on Unsplash","date_published":"2024-08-09T16:44:04.000Z","tags":["授言册","硅基流动","AIGC","划词翻译"]},{"id":"https://blog.cxplay.org/works/match-limitations-of-adguard-general-filter-ruler-expressions/","url":"https://blog.cxplay.org/works/match-limitations-of-adguard-general-filter-ruler-expressions/","title":"AdGuard 一般过滤器规则表达式的匹配功能局限性","content_html":"<p>一般来说, AdGuard 和 uBlock Origin 用的这种 Adblock Plus 风格的语法非常适合用于匹配 URL, 但是也会存在一些极其少见的情况必须要用到正则来匹配.</p>\n<p>比如小红书的网页版, 主页使用参数来定位贴文频道位置:</p>\n<ul>\n<li><code>https://www.xiaohongshu.com/explore?channel_id=homefeed_recommend</code></li>\n</ul>\n<p>而具体的贴文则是:</p>\n<ul>\n<li><code>https://www.xiaohongshu.com/explore/xxxxx?xsec_token=xxxxx</code></li>\n</ul>\n<p>如果我们要清理小红书的贴文的参数, 一般就会写这样的规则:</p>\n<pre class=\"language-text\"><code>||www.xiaohongshu.com/explore^$removeparam\n</code></pre>\n<p>但是这条规则不仅仅匹配到了贴文, 还匹配到了主页, 导致主页用来定位的参数会在刷新后命中规则, 然后被清理.</p>\n<p>除非去详细指定要清理的参数键, 类似这样:</p>\n<pre class=\"language-text\"><code>||www.xiaohongshu.com/explore^$removeparam=/^(p1|p2|p3)=/\n</code></pre>\n<p>这样能避开 channel_id 这样的主页参数被意外清理, 但是要最大限度清理参数非常繁琐(要显式指定每一个目标参数键), 当然也能排除特定的不需要清理(或者说不能)的参数键:</p>\n<pre class=\"language-text\"><code>||www.xiaohongshu.com/explore^$removeparam=~channel_id\n</code></pre>\n<p>但问题又来了, 我们没办法确定 &quot;不能&quot; 清理的参数键在意外匹配到的页面上是否与实际我们要清理的贴文的 URL 有重合, 很可能一个参数在首页就是重要的, 但在贴文中就是无用的(大多数社媒的贴文页面参数基本都是无用的).</p>\n<hr>\n<p>另外一个例子是新浪微博, 用户资料页是:</p>\n<ul>\n<li><code>https://weibo.com/u/xxxxx?tabtype=feed</code></li>\n</ul>\n<p>微博贴文页是:</p>\n<ul>\n<li><code>https://weibo.com/123456/XrcXwXTWX?from=xxxxx</code></li>\n</ul>\n<p>如果要清理微博贴文 URL 中的全部参数而直接用这条规则:</p>\n<pre class=\"language-text\"><code>||weibo.com/*/*^$removeparam\n</code></pre>\n<p>很显然就会误伤到用户资料页的参数, 除非我们再写一条规则排除掉资料页:</p>\n<pre class=\"language-text\"><code>@@||weibo.com/u^$removeparam\n</code></pre>\n<p>这种情况在用户侧实际上很多, 我们没办法观测黑盒里全部可以和不可以清理的参数, 只知道<strong>部分可以清理</strong>的和<strong>部分必要</strong>的参数. 这种匹配不精准的情况, 要么尽可能探明可以清理的参数精准指定, 要么尽量把规则的匹配范围限定在更加具体的 URL 上只保留部分必要的参数.</p>\n<p>本来 &quot;精准匹配&quot; 就是写这类规则的首要考虑目标, 现在既然发现了容易被 &quot;误伤&quot; 的页面, 那就只能退而求其次选择更原始的正则匹配了:</p>\n<ul>\n<li>\n<p>小红书贴文:</p>\n  <pre class=\"language-text\"><code>/https?:\\/\\/www\\.xiaohongshu\\.com\\/explore\\/[0-9a-z]+/$removeparam=~xsec_token\n</code></pre>\n</li>\n<li>\n<p>微博贴文:</p>\n  <pre class=\"language-text\"><code>/https?:\\/\\/weibo\\.com\\/[0-9]+\\/[0-9a-zA-Z]+/$removeparam\n</code></pre>\n</li>\n</ul>\n<p>使用正则需要更加注意匹配范围的精确性, AdGuard 里的正则匹配性能相对一般过滤器规则表达式要差(虽然人也没办法非常直观地感受到).</p>\n<blockquote>\n<p><a href=\"https://adguard.com/kb/general/ad-filtering/create-own-filters/#regexp-support\">How to create your own ad filters | AdGuard Knowledge Base - Regular expressions support</a></p>\n</blockquote>\n<h2 id=\"交叉发布\"><a href=\"#交叉发布\"></a>交叉发布</h2>\n<ul>\n<li><strong>Nostr</strong>: <a href=\"https://habla.news/zh/a/naddr1qvzqqqr4gupzqs60j7vnvfl3uc03fm40vr923n7uasg2tyk2l7p9pjp9y5k4frq4qq7k6ct5vd5z6mrfd45hgct5d9hkuueddanz6ctyva6kzuny94nk2mn9wfskcttxd9k8getj94e82mr9wgkk27rswfjhxumfdah8xsd52lx\">https://habla.news/zh/a/naddr1qvzqqqr4gupzqs60j7vnvfl3uc03fm40vr923n7uasg2tyk2l7p9pjp9y5k4frq4qq7k6ct5vd5z6mrfd45hgct5d9hkuueddanz6ctyva6kzuny94nk2mn9wfskcttxd9k8getj94e82mr9wgkk27rswfjhxumfdah8xsd52lx</a></li>\n</ul>\n","content_text":"一般来说, AdGuard 和 uBlock Origin 用的这种 Adblock Plus 风格的语法非常适合用于匹配 URL, 但是也会存在一些极其少见的情况必须要用到正则来匹配. 比如小红书的网页版, 主页使用参数来定位贴文频道位置: https://www.xiaohongshu.com/explore?channel_id=homefeed_recommend 而具体的贴文则是: https://www.xiaohongshu.com/explore/xxxxx?xsec_token=xxxxx 如果我们要清理小红书的贴文的参数, 一般就会写这样的规则: 1||www.xiaohongshu.com/explore^$removeparam 但是这条规则不仅仅匹配到了贴文, 还匹配到了主页, 导致主页用来定位的参数会在刷新后命中规则, 然后被清理. 除非去详细指定要清理的参数键, 类似这样: 1||www.xiaohongshu.com/explore^$removeparam=/^(p1|p2|p3)=/ 这样能避开 channel_id 这样的主页参数被意外清理, 但是要最大限度清理参数非常繁琐(要显式指定每一个目标参数键), 当然也能排除特定的不需要清理(或者说不能)的参数键: 1||www.xiaohongshu.com/explore^$removeparam=~channel_id 但问题又来了, 我们没办法确定 &quot;不能&quot; 清理的参数键在意外匹配到的页面上是否与实际我们要清理的贴文的 URL 有重合, 很可能一个参数在首页就是重要的, 但在贴文中就是无用的(大多数社媒的贴文页面参数基本都是无用的). 另外一个例子是新浪微博, 用户资料页是: https://weibo.com/u/xxxxx?tabtype=feed 微博贴文页是: https://weibo.com/123456/XrcXwXTWX?from=xxxxx 如果要清理微博贴文 URL 中的全部参数而直接用这条规则: 1||weibo.com/*/*^$removeparam 很显然就会误伤到用户资料页的参数, 除非我们再写一条规则排除掉资料页: 1@@||weibo.com/u^$removeparam 这种情况在用户侧实际上很多, 我们没办法观测黑盒里全部可以和不可以清理的参数, 只知道部分可以清理的和部分必要的参数. 这种匹配不精准的情况, 要么尽可能探明可以清理的参数精准指定, 要么尽量把规则的匹配范围限定在更加具体的 URL 上只保留部分必要的参数. 本来 &quot;精准匹配&quot; 就是写这类规则的首要考虑目标, 现在既然发现了容易被 &quot;误伤&quot; 的页面, 那就只能退而求其次选择更原始的正则匹配了: 小红书贴文: 1/https?:\\/\\/www\\.xiaohongshu\\.com\\/explore\\/[0-9a-z]+/$removeparam=~xsec_token 微博贴文: 1/https?:\\/\\/weibo\\.com\\/[0-9]+\\/[0-9a-zA-Z]+/$removeparam 使用正则需要更加注意匹配范围的精确性, AdGuard 里的正则匹配性能相对一般过滤器规则表达式要差(虽然人也没办法非常直观地感受到). How to create your own ad filters | AdGuard Knowledge Base - Regular expressions support 交叉发布 Nostr: https://habla.news/zh/a/naddr1qvzqqqr4gupzqs60j7vnvfl3uc03fm40vr923n7uasg2tyk2l7p9pjp9y5k4frq4qq7k6ct5vd5z6mrfd45hgct5d9hkuueddanz6ctyva6kzuny94nk2mn9wfskcttxd9k8getj94e82mr9wgkk27rswfjhxumfdah8xsd52lx","summary":"一般来说, AdGuard 和 uBlock Origin 用的这种 Adblock Plus 风格的语法非常适合用于匹配 URL, 但是也会存在一些极其少见的情况必须要用到正则来匹配. 比如小红书的网页版, 主页使用参数来定位贴文频道位置: https://www.xiaohongshu.com/explore?channel_id=homefeed_recommend 而具体的贴文则是: https://www.xiaohongshu.com/explore/xxxxx?xsec_token=xxxxx 如果我们要清理小红书的贴文的参数, 一般就会写这样的规则: 1||www.xiaohongshu.com/explore^$removeparam 但是这条规则不仅仅匹配到了贴文, 还匹配到了主页, 导致主页用来定位的参数会在刷新后命中规则, 然后被清理. 除非去详细指定要清理的参数键, 类似这样: 1||www.xiaohongshu.com/explore^$removeparam=/^(p1|p2|p3)=/ 这样能避开 channel_id 这样的主页参数被意外清理, 但是要最大限度清理参数非常繁琐(要显式指定每一个目标参数键), 当然也能排除特定的不需要清理(或者说不能)的参数键: 1||www.xiaohongshu.com/explore^$removeparam=~channel_id 但问题又来了, 我们没办法确定 &quot;不能&quot; 清理的参数键在意外匹配到的页面上是否与实际我们要清理的贴文的 URL 有重合, 很可能一个参数在首页就是重要的, 但在贴文中就是无用的(大多数社媒的贴文页面参数基本都是无用的). 另外一个例子是新浪微博, 用户资料页是: https://weibo.com/u/xxxxx?tabtype=feed 微博贴文页是: https://weibo.com/123456/XrcXwXTWX?from=xxxxx 如果要清理微博贴文 URL 中的全部参数而直接用这条规则: 1||weibo.com/*/*^$removeparam 很显然就会误伤到用户资料页的参数, 除非我们再写一条规则排除掉资料页: 1@@||weibo.com/u^$removeparam 这种情况在用户侧实际上很多, 我们没办法观测黑盒里全部可以和不可以清理的参数, 只知道部分可以清理的和部分必要的参数. 这种匹配不精准的情况, 要么尽可能探明可以清理的参数精准指定, 要么尽量把规则的匹配范围限定在更加具体的 URL 上只保留部分必要的参数. 本来 &quot;精准匹配&quot; 就是写这类规则的首要考虑目标, 现在既然发现了容易被 &quot;误伤&quot; 的页面, 那就只能退而求其次选择更原始的正则匹配了: 小红书贴文: 1/https?:\\/\\/www\\.xiaohongshu\\.com\\/explore\\/[0-9a-z]+/$removeparam=~xsec_token 微博贴文: 1/https?:\\/\\/weibo\\.com\\/[0-9]+\\/[0-9a-zA-Z]+/$removeparam 使用正则需要更加注意匹配范围的精确性, AdGuard 里的正则匹配性能相对一般过滤器规则表达式要差(虽然人也没办法非常直观地感受到). How to create your own ad filters | AdGuard Knowledge Base - Regular expressions support 交叉发布 Nostr: https://habla.news/zh/a/naddr1qvzqqqr4gupzqs60j7vnvfl3uc03fm40vr923n7uasg2tyk2l7p9pjp9y5k4frq4qq7k6ct5vd5z6mrfd45hgct5d9hkuueddanz6ctyva6kzuny94nk2mn9wfskcttxd9k8getj94e82mr9wgkk27rswfjhxumfdah8xsd52lx","date_published":"2024-08-07T14:46:27.000Z","tags":["资料库","AdGuard","AdGuard","正则表达式"]},{"id":"https://blog.cxplay.org/works/from-fediverse-spam-attacks-to-nostr-event-delivery-and-storage/","url":"https://blog.cxplay.org/works/from-fediverse-spam-attacks-to-nostr-event-delivery-and-storage/","title":"从 Fediverse 垃圾内容攻击到 Nostr 事件传递与存储","content_html":"<p>从 Fediverse 垃圾内容攻击到 Nostr 事件传递与存储\nFediverse 最近遭遇了一轮大规模的垃圾信息攻击, 身边的朋友大多都收到了影响, 管理员们也在积极互助传递从服务器抗击和清理垃圾内容的方案.</p>\n<iframe src=\"https://mastodon.social/@Gargron/111953045633249137/embed\" class=\"mastodon-embed\" style=\"max-width: 100%; border: 0\" width=\"100%\" height=\"320px\" allowfullscreen=\"allowfullscreen\"></iframe><script src=\"https://mastodon.social/embed.js\" async=\"async\"></script>\n<p>这也不由得提到 Nostr 中的同样存在的大规模垃圾内容问题. 在 Nostr 客户端刚上线的时候, 主流的免费公共中继提供的全球时间线几乎全都是垃圾內容, 而且大部分还是中文的. 而后的几个月, 热度褪去, 似乎 Nostr 对操控垃圾账户的人失去了吸引力? 在服务端这边, 中继器的管理员面对已经存在的垃圾内容也只能通过手动或脚本的方式操作数据库删除, 甚至部分免费的中继器会有定期清空数据库的习惯, 或者针对特定类型的事件进行清理. 随后, 付费中继开始集中涌现出来, 也是一种基于金钱的工作量证明中继准入的方式, 当然目前所有的付费中继也没有任何一个承诺用户数据持久化, 即使是头部的 nostr.wine 准入门槛高达 $9.99 也没有, 甚至推出过专门针对持久化存档事件提供的服务(现在似乎下线了).</p>\n<p>从中继管理员直接操纵数据库清理垃圾内容似乎是最高效的, 但是这也暴露出 Nostr 依旧受制于传统基础设施不「抗审查」, 中继管理员即使无法签署 kind-5 事件来删除我们的事件, 只需要让它们数据库里的行列消失就已经足够了. 中继查找不到事件, 自然就做到了审查, 其中也包括了公钥对应的最基础的用户资料, 如果公钥不存在任何关联事件, 那么这对密钥就等于没有任何价值. 社区开始反思目前的状况, Nostr 中 T 代表传输, 没有任何一个关键字代表着存储, 也就是中继并不应该承担保存事件数据实体的用途, 而是仅传输1, 中继选择保存事件到数据库那是受限于空间和时间做出的妥协. NIPs 也没有规范中继软件如何存储用户事件在服务器中. 但是现在没有任何一个客户端支持在事件被广播出去之前将这串 JSON 保存在本地以供日后不时之需, 同时也养成了错误的用户习惯, 用户默认了中继就是保存 Nostr 网络中一切的地方, 发送完事件后就再也不管了. 这可能确实需要一些改变, 结合上文的垃圾信息攻击问题, 到最后真正保存下来的事件恐怕只有私人中继和用户本地存档中的事件.</p>\n<hr>\n<blockquote>\n<p>原文: <a href=\"https://nostr.cxplay.org/naddr1qqgxydnyxsexyvryxdnrxvrrvgmnyq3qgd8e0xfkylc7v8c5a6hkpj4gelwwcy99jt90lqjseqjj2t253s2sxpqqqp65w6pmg77\">https://nostr.cxplay.org/naddr1qqgxydnyxsexyvryxdnrxvrrvgmnyq3qgd8e0xfkylc7v8c5a6hkpj4gelwwcy99jt90lqjseqjj2t253s2sxpqqqp65w6pmg77</a></p>\n</blockquote>\n","content_text":"从 Fediverse 垃圾内容攻击到 Nostr 事件传递与存储 Fediverse 最近遭遇了一轮大规模的垃圾信息攻击, 身边的朋友大多都收到了影响, 管理员们也在积极互助传递从服务器抗击和清理垃圾内容的方案. 这也不由得提到 Nostr 中的同样存在的大规模垃圾内容问题. 在 Nostr 客户端刚上线的时候, 主流的免费公共中继提供的全球时间线几乎全都是垃圾內容, 而且大部分还是中文的. 而后的几个月, 热度褪去, 似乎 Nostr 对操控垃圾账户的人失去了吸引力? 在服务端这边, 中继器的管理员面对已经存在的垃圾内容也只能通过手动或脚本的方式操作数据库删除, 甚至部分免费的中继器会有定期清空数据库的习惯, 或者针对特定类型的事件进行清理. 随后, 付费中继开始集中涌现出来, 也是一种基于金钱的工作量证明中继准入的方式, 当然目前所有的付费中继也没有任何一个承诺用户数据持久化, 即使是头部的 nostr.wine 准入门槛高达 $9.99 也没有, 甚至推出过专门针对持久化存档事件提供的服务(现在似乎下线了). 从中继管理员直接操纵数据库清理垃圾内容似乎是最高效的, 但是这也暴露出 Nostr 依旧受制于传统基础设施不「抗审查」, 中继管理员即使无法签署 kind-5 事件来删除我们的事件, 只需要让它们数据库里的行列消失就已经足够了. 中继查找不到事件, 自然就做到了审查, 其中也包括了公钥对应的最基础的用户资料, 如果公钥不存在任何关联事件, 那么这对密钥就等于没有任何价值. 社区开始反思目前的状况, Nostr 中 T 代表传输, 没有任何一个关键字代表着存储, 也就是中继并不应该承担保存事件数据实体的用途, 而是仅传输1, 中继选择保存事件到数据库那是受限于空间和时间做出的妥协. NIPs 也没有规范中继软件如何存储用户事件在服务器中. 但是现在没有任何一个客户端支持在事件被广播出去之前将这串 JSON 保存在本地以供日后不时之需, 同时也养成了错误的用户习惯, 用户默认了中继就是保存 Nostr 网络中一切的地方, 发送完事件后就再也不管了. 这可能确实需要一些改变, 结合上文的垃圾信息攻击问题, 到最后真正保存下来的事件恐怕只有私人中继和用户本地存档中的事件. 原文: https://nostr.cxplay.org/naddr1qqgxydnyxsexyvryxdnrxvrrvgmnyq3qgd8e0xfkylc7v8c5a6hkpj4gelwwcy99jt90lqjseqjj2t253s2sxpqqqp65w6pmg77","summary":"从 Fediverse 垃圾内容攻击到 Nostr 事件传递与存储 Fediverse 最近遭遇了一轮大规模的垃圾信息攻击, 身边的朋友大多都收到了影响, 管理员们也在积极互助传递从服务器抗击和清理垃圾内容的方案. 这也不由得提到 Nostr 中的同样存在的大规模垃圾内容问题. 在 Nostr 客户端刚上线的时候, 主流的免费公共中继提供的全球时间线几乎全都是垃圾內容, 而且大部分还是中文的. 而后的几个月, 热度褪去, 似乎 Nostr 对操控垃圾账户的人失去了吸引力? 在服务端这边, 中继器的管理员面对已经存在的垃圾内容也只能通过手动或脚本的方式操作数据库删除, 甚至部分免费的中继器会有定期清空数据库的习惯, 或者针对特定类型的事件进行清理. 随后, 付费中继开始集中涌现出来, 也是一种基于金钱的工作量证明中继准入的方式, 当然目前所有的付费中继也没有任何一个承诺用户数据持久化, 即使是头部的 nostr.wine 准入门槛高达 $9.99 也没有, 甚至推出过专门针对持久化存档事件提供的服务(现在似乎下线了). 从中继管理员直接操纵数据库清理垃圾内容似乎是最高效的, 但是这也暴露出 Nostr 依旧受制于传统基础设施不「抗审查」, 中继管理员即使无法签署 kind-5 事件来删除我们的事件, 只需要让它们数据库里的行列消失就已经足够了. 中继查找不到事件, 自然就做到了审查, 其中也包括了公钥对应的最基础的用户资料, 如果公钥不存在任何关联事件, 那么这对密钥就等于没有任何价值. 社区开始反思目前的状况, Nostr 中 T 代表传输, 没有任何一个关键字代表着存储, 也就是中继并不应该承担保存事件数据实体的用途, 而是仅传输1, 中继选择保存事件到数据库那是受限于空间和时间做出的妥协. NIPs 也没有规范中继软件如何存储用户事件在服务器中. 但是现在没有任何一个客户端支持在事件被广播出去之前将这串 JSON 保存在本地以供日后不时之需, 同时也养成了错误的用户习惯, 用户默认了中继就是保存 Nostr 网络中一切的地方, 发送完事件后就再也不管了. 这可能确实需要一些改变, 结合上文的垃圾信息攻击问题, 到最后真正保存下来的事件恐怕只有私人中继和用户本地存档中的事件. 原文: https://nostr.cxplay.org/naddr1qqgxydnyxsexyvryxdnrxvrrvgmnyq3qgd8e0xfkylc7v8c5a6hkpj4gelwwcy99jt90lqjseqjj2t253s2sxpqqqp65w6pmg77","date_published":"2024-02-23T11:24:03.000Z","tags":["博物志","社交媒体","Nostr","Fediverse","社交媒体","Mastodon"]},{"id":"https://blog.cxplay.org/works/firefox-is-here-with-sec-gpc-what-about-dnt/","url":"https://blog.cxplay.org/works/firefox-is-here-with-sec-gpc-what-about-dnt/","title":"Sec-GPC 在 Firefox  得到了支持, 那 DNT 怎么样了?","content_html":"<p><img src=\"https://images.unsplash.com/photo-1591276625440-d50e6618dfd1?q=80&amp;w=2670&amp;auto=format&amp;fit=crop&amp;ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\" alt=\"\" loading='lazy'></p>\n<blockquote>\n<p>Photo by <a href=\"https://unsplash.com/@jacksondavid\">Jackson David</a> on <a href=\"https://unsplash.com/photos/grayscale-photo-of-persons-hand-cIcBInoyb7U\">Unsplash</a></p>\n</blockquote>\n<hr>\n<p>Firefox  于 11 月 21 日发布了 Firefox  120 Beta 版本, 其中在 HTTP 协议部分添加了对 <code>Sec-GPC</code> 标头的支持:</p>\n<blockquote>\n<p>Firefox 支持全球隐私控制 <code>Sec-GPC</code> 请求标头, 发送该标头可表明用户不同意网站或服务向第三方出售或共享其个人信息. 用户可以通过将首选项 <code>privacy.globalprivacycontrol.enabled</code> 设置为 <code>true</code> (在 <code>about:config</code> 中)在正常和私密浏览模式下启用标头. <code>Navigator.globalPrivacyControl</code> 和 <code>WorkerNavigator.globalPrivacyControl</code> 属性允许 JavaScript 检查用户同意首选项.</p>\n<p>—— <a href=\"https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/120#:~:text=Firefox%20supports%20the%20Global%20Privacy%20Control%20Sec%2DGPC%20request%20header\">Firefox 120 for developers - Mozilla | MDN</a> [<a href=\"https://archive.ph/Jjssa#selection-1263.5-1273.15\">存档</a>]</p>\n</blockquote>\n<p>那么这个 <code>Sec-GPC</code> 究竟是什么东西? 它为什么要表明用户对数据的请求意愿? 曾经和它相同愿景的 DNT 怎么样了?</p>\n<hr>\n<p><strong>长话短说, Sec-GPC 是 DNT 的继任者.</strong></p>\n<p><code>Sec-GPC</code> 这个标头起源于 2009 年提出的 DNT(Do-Not-Track), Firefox  是第一个支持 DNT 的浏览器, 但是目前 <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT#:~:text=Non%2Dstandard%3A%20This%20feature%20is%20non%2Dstandard%20and%20is%20not%20on%20a%20standards%20track.%20Do%20not%20use%20it%20on%20production%20sites%20facing%20the%20Web%3A%20it%20will%20not%20work%20for%20every%20user.%20There%20may%20also%20be%20large%20incompatibilities%20between%20implementations%20and%20the%20behavior%20may%20change%20in%20the%20future.\">MDN 里面给出的提示</a>是: 「不再推荐此功能. 尽管某些浏览器可能仍然支持它, 但它可能已从相关网络标准中删除, 可能正在被删除或者可能仅出于兼容性目的而保留.」, <code>Sec-GPC</code> 虽然也<a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-GPC#:~:text=Non%2Dstandard%3A%20This%20feature%20is%20non%2Dstandard%20and%20is%20not%20on%20a%20standards%20track.%20Do%20not%20use%20it%20on%20production%20sites%20facing%20the%20Web%3A%20it%20will%20not%20work%20for%20every%20user.%20There%20may%20also%20be%20large%20incompatibilities%20between%20implementations%20and%20the%20behavior%20may%20change%20in%20the%20future.\">好不到哪里去</a>: 「 此功能是非标准的, 并且不在标准轨道上. 不要在面向 Web 的生产站点上使用它: 它不适用于每个用户. 实现之间也可能存在很大的不兼容性, 并且行为将来可能会发生变化.」, DNT 被看作是一个「失败的网络试验」, 不过起码是有人提出了这种倡议, 并且确实存在也产生过影响.</p>\n<p>对 <code>Sec-GPC</code> 的详细描述参见 Global Privacy Control 提案:</p>\n<blockquote>\n<p><a href=\"https://privacycg.github.io/gpc-spec/\">Global Privacy Control (GPC)</a> [<a href=\"https://archive.ph/EvANA\">存档</a>]</p>\n</blockquote>\n<h2 id=\"现状\"><a href=\"#现状\"></a>现状</h2>\n<p>对于 DNT 在用户侧的最直接的表现就是浏览器里面的 <a href=\"https://support.google.com/chrome/answer/2790761?hl=zh-Hans&amp;co=GENIE.Platform%3DDesktop\">&quot;请勿跟踪&quot; 选项</a>:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/firefox-is-here-with-sec-gpc-what-about-dnt/image-20231121230850207.webp\" alt=\"浏览器里面的 DNT\" loading='lazy'></p>\n<p>即使最流行的浏览器都支持了该功能并且还引起了很多人的误会, 让人以为在浏览器中启用了这个选项就不会被跟踪了, 但实际上由于 DNT 最终依赖于服务方而非用户方. 流行且并且支持 DNT 的只有 Medium, Pinterest 和 Reddit. <a href=\"https://archive.ph/I8ulU\">Yahoo</a> 和 <a href=\"https://archive.ph/wsiWR\">Twitter</a> 曾经有对 DNT 的支持, 但现在已经全都是昨日黄花.</p>\n<p>现在还支持 DNT 的全部加起来都是屈指可数. 一般性的 DNT 政策对于提供服务的平台都是写在隐私政策里面, 在这里可以看到所有目前已经实施了 DNT 政策的平台:</p>\n<blockquote>\n<p><a href=\"https://allaboutdnt.com/companies/\">Companies Archive - All About DNT</a> [<a href=\"https://archive.ph/xncZs\">存档</a>]</p>\n</blockquote>\n<p>DNT 的现状以至于让很多人认为 DNT 没有任何作用.</p>\n<p>当然也有明确在政策里面写明不遵守的, 比如 Cygames 的隐私政策的 &quot;在线跟踪&quot; 里面写道:</p>\n<blockquote>\n<p>某些 Web 浏览器和其他程序可能会被用来向我们传达您对我们或第三方如何或是否可以收集您在线活动信息的偏好. 目前对于如何响应 &quot;请勿跟踪&quot;(DNT) 信号尚无公认标准, 因此 Cygames 不支持 DNT 浏览器设置, 并且目前未参与任何可能允许我们响应由您发出的关于收集您个人信息或非个人信息的信号或其他机制的 DNT 框架.</p>\n<p>—— <a href=\"https://shadowverse.com/chs/privacy/#:~:text=%E7%9A%84%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF%E3%80%82-,%E5%9C%A8%E7%BA%BF%E8%B7%9F%E8%B8%AA,-%E6%9F%90%E4%BA%9B%20Web%20%E6%B5%8F%E8%A7%88\">隐私政策 | 【Shadowverse】官方网站 | Cygames</a> [<a href=\"https://shadowverse.com/chs/privacy/#:~:text=%E7%9A%84%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF%E3%80%82-,%E5%9C%A8%E7%BA%BF%E8%B7%9F%E8%B8%AA,-%E6%9F%90%E4%BA%9B%20Web%20%E6%B5%8F%E8%A7%88\">存档</a>]</p>\n</blockquote>\n<p>这也是大多数的做法, 比起隐私政策更像一种 &quot;免责声明&quot;.</p>\n<p>那么, 为什么没有作用? DuckDuckGo 在内的大部分人都认为是法律的缺失, 所以 DuckDuckGo 在欧盟 GDPR 生效后的一年发表了《<a href=\"https://duckduckgo.com/download/The_Do-Not-Track_Act_of_2019.pdf\">2019 年禁止跟踪法案</a>》, 期望通过立法为这类技术提供程序性正义的保障.</p>\n<blockquote>\n<p><a href=\"https://www.w3.org/blog/2018/do-not-track-and-the-gdpr/\">Do Not Track and the GDPR | 2018 | Blog | W3C</a> [<a href=\"https://archive.ph/mGkmY\">存档</a>]</p>\n</blockquote>\n<p>几乎没有平台遵守, 那么 DNT 是否失去了意义? 特别是对经营全球性服务的互联网公司来说? 但现在下结论还可能为时尚早:</p>\n<p>nostr:nevent1qqs0hx0rezzmk9w6fk7geawlns4tj0qkt32sf3adt6j7wzymwfqpl0saqxun4</p>\n<blockquote>\n<p>但德国柏林地区法院裁决, 请勿追踪具有法律约束力. 此案与微软旗下的职业社交网络 LinkedIn 有关, 它在网站上明确表示: 由于目前没有 &quot;请勿追踪&quot; 的相关标准, 它对请勿追踪信号不予回应. 但德国消费者保护组织 vzbv 指出, 如果消费者启用了浏览器的请勿追踪功能, 那么他们发出了一个明确的信号: 不希望其上网行为被跟踪用于广告等目的, 网站运营者必须尊重这一信号. 法官同意了这一意见, 认为 LinkedIn 的声明具有误导性.</p>\n<p>—— <a href=\"https://www.solidot.org/story?sid=76492\">德国法庭裁决 Do Not Track 具有法律约束力 | 奇客Solidot</a> [<a href=\"https://archive.ph/A4vpt\">存档</a>]</p>\n</blockquote>\n<p>现在的 <code>Sec-GPC</code> 的状态是 &quot;规范提案&quot;, Mozilla 决定在 Firefox  中支持 <code>Sec-GPC</code> 表达了对这个提案的响应, 它也是第一个支持该特性的浏览器.</p>\n<h2 id=\"dnt-和-sec-gpc-的意义\"><a href=\"#dnt-和-sec-gpc-的意义\"></a>DNT 和 Sec-GPC 的意义</h2>\n<p>对互联网用户而言, DNT 和 <code>Sec-GPC</code> 不是一个反追踪和隐私保护工具, 比起要达成更深远的隐私保护目的而言, 它只是一种比 &quot;Cookie 确认弹窗&quot; 更进步和现代的技术. 毕竟第三方 Cookie 马上就要走到了末路:</p>\n<blockquote>\n<p>如果您的网站使用第三方 Cookie, 那么当我们即将弃用它们时, 就该采取行动了. Chrome 计划从 2024 年第一季度开始对 1% 的用户禁用第三方 Cookie, 以方便测试, 然后从 2024 年第三季度开始将覆盖范围扩大到 100%. 最终是否扩大到 100% 的用户范围取决于解决英国竞争与市场管理局(CMA)调查的竞争问题.</p>\n<p>—— <a href=\"https://developer.chrome.com/blog/cookie-countdown-2023oct/\">Preparing for the end of third-party cookies - Chrome for Developers</a> [<a href=\"https://archive.ph/hl1TP\">存档</a>]</p>\n</blockquote>\n<p>是第三方 Cookie 消失了吗? 论形式和手段而言确实消失了, 但论达成目的而言并没有, 它们其实是 &quot;逃&quot; 到了新的地方, Google 为这类 &quot;逃离的第三方 Cookie&quot; 设计了一个新的环境: <a href=\"https://en.wikipedia.org/wiki/Privacy_Sandbox\">隐私沙盒</a>(Privacy Sandbox). 但隐私沙盒对用户而言又是一个在对手的规则里进行 &quot;公平对决&quot; 的擂台.</p>\n<p>对互联网用户来说, 隐私是一种「筹码」, 是很多互联网服务的隐藏条件, 特别是依赖于对用户数据分析和货币化的公司而言. Google 推进隐私沙盒与第三方 Cookie 形式上的对抗引起的各国反垄断调查的的原因也只是: 隐私沙盒破坏了<strong>几乎所有</strong>不通过 Google 用于追踪和收集数据的方式(美国), 可能会对传统媒体和其他数字广告市场产生重大影响(英国).</p>\n<blockquote>\n<p><a href=\"https://en.wikipedia.org/wiki/Privacy_Sandbox#Antitrust_concerns\">Privacy Sandbox #Antitrust concerns - Wikipedia</a> [<a href=\"https://archive.ph/Canyk#Antitrust_concerns\">存档</a>]</p>\n</blockquote>\n<p>那么, 互联网用户该怎么办?</p>\n<p>我们在这之前可以通过主动或被动避开第三方 Cookie, 隐私沙盒之后呢? 遗憾的是, 现在看来最让用户处于最 &quot;主动&quot; 地位的只有法律, 比如 GDPR, 加州消费者隐私法案(CCPA), 加州在线隐私保护法(CalOPPA)甚至美国儿童在线隐私保护法(COPPA).</p>\n<p>DNT 和 <code>Sec-GPC</code> 是相对于这些事后的纠纷依据更提前的事前申明, 让用户和平台之间直接进行隐私和数据「筹码」交易的协商, 这种功能和如今的 &quot;Cookie 确认弹窗&quot; 一模一样, 在更远的未来可能会将这种对 &quot;第三方&quot; 的约束扩展到第一方.</p>\n<p>正如 DuckDuckGo 的 CEO 所说的 &quot;<a href=\"https://archive.ph/xDj22\">我们应该选择数据跟踪, 而不是拒绝数据跟踪.</a>&quot;, 天下没有免费的午餐, 在互联网上这顿免费的 &quot;午餐&quot; 一切的代价要么是我们的财产要么是我们的隐私, DNT 和 <code>Sec-GPC</code> 是「交易」前的谈判, 隐私保护相关的法律是「交易」破裂后的仲裁. 我们作为互联网的消费者, 应该尽可能多地掌握主动权.</p>\n<blockquote>\n<p>隐私为什么不能算作一种财产呢?</p>\n</blockquote>\n","content_text":"Photo by Jackson David on Unsplash Firefox 于 11 月 21 日发布了 Firefox 120 Beta 版本, 其中在 HTTP 协议部分添加了对 Sec-GPC 标头的支持: Firefox 支持全球隐私控制 Sec-GPC 请求标头, 发送该标头可表明用户不同意网站或服务向第三方出售或共享其个人信息. 用户可以通过将首选项 privacy.globalprivacycontrol.enabled 设置为 true (在 about:config 中)在正常和私密浏览模式下启用标头. Navigator.globalPrivacyControl 和 WorkerNavigator.globalPrivacyControl 属性允许 JavaScript 检查用户同意首选项. —— Firefox 120 for developers - Mozilla | MDN [存档] 那么这个 Sec-GPC 究竟是什么东西? 它为什么要表明用户对数据的请求意愿? 曾经和它相同愿景的 DNT 怎么样了? 长话短说, Sec-GPC 是 DNT 的继任者. Sec-GPC 这个标头起源于 2009 年提出的 DNT(Do-Not-Track), Firefox 是第一个支持 DNT 的浏览器, 但是目前 MDN 里面给出的提示是: 「不再推荐此功能. 尽管某些浏览器可能仍然支持它, 但它可能已从相关网络标准中删除, 可能正在被删除或者可能仅出于兼容性目的而保留.」, Sec-GPC 虽然也好不到哪里去: 「 此功能是非标准的, 并且不在标准轨道上. 不要在面向 Web 的生产站点上使用它: 它不适用于每个用户. 实现之间也可能存在很大的不兼容性, 并且行为将来可能会发生变化.」, DNT 被看作是一个「失败的网络试验」, 不过起码是有人提出了这种倡议, 并且确实存在也产生过影响. 对 Sec-GPC 的详细描述参见 Global Privacy Control 提案: Global Privacy Control (GPC) [存档] 现状 对于 DNT 在用户侧的最直接的表现就是浏览器里面的 &quot;请勿跟踪&quot; 选项: 即使最流行的浏览器都支持了该功能并且还引起了很多人的误会, 让人以为在浏览器中启用了这个选项就不会被跟踪了, 但实际上由于 DNT 最终依赖于服务方而非用户方. 流行且并且支持 DNT 的只有 Medium, Pinterest 和 Reddit. Yahoo 和 Twitter 曾经有对 DNT 的支持, 但现在已经全都是昨日黄花. 现在还支持 DNT 的全部加起来都是屈指可数. 一般性的 DNT 政策对于提供服务的平台都是写在隐私政策里面, 在这里可以看到所有目前已经实施了 DNT 政策的平台: Companies Archive - All About DNT [存档] DNT 的现状以至于让很多人认为 DNT 没有任何作用. 当然也有明确在政策里面写明不遵守的, 比如 Cygames 的隐私政策的 &quot;在线跟踪&quot; 里面写道: 某些 Web 浏览器和其他程序可能会被用来向我们传达您对我们或第三方如何或是否可以收集您在线活动信息的偏好. 目前对于如何响应 &quot;请勿跟踪&quot;(DNT) 信号尚无公认标准, 因此 Cygames 不支持 DNT 浏览器设置, 并且目前未参与任何可能允许我们响应由您发出的关于收集您个人信息或非个人信息的信号或其他机制的 DNT 框架. —— 隐私政策 | 【Shadowverse】官方网站 | Cygames [存档] 这也是大多数的做法, 比起隐私政策更像一种 &quot;免责声明&quot;. 那么, 为什么没有作用? DuckDuckGo 在内的大部分人都认为是法律的缺失, 所以 DuckDuckGo 在欧盟 GDPR 生效后的一年发表了《2019 年禁止跟踪法案》, 期望通过立法为这类技术提供程序性正义的保障. Do Not Track and the GDPR | 2018 | Blog | W3C [存档] 几乎没有平台遵守, 那么 DNT 是否失去了意义? 特别是对经营全球性服务的互联网公司来说? 但现在下结论还可能为时尚早: nostr:nevent1qqs0hx0rezzmk9w6fk7geawlns4tj0qkt32sf3adt6j7wzymwfqpl0saqxun4 但德国柏林地区法院裁决, 请勿追踪具有法律约束力. 此案与微软旗下的职业社交网络 LinkedIn 有关, 它在网站上明确表示: 由于目前没有 &quot;请勿追踪&quot; 的相关标准, 它对请勿追踪信号不予回应. 但德国消费者保护组织 vzbv 指出, 如果消费者启用了浏览器的请勿追踪功能, 那么他们发出了一个明确的信号: 不希望其上网行为被跟踪用于广告等目的, 网站运营者必须尊重这一信号. 法官同意了这一意见, 认为 LinkedIn 的声明具有误导性. —— 德国法庭裁决 Do Not Track 具有法律约束力 | 奇客Solidot [存档] 现在的 Sec-GPC 的状态是 &quot;规范提案&quot;, Mozilla 决定在 Firefox 中支持 Sec-GPC 表达了对这个提案的响应, 它也是第一个支持该特性的浏览器. DNT 和 Sec-GPC 的意义 对互联网用户而言, DNT 和 Sec-GPC 不是一个反追踪和隐私保护工具, 比起要达成更深远的隐私保护目的而言, 它只是一种比 &quot;Cookie 确认弹窗&quot; 更进步和现代的技术. 毕竟第三方 Cookie 马上就要走到了末路: 如果您的网站使用第三方 Cookie, 那么当我们即将弃用它们时, 就该采取行动了. Chrome 计划从 2024 年第一季度开始对 1% 的用户禁用第三方 Cookie, 以方便测试, 然后从 2024 年第三季度开始将覆盖范围扩大到 100%. 最终是否扩大到 100% 的用户范围取决于解决英国竞争与市场管理局(CMA)调查的竞争问题. —— Preparing for the end of third-party cookies - Chrome for Developers [存档] 是第三方 Cookie 消失了吗? 论形式和手段而言确实消失了, 但论达成目的而言并没有, 它们其实是 &quot;逃&quot; 到了新的地方, Google 为这类 &quot;逃离的第三方 Cookie&quot; 设计了一个新的环境: 隐私沙盒(Privacy Sandbox). 但隐私沙盒对用户而言又是一个在对手的规则里进行 &quot;公平对决&quot; 的擂台. 对互联网用户来说, 隐私是一种「筹码」, 是很多互联网服务的隐藏条件, 特别是依赖于对用户数据分析和货币化的公司而言. Google 推进隐私沙盒与第三方 Cookie 形式上的对抗引起的各国反垄断调查的的原因也只是: 隐私沙盒破坏了几乎所有不通过 Google 用于追踪和收集数据的方式(美国), 可能会对传统媒体和其他数字广告市场产生重大影响(英国). Privacy Sandbox #Antitrust concerns - Wikipedia [存档] 那么, 互联网用户该怎么办? 我们在这之前可以通过主动或被动避开第三方 Cookie, 隐私沙盒之后呢? 遗憾的是, 现在看来最让用户处于最 &quot;主动&quot; 地位的只有法律, 比如 GDPR, 加州消费者隐私法案(CCPA), 加州在线隐私保护法(CalOPPA)甚至美国儿童在线隐私保护法(COPPA). DNT 和 Sec-GPC 是相对于这些事后的纠纷依据更提前的事前申明, 让用户和平台之间直接进行隐私和数据「筹码」交易的协商, 这种功能和如今的 &quot;Cookie 确认弹窗&quot; 一模一样, 在更远的未来可能会将这种对 &quot;第三方&quot; 的约束扩展到第一方. 正如 DuckDuckGo 的 CEO 所说的 &quot;我们应该选择数据跟踪, 而不是拒绝数据跟踪.&quot;, 天下没有免费的午餐, 在互联网上这顿免费的 &quot;午餐&quot; 一切的代价要么是我们的财产要么是我们的隐私, DNT 和 Sec-GPC 是「交易」前的谈判, 隐私保护相关的法律是「交易」破裂后的仲裁. 我们作为互联网的消费者, 应该尽可能多地掌握主动权. 隐私为什么不能算作一种财产呢?","summary":"Photo by Jackson David on Unsplash Firefox 于 11 月 21 日发布了 Firefox 120 Beta 版本, 其中在 HTTP 协议部分添加了对 Sec-GPC 标头的支持: Firefox 支持全球隐私控制 Sec-GPC 请求标头, 发送该标头可表明用户不同意网站或服务向第三方出售或共享其个人信息. 用户可以通过将首选项 privacy.globalprivacycontrol.enabled 设置为 true (在 about:config 中)在正常和私密浏览模式下启用标头. Navigator.globalPrivacyControl 和 WorkerNavigator.globalPrivacyControl 属性允许 JavaScript 检查用户同意首选项. —— Firefox 120 for developers - Mozilla | MDN [存档] 那么这个 Sec-GPC 究竟是什么东西? 它为什么要表明用户对数据的请求意愿? 曾经和它相同愿景的 DNT 怎么样了? 长话短说, Sec-GPC 是 DNT 的继任者. Sec-GPC 这个标头起源于 2009 年提出的 DNT(Do-Not-Track), Firefox 是第一个支持 DNT 的浏览器, 但是目前 MDN 里面给出的提示是: 「不再推荐此功能. 尽管某些浏览器可能仍然支持它, 但它可能已从相关网络标准中删除, 可能正在被删除或者可能仅出于兼容性目的而保留.」, Sec-GPC 虽然也好不到哪里去: 「 此功能是非标准的, 并且不在标准轨道上. 不要在面向 Web 的生产站点上使用它: 它不适用于每个用户. 实现之间也可能存在很大的不兼容性, 并且行为将来可能会发生变化.」, DNT 被看作是一个「失败的网络试验」, 不过起码是有人提出了这种倡议, 并且确实存在也产生过影响. 对 Sec-GPC 的详细描述参见 Global Privacy Control 提案: Global Privacy Control (GPC) [存档] 现状 对于 DNT 在用户侧的最直接的表现就是浏览器里面的 &quot;请勿跟踪&quot; 选项: 即使最流行的浏览器都支持了该功能并且还引起了很多人的误会, 让人以为在浏览器中启用了这个选项就不会被跟踪了, 但实际上由于 DNT 最终依赖于服务方而非用户方. 流行且并且支持 DNT 的只有 Medium, Pinterest 和 Reddit. Yahoo 和 Twitter 曾经有对 DNT 的支持, 但现在已经全都是昨日黄花. 现在还支持 DNT 的全部加起来都是屈指可数. 一般性的 DNT 政策对于提供服务的平台都是写在隐私政策里面, 在这里可以看到所有目前已经实施了 DNT 政策的平台: Companies Archive - All About DNT [存档] DNT 的现状以至于让很多人认为 DNT 没有任何作用. 当然也有明确在政策里面写明不遵守的, 比如 Cygames 的隐私政策的 &quot;在线跟踪&quot; 里面写道: 某些 Web 浏览器和其他程序可能会被用来向我们传达您对我们或第三方如何或是否可以收集您在线活动信息的偏好. 目前对于如何响应 &quot;请勿跟踪&quot;(DNT) 信号尚无公认标准, 因此 Cygames 不支持 DNT 浏览器设置, 并且目前未参与任何可能允许我们响应由您发出的关于收集您个人信息或非个人信息的信号或其他机制的 DNT 框架. —— 隐私政策 | 【Shadowverse】官方网站 | Cygames [存档] 这也是大多数的做法, 比起隐私政策更像一种 &quot;免责声明&quot;. 那么, 为什么没有作用? DuckDuckGo 在内的大部分人都认为是法律的缺失, 所以 DuckDuckGo 在欧盟 GDPR 生效后的一年发表了《2019 年禁止跟踪法案》, 期望通过立法为这类技术提供程序性正义的保障. Do Not Track and the GDPR | 2018 | Blog | W3C [存档] 几乎没有平台遵守, 那么 DNT 是否失去了意义? 特别是对经营全球性服务的互联网公司来说? 但现在下结论还可能为时尚早: nostr:nevent1qqs0hx0rezzmk9w6fk7geawlns4tj0qkt32sf3adt6j7wzymwfqpl0saqxun4 但德国柏林地区法院裁决, 请勿追踪具有法律约束力. 此案与微软旗下的职业社交网络 LinkedIn 有关, 它在网站上明确表示: 由于目前没有 &quot;请勿追踪&quot; 的相关标准, 它对请勿追踪信号不予回应. 但德国消费者保护组织 vzbv 指出, 如果消费者启用了浏览器的请勿追踪功能, 那么他们发出了一个明确的信号: 不希望其上网行为被跟踪用于广告等目的, 网站运营者必须尊重这一信号. 法官同意了这一意见, 认为 LinkedIn 的声明具有误导性. —— 德国法庭裁决 Do Not Track 具有法律约束力 | 奇客Solidot [存档] 现在的 Sec-GPC 的状态是 &quot;规范提案&quot;, Mozilla 决定在 Firefox 中支持 Sec-GPC 表达了对这个提案的响应, 它也是第一个支持该特性的浏览器. DNT 和 Sec-GPC 的意义 对互联网用户而言, DNT 和 Sec-GPC 不是一个反追踪和隐私保护工具, 比起要达成更深远的隐私保护目的而言, 它只是一种比 &quot;Cookie 确认弹窗&quot; 更进步和现代的技术. 毕竟第三方 Cookie 马上就要走到了末路: 如果您的网站使用第三方 Cookie, 那么当我们即将弃用它们时, 就该采取行动了. Chrome 计划从 2024 年第一季度开始对 1% 的用户禁用第三方 Cookie, 以方便测试, 然后从 2024 年第三季度开始将覆盖范围扩大到 100%. 最终是否扩大到 100% 的用户范围取决于解决英国竞争与市场管理局(CMA)调查的竞争问题. —— Preparing for the end of third-party cookies - Chrome for Developers [存档] 是第三方 Cookie 消失了吗? 论形式和手段而言确实消失了, 但论达成目的而言并没有, 它们其实是 &quot;逃&quot; 到了新的地方, Google 为这类 &quot;逃离的第三方 Cookie&quot; 设计了一个新的环境: 隐私沙盒(Privacy Sandbox). 但隐私沙盒对用户而言又是一个在对手的规则里进行 &quot;公平对决&quot; 的擂台. 对互联网用户来说, 隐私是一种「筹码」, 是很多互联网服务的隐藏条件, 特别是依赖于对用户数据分析和货币化的公司而言. Google 推进隐私沙盒与第三方 Cookie 形式上的对抗引起的各国反垄断调查的的原因也只是: 隐私沙盒破坏了几乎所有不通过 Google 用于追踪和收集数据的方式(美国), 可能会对传统媒体和其他数字广告市场产生重大影响(英国). Privacy Sandbox #Antitrust concerns - Wikipedia [存档] 那么, 互联网用户该怎么办? 我们在这之前可以通过主动或被动避开第三方 Cookie, 隐私沙盒之后呢? 遗憾的是, 现在看来最让用户处于最 &quot;主动&quot; 地位的只有法律, 比如 GDPR, 加州消费者隐私法案(CCPA), 加州在线隐私保护法(CalOPPA)甚至美国儿童在线隐私保护法(COPPA). DNT 和 Sec-GPC 是相对于这些事后的纠纷依据更提前的事前申明, 让用户和平台之间直接进行隐私和数据「筹码」交易的协商, 这种功能和如今的 &quot;Cookie 确认弹窗&quot; 一模一样, 在更远的未来可能会将这种对 &quot;第三方&quot; 的约束扩展到第一方. 正如 DuckDuckGo 的 CEO 所说的 &quot;我们应该选择数据跟踪, 而不是拒绝数据跟踪.&quot;, 天下没有免费的午餐, 在互联网上这顿免费的 &quot;午餐&quot; 一切的代价要么是我们的财产要么是我们的隐私, DNT 和 Sec-GPC 是「交易」前的谈判, 隐私保护相关的法律是「交易」破裂后的仲裁. 我们作为互联网的消费者, 应该尽可能多地掌握主动权. 隐私为什么不能算作一种财产呢?","date_published":"2023-11-21T17:07:45.000Z","tags":["博物志","互联网","DNT","GPC","Firefox"]},{"id":"https://blog.cxplay.org/works/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/","url":"https://blog.cxplay.org/works/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/","title":"AdGuard 的产品线: 下一代内容拦截技术是什么?","content_html":"<blockquote>\n<p>Manifest V3 对于 AdGuard 来说完全是大顺风的优势, 因为它们不靠浏览器扩展盈利, 而是靠客户端程序许可. 虽然浏览器扩展几乎能应对 80% 的拦截需求, 但 Google 起了一个底, YouTube 开了个头, 对浏览器扩展的限制越来越大, 虽然 MV3 不是为了针对广告拦截器而设下的, 但来自 Chromium 主线的推动足够开始改变很多东西. 对广告拦截的目标也应该尽快提升到内容拦截.</p>\n</blockquote>\n<p>AdGuard 是商业化运作的软件公司, 创办于俄罗斯莫斯科(虽然目前总部已经迁到塞浦路斯), 旗下的所有免费产品都是开源的, 包括浏览器扩展, AdGuard Home 和 Android 专用内容拦截器. 它们的主要营利产品是: AdGuard 客户端, 私人 DNS, VPN.</p>\n<p>对浏览器操作了如指掌的电脑玩家绝对不会落下广告拦截器插件, 但要真正将广告拦截运用自如就要更加深入地了解了广告拦截器, 否则最后只能靠其他人写的过滤器规则来拦截自己看到的广告. 当广告拦截器和互联网广告主的对决开始, 这种滞后性会变得非常突出, 况且广告主自己还控制着浏览器内核的主导权呢?</p>\n<p>第一个网页广告和第一个广告拦截器几乎是统一时间出现, 自上世纪九十年代至今, 广告和广告拦截器的对决从没有停止过. 但是互联网广告诞生至今已经变得越来越多样, 而广告拦截器似乎始终以浏览器扩展插件为阵地, 这种滞后的局面何时才能迎来转变? 如果说在浏览器里用浏览器扩展拦截广告就是在 &quot;八角笼&quot; 里和广告公平互殴, 偶有优势. 但似乎已经让人忘记了, 这场广告和反广告的比赛的目的本应该是让我们保持主动并占据上风且不将对手打垮, 而不该是在对手的规则里展开他口里的 &quot;公平对决&quot;.</p>\n<p>笔者将依照自身亲身经验分析市场上最流行的, 依靠广告拦截而商业化运作的公司 AdGuard 的主要产品的特性, 得出它们产品的相对如今其他流量广告拦截器之间的区别, 以及存在的独特优势和依旧存在的局限性.</p>\n<blockquote>\n<p>比赛在即, 裁判却决定将跑道越缩越窄, 以前在赛场上还能靠人数优势, 前赴后继接力才能和另外骑着自行车的选手一较高下. 但现在跑道越来越窄, 人数优势变得越来越小, 回头发现对手的自行车已经早就换成了摩托, 细看之下, 这位选手似乎和刚才还在闲聊的裁判长得似乎有点相似... 比赛开始了, 你应该如果取胜?</p>\n</blockquote>\n<h2 id=\"内容拦截类\"><a href=\"#内容拦截类\"></a>内容拦截类</h2>\n<p>内容拦截是 AdGuard 主线产品的核心功能, 它们又分为浏览器扩展和客户端两类, 按照对过滤器特性的兼容性, 可以得出其内容拦截能力的高低:</p>\n<blockquote>\n<p>&quot;CoreLibs Apps&quot; 是指使用闭源库 CoreLibs 开发的 AdGuard 客户端, 目前在 Windows, MacOS 和 Android 进行了适配和发行, 是付费产品.</p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/image-20231115202352808.webp\" alt=\"基础修饰符兼容性\" loading='lazy'></p>\n<p>如果只使用基础的规则和修饰符, 那么基本上它们之间除了来自浏览器内核和程序访问能力的限制外, 没有什么区别.</p>\n<p>但是如果过滤的需求范围来到了浏览器扩展的能力范围之外, 区别就开始逐渐显现. 以下是 AdGuard 内容过滤规则高级修饰符的兼容性对比:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/image-20231115203800783.webp\" alt=\"高级修饰符兼容性\" loading='lazy'></p>\n<p>看起来优势也并不多? 但别忘了, 现在对比的范围已经离开浏览器这个约束范围了, 浏览器扩展和 CoreLibs 应用已经处于不同的应用层面.</p>\n<p>对内容的 &quot;拦截&quot; 开始过渡到 &quot;重写&quot; 和 &quot;替换&quot; 时, 就需要深入到传输层. 目前在传输层才能实现的高级修饰符有:</p>\n<ul>\n<li>\n<p><code>$hls</code>: 移除 HLS 列表中的内容片段.</p>\n</li>\n<li>\n<p><code>$jsonprune</code>: 移除 JSON 响应中的片段.</p>\n</li>\n<li>\n<p><code>$network</code>: 阻止特定 IP 的连接.</p>\n</li>\n<li>\n<p><code>$referrerpolicy</code>: 替换页面的 referrer 策略(Referrer-Policy).</p>\n</li>\n<li>\n<p><code>$replace</code>: 使用正则表达式替换响应内容中的字串符.</p>\n</li>\n</ul>\n<p>如果需要让 AdGuard 实现这些功能, 或者需要在浏览器之外继续过滤内容, 那么就需要使用基于 CoreLibs 的 AdGuard 客户端.</p>\n<h3 id=\"adguard-浏览器扩展\"><a href=\"#adguard-浏览器扩展\"></a>AdGuard 浏览器扩展</h3>\n<blockquote>\n<p>这里的浏览器扩展仅指能够独立实现内容拦截的浏览器扩展.</p>\n</blockquote>\n<p>这是对抗同类产品而设置的「守门员」, 免费且开源, 它的目标应当是引导用户去购买它们的付费产品. 与 uBlock Origin 之类的浏览器扩展来说并没有突出的优势, 在本章节开头的规则兼容性对比中就能看出, 浏览器扩展能力范围的拦截任务与其他流行的内容拦截扩展并没有什么独特优势. 但是文章如开头所说, MV3 的推进可能会改变这类扩展的格局.</p>\n<ul>\n<li><strong>AdGuard 浏览器扩展源代码:</strong><a href=\"https://github.com/AdguardTeam/AdguardBrowserExtension\">AdguardTeam/AdguardBrowserExtension: AdGuard browser extension</a> - GitHub</li>\n<li><strong>AdGuard for Safari 源代码:</strong> <a href=\"https://github.com/AdguardTeam/AdGuardForSafari\">AdguardTeam/AdGuardForSafari: AdGuard for Safari app extension</a> - GitHub</li>\n</ul>\n<h4 id=\"局限性\"><a href=\"#局限性\"></a>局限性</h4>\n<p>虽然浏览器扩展适配了主流的浏览器, 但实际上由于浏览器内核的限制, 只有 Chrome, 类 Chromium 和 Firefox 的扩展才能实现完整过滤器特性, 而例如 Safari 由于 WebKit 的限制无法实现完整的过滤器特性, AdGuard 也专门为 Safari 版本的 AdGuard 浏览器扩展开设了独立的发布页面:</p>\n<blockquote>\n<p><a href=\"https://adguard.com/en/versions/safari/release.html\">Safari Release - AdGuard versions</a></p>\n</blockquote>\n<p>AdGuard 团队也在它们的博客文章中解释了为什么 Safari 中的内容拦截扩展能力受限:</p>\n<blockquote>\n<p><a href=\"https://adguard.com/en/blog/youtube-ads-in-safari-explained.html\">YouTube ads in Safari: why do you see them and can they be removed?</a> - AdGuard Blog</p>\n</blockquote>\n<p>当然这是所有的内容拦截类浏览器扩展都要面对的问题, 如果需要完全限制这一类扩展, 那么就需要在浏览器内核上动手. 比如 Google 起头以 Chromium 为首而计划实施的:</p>\n<ul>\n<li>\n<p>Web Environment Integrity: <a href=\"https://brave.com/web-standards-at-brave/9-web-environment-integrity/\">&quot;Web Environment Integrity&quot;: Locking Down the Web | Brave</a></p>\n</li>\n<li>\n<p>Manifest v3: <a href=\"https://adguard.com/en/blog/how-ad-blocking-is-done.html\">How ad blocking is done and why it's in danger — AdGuard</a></p>\n</li>\n</ul>\n<p>虽然这两个提议并没有人认为就是为了针对广告拦截器, <a href=\"https://android-developers.googleblog.com/2023/11/increasing-trust-for-embedded-media.html\">Google 对 Chromium 中实现 Web Environment Integrity 的计划也已经破产</a>. 但很明显, 广告主对如今的广告拦截器很不满意, YouTube 也突然开始了反广告拦截器的行动. 虽然它肯定不是第一个反广告拦截的, 但 YouTube 的背后恰好还是 Google.</p>\n<blockquote>\n<p><a href=\"https://www.wired.com/story/youtubes-ad-blocker-crackdown-spurs-record-uninstalls/\">YouTube's Crackdown Spurs Record Uninstalls of Ad Blockers | WIRED</a></p>\n</blockquote>\n<p>连 <a href=\"https://www.securityweek.com/fbi-recommends-ad-blockers-cybercriminals-impersonate-brands-search-engine-ads/\">FBI 都推荐使用广告拦截器去拦截搜索引擎中搜索结果的广告</a>, 很明显这场「战斗」已经变得异常胶着, Google 的身影已经变得无处不在, 毕竟 <a href=\"https://www.visualcapitalist.com/cp/big-tech-revenue-profit-by-company/\">Google 的大部分收入都是靠广告</a>.</p>\n<blockquote>\n<p>如果不能阻止浏览器扩展拦截浏览器广告, 那么就离开浏览器, 比传统网页更适合广告的是移动互联网.</p>\n</blockquote>\n<h3 id=\"adguard-客户端\"><a href=\"#adguard-客户端\"></a>AdGuard 客户端</h3>\n<p>AdGuard 的客户端是相对于浏览器扩展而强化内容过滤能力的独立应用, 它实际上更类似于一个工作在浏览器之上的专用防火墙. 在使用 HTTPS 过滤前, 它的过滤能力还不如浏览器扩展, 因为浏览器中的扩展一样都工作在 OSI 模型中的第七层: 应用层, 并且它独立于浏览器也无法使用浏览器扩展才能使用的的 API. 此外如果不使用过滤功能, 它也还能仅充当 DNS 代理客户端.</p>\n<p>当配置完成 HTTPS 过滤, 系统默认信任来自 AdGuard 客户端的 SSL 证书后, 此时的它就开始使用 MiTM(中间人攻击) 的方式进行内容过滤, 工作方式开始介入 OSI 模型中的第四层: 传输层. 对仅使用 HTTPS 加密的流量实行完全的解密, 过滤和重写.</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/image-20231117080015388.webp\" alt=\"AdGuard for Windows 中的 HTTPS 过滤配置界面\" loading='lazy'></p>\n<p>桌面客户端与之配套的有一个用于控制客户端的浏览器扩展, 名叫 &quot;<a href=\"https://adguard.com/zh_cn/adguard-assistant/overview.html\">AdGuard 浏览器助手</a>&quot;, 可以在浏览器里控制外部客户端对指定站点的过滤, 还能充当元素选择器生成规则即时拦截广告元素.</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/image-20231117064728056.webp\" alt=\"与 AdGuard 客户端配套的浏览器扩展\" loading='lazy'></p>\n<h4 id=\"闭源之路\"><a href=\"#闭源之路\"></a>闭源之路</h4>\n<p>AdGuard 客户端目前仅在 Windows, Mac 和 Android 操作系统适配并发行. 它们都是基于的闭源库 AdGuard CoreLibs 开发的闭源产品.</p>\n<p>要注意的是, 目前 AdGuard 的 iOS 客户端只是用于应对移动端 Safari 内容拦截的应用, 它作用类似于 Safari 浏览器扩展, 定位接近于 Android 平台的「内容拦截器」, 它也不开源, 也不是基于 CoreLibs 开发的产品. 因此现在 AdGuard 唯一支持全功能过滤的移动端应用只有 Android 平台.</p>\n<ul>\n<li><strong>AdGuard Core C/C++ libraries 的 Issues 仓库:</strong> <a href=\"https://github.com/AdguardTeam/CoreLibs\">AdguardTeam/CoreLibs: Core Adguard libraries</a> - GitHub</li>\n</ul>\n<h4 id=\"规则优先\"><a href=\"#规则优先\"></a>规则优先</h4>\n<p>客户端在多平台都有实现和发行, 它的过滤能力也完全取决于规则. 虽然客户端中有许多功能都是能以开关或选择的形式让用户轻松就能使用, 比如 DNS 代理, 隐形模式和浏览安全等模块, 但这些功能实际上都与内容拦截没有关系, 它们并不能帮助用户拦截更多的广告, 更多的只能算是一种加强上网安全和隐私的附加功能.</p>\n<p>为了让不精通技术的用户也能轻松使用客户端, 在 DNS 过滤器和内容过滤器的都添加了可供用户直接选择并使用的过滤规则订阅, 并且客户端安装完毕实际上就默认启用了数个 AdGuard 直接负责维护的过滤器订阅. 这种配置能够应对绝大多数的内容过滤的需求.</p>\n<p>以规则为核心实现过滤的客户端中, 有两种类似但并不完全相同的 AdGuard 规则:</p>\n<ul>\n<li>DNS 过滤规则</li>\n<li>内容过滤规则</li>\n</ul>\n<p>前者仅作用于 DNS, 在 DNS 层面的域名解析过滤拦截和重写. 后者作用于 HTTP 和 HTTPS, 对应用的请求和内容进行过滤拦截和重写, 它们使用相似的规则语法, 主要区别在于修饰符, DNS 过滤规则语法中拥有与其能力符合的独有修饰符, 比如 <code>$dnstype</code> 和 <code>$dnsrewrite</code>, 详见 DNS 过滤规则文档:</p>\n<blockquote>\n<p><a href=\"https://adguard-dns.io/kb/zh-CN/general/dns-filtering-syntax/\">DNS 过滤规则语法 | AdGuard DNS Knowledge Base</a></p>\n</blockquote>\n<p>又由于 DNS 规则语法几乎与内容拦截规则相同(除了部分修饰符), 所以基本上来说 DNS 过滤规则也能直接当作内容过滤规则使用. 在实际使用的时候, 如果能够在 DNS 请求时就能将内容进行拦截, 那么就应该优先使用 DNS 过滤.</p>\n<h4 id=\"过滤行为\"><a href=\"#过滤行为\"></a>过滤行为</h4>\n<p>本节开头说过, AdGuard 客户端实际上是一个防火墙. 但目前在 Windows 上只会选择性过滤在其设置中加入过滤名单中的应用程序, 客户端会默认添加部分常见的需要过滤的引用程序, 比如各种浏览器(Edge, Chrome/类 Chromium, IE), Steam, Potplayer 等等. 如果需要过滤不在其中的应用程序, 那么就需要手动添加到名单中, .</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/image-20231115213849773.webp\" alt=\"选择性过滤的名单\" loading='lazy'></p>\n<p>在 Android 平台上略有不同, AdGuard 的 Android 客户端是一个更全面的防火墙, 不仅支持网络访问控制, 还会默认对所有应用程序进行过滤, 包括新安装的甚至系统应用, 而 MacOS 上的 AdGuard 过滤行为介于 Windows 和 Android 之间, 这是由于不同操作系统使用的不同过滤技术导致的:</p>\n<ul>\n<li><strong>Windows:</strong> <a href=\"https://learn.microsoft.com/en-us/windows/win32/fwp/windows-filtering-platform-start-page\">WFP</a>, <a href=\"https://en.wikipedia.org/wiki/Transport_Driver_Interface\">TDI</a>, HTTP 代理.</li>\n<li><strong>Android:</strong> <a href=\"https://developer.android.com/reference/android/net/VpnService\">VPNService</a>, HTTP 代理.</li>\n<li><strong>MacOS:</strong> <a href=\"https://developer.apple.com/documentation/networkextension\">Network Extension</a>, <a href=\"https://zh.wikipedia.org/wiki/%E5%8F%AF%E8%BC%89%E5%85%A5%E6%A0%B8%E5%BF%83%E6%A8%A1%E7%B5%84\">Kernel Extension</a>, HTTP 代理.</li>\n</ul>\n<p>基本上来说, AdGuard 所有已实现并发行的客户端中, Android 和 MacOS  都在使用 VPN 或类 VPN 的方式过滤网络流量, 这在配置完成 HTTPS 过滤的情况下能够提供最全面的过滤特性和效果. 但 Windows 是一个例外, 如果没有在名单之中的程序就不会被过滤, 即使现在使用的 WFP 技术完全能够直接做到防火墙一样的控制, 但不知道为什么没有做. 不过在 Windows 客户端的 GitHub Issue 队列中, 过滤全局的流量已经被提上 v8.0 版本的日程, 而且优先级非常高(P2):</p>\n<blockquote>\n<p><a href=\"https://github.com/AdguardTeam/AdguardForWindows/issues/4732\">Filter All Traffic By Default · Issue #4732 · AdguardTeam/AdguardForWindows</a> - GitHub</p>\n</blockquote>\n<h4 id=\"局限性-2\"><a href=\"#局限性-2\"></a>局限性</h4>\n<p>面对深入到传输层的内容拦截, 难道就是广告拦截的万能钥匙了吗? 并不, 如果了解了 AdGuard 实现过滤的方式就能「反击」:</p>\n<ul>\n<li><strong>SSL 证书验证</strong>: HTTPS 过滤依赖 SSL 证书, 应用可以实行证书验证, 比如常见的 <a href=\"https://zh.wikipedia.org/zh-cn/HTTP%E5%85%AC%E9%92%A5%E5%9B%BA%E5%AE%9A\">HPKP</a>(HTTP公钥固定) 和<a href=\"https://zh.wikipedia.org/zh-hans/%E8%AF%81%E4%B9%A6%E9%80%8F%E6%98%8E%E5%BA%A6\">证书透明度</a>(CT)检查.</li>\n<li><strong>重新编码或使用加密协议</strong>: AdGuard 虽然能够解密 HTTPS, 但目前无法对解密后的响应或请求再次解码, 只需要对重要的响应内容进行二次编码, 即使是 base64 都能够使用于处理数据的过滤器规则失去作用. 或者进一步直接使用加密协议, 在端对端加密盛行的今天, 这也是很常见的做法.</li>\n<li><strong>提前代理流量</strong>: 应用能够直接内置 VPN 或其他技术的流量代理, 在流量到达拦截器之前进行伪装或加密. 当然任何应用也能做到要求用户通过 VPN 才能访问对应的网络资源.</li>\n</ul>\n<h3 id=\"专用内容拦截器\"><a href=\"#专用内容拦截器\"></a>专用内容拦截器</h3>\n<p>专用内容拦截器主要是指通过其他软件提供的 API 而非通过系统网络流量进行针对性内容拦截的拦截器, 虽然 AdGuard 自己只把 Android 上还存在于 Google Play 上的那款 AdGuard 叫做 &quot;内容拦截器&quot;(目前仅适用于三星浏览器和 Yandex 浏览器). 实际上 iOS 平台的 AdGuard 客户端也是这类专用内容拦截器, 因为它只能用于 Safari 浏览器内的内容拦截, 通过规则修饰符的兼容性来看, Android 专用内容拦截器是所有 AdGuard 内容拦截器中效果最差的, 其次就是 iOS 客户端和 Safari 浏览器扩展(两者一模一样).</p>\n<blockquote>\n<p>在 Google 的 Manifest v3 执行后, 浏览器扩展类的内容拦截器按照运行方式或许应该也要被归类为专用内容拦截器. 不过在浏览器内这种特定环境下, MV3 的浏览器扩展拦截器对过滤规则修饰符的兼容性依旧比现有的专用内容拦截器好得多.</p>\n</blockquote>\n<h2 id=\"adguard-dns-adguard-home\"><a href=\"#adguard-dns-adguard-home\"></a>AdGuard DNS &amp; AdGuard Home</h2>\n<p>AdGuard DNS 是由 AdGuard 托管的私人 DNS 服务, 同类产品有 NextDNS, RethinkDNS, NovaXNS. 提供 DoT, DoH, DoQ 加密 DNS 服务, 可设置自定义的 DNS 过滤规则. 计费方式是订阅制, 也提供免费使用的基本层.</p>\n<ul>\n<li><strong>AdGuard C++ DNS libraries 源代码:</strong> <a href=\"https://github.com/AdguardTeam/DnsLibs\">AdguardTeam/DnsLibs: DNS filtering library that's used in AdGuard products</a> - GitHub</li>\n<li><strong>DNS Proxy 源代码:</strong> <a href=\"https://github.com/AdguardTeam/dnsproxy\">AdguardTeam/dnsproxy: Simple DNS proxy with DoH, DoT, DoQ and DNSCrypt support</a> - GitHub</li>\n<li><strong>AdGuard Home 源代码:</strong> <a href=\"https://github.com/AdguardTeam/AdGuardHome\">AdguardTeam/AdGuardHome: Network-wide ads &amp; trackers blocking DNS server</a> - GitHub</li>\n</ul>\n<h3 id=\"同宗同源不同样\"><a href=\"#同宗同源不同样\"></a>同宗同源不同样</h3>\n<p>AdGuard Home 是 AdGuard DNS 依赖软件的简化版, 它们都基于相同的开源库, 但在其基础上构建的产品一个是闭源的专有软件, 一个是使用 GPL 3.0 开源许可发行的自由软件. Home 是供自托管使用的 DNS 中继服务器软件, 对个人和家庭局域网环境还提供了 DHCP 服务器的功能, Home 支持几乎所有的传统和现代安全 DNS(除了 DNSCrypt): 明文, DoT, DoH, DoQ; 支持 DNSSEC, RDNS 和 ECS 扩展. 同样 Home 也支持自定义 DNS 过滤规则, 甚至能够订阅 DNS 过滤规则列表(闭源产品 AdGuard DNS 仅支持订阅通过验证的列表). 虽然 Home 是 AdGuard DNS 的简化版, 但并不是用来和它竞争的对手, Home 仅提供软件, 而 AdGuard DNS 是一整套的解决方案, Home 经常被拿来比较的对手是 Pi-Hole.</p>\n<h3 id=\"如果只是内容拦截\"><a href=\"#如果只是内容拦截\"></a>如果只是内容拦截</h3>\n<p>DNS 简单又脆弱, 是网络攻击中经常被针对的互联网基础设施. 它是人类访问互联网的必经之路, 为了让 DNS 正确无误且安静地工作, 延伸出了很多安全 DNS 协议, 发展到现在 DoH(H2/H3) 和 DoQ, 也终于在速度, 安全和易用之间达到了平衡. 由于大部分的广告, 追踪和恶意内容为了在线上传播也要依靠 DNS, 所以基于 DNS 最初原型 &quot;Hosts 文件&quot; 衍生发展出了如今的 DNS 过滤规则,  对通过 DNS 的恶意域名进行错误的 DNS 响应, 这样就能针对性地利用 DNS 的特性实现了粗略的内容拦截功能.</p>\n<p>所谓粗略, 因为 DNS 只能对域名进行作用, 如果所有的广告都用自己的独特域名, 那么在 DNS 层面进行内容拦截早该屡试不爽. 但实际情况是越来越多的恶意内容被掺杂在正常的域名中进行请求和响应, 更有的广告直接被写在了请求响应中, 和正常内容混杂在一起. 对于这种情况如果还使用 DNS 进行拦截只会 &quot;杀敌一千自损八百&quot;, 破坏整个在线服务体验的完整性.</p>\n<p>DNS 设计之初并没有考虑内容拦截, 它只是恰好能做到在应用层进行 &quot;疏&quot; 和 &quot;堵&quot; 的系统而已.</p>\n<blockquote>\n<p>早年的 HTTP 时代, DNS 非常容易被 &quot;污染&quot; 导致流量被劫持传输恶意内容, 并且难以被及时发现. 后来 TLS/SSL 让 HTTP 进化到了 HTTPS, 结合 CA 证书链已经很少让 DNS 污染导致流量劫持难以察觉了. 到了如今 &quot;合法&quot; 的恶意内容盛行的现代互联网, DNS 劫持开始被部分互联网用户反向利用来针对这些恶意在线服务的域名, 也促使诞生了 AdGuard DNS, NextDNS, Quad9 等等一类 &quot;安全 DNS&quot;, 这些依旧是内容拦截对抗广告的一个战场, 只是战况不太激烈而已. DNS 也随着 HTTP 进化, 越来越难以被干扰, DNS 查询特征也变得越来越隐秘, 大多数的操作系统甚至应用都开始内置 DNS 客户端进行独立的 DNS 查询.</p>\n</blockquote>\n<h2 id=\"adguard-vpn\"><a href=\"#adguard-vpn\"></a>AdGuard VPN</h2>\n<p>这可能是相对同类竞争对手来说最没有特色的产品, 市面上的 VPN 已经早就变得泛滥. AdGuard 的 VPN 并没有被宣传为突破审查, 突破封锁, 也没有标榜流媒体专用, 也没有明说可以用于 P2P 传输. 对于这类产品的爱好者来说, AdGuard VPN 几乎不会被考虑, 更多的时候是和 AdGuard 的其他产品捆绑销售, 比如 AdGuard DNS. 当然, 在 AdGuard 品牌和营销的加持下, 也会变成泛泛之辈中值得留意的选择.</p>\n<p>AdGuard VPN 自一开始就没有选择主流的协议, 而是使用了自己开发的新协议, 他们也在自己的文章中表示了自己的独特协议具有的优势, 并且表示相比其他协议更难以被探测.</p>\n<blockquote>\n<p><a href=\"https://adguard-vpn.com/zh_cn/blog/adguard-vpn-protocol.html\">协议如何运行：深入了解独特的 AdGuard VPN 协议</a>  - AdGuard VPN Blog</p>\n</blockquote>\n<p>但是, 在众多加密网络代理协议的实践下证明: 私有协议难以被检测不是因为它完美无瑕, 只不过是因为这种协议普及程度不及针对性检测和封锁的必要性. 笔者并不认为 AdGuard 会加入这场猫鼠游戏之中, 把它当作是自卖自夸的口号足矣.</p>\n<p>AdGuard 也在他们客户端系列产品中加入对 VPN 产品的联通, 在移动设备上和桌面设备上 AdGuard 客户端和 AdGuard VPN 相互配合使用也比其他的 VPN 组合更顺畅一些.</p>\n<blockquote>\n<p>这类平庸的 VPN 产品最大的对手应该是 Cloudflare Warp, 以及各类安全产品中提供网络保护用途的 VPN, 比如 Apple One 提供的 VPN, Google One 提供的 VPN; 以及传统反病毒公司 Norton(诺顿), Kaspersky(卡巴斯基)在高级订阅套餐中提供的网络保护功能. 对于软件集成, 交互体验和服务体验, 这些实力干瘪的独立 VPN 服务几乎没有优势(或许独立销售也是种优势).</p>\n</blockquote>\n<h2 id=\"有趣的事实\"><a href=\"#有趣的事实\"></a>有趣的事实</h2>\n<h3 id=\"google-amazon-和-apple-拒绝上架-adguard-客户端的原因是相同的\"><a href=\"#google-amazon-和-apple-拒绝上架-adguard-客户端的原因是相同的\"></a>Google, Amazon 和 Apple 拒绝上架 AdGuard 客户端的原因是相同的</h3>\n<p>现在去 Google Play, Amazon Appstore 和 App Store 上下载 AdGuard 是不可能下载到包含全功能的 AdGuard 客户端的.</p>\n<blockquote>\n<p>Amazon Appstore 是 Amazon(亚马逊) 运营的一个 Android 软件商店, 类似于 Google Play.</p>\n</blockquote>\n<p>2014 年开始到 2018 年的 5 年间, 三家主要的移动应用商店先后实行了新政策使得 AdGuard 的全功能客户端被迫下架, 这三位给出的政策理由如出一辙: <strong>干扰或阻止其他第三方应用程序显示广告的应用程序不会被允许上架.</strong></p>\n<ul>\n<li><strong>2014 年, Google Play 下架</strong>: <a href=\"https://adguard.com/en/blog/adguard-google-play-removal.html\">Why is AdGuard not on Google Play?</a> - AdGuard Blog</li>\n<li><strong>2018 年 5 月, Amazon Appstore 下架:</strong> <a href=\"https://adguard.com/en/blog/goodbye-amazon.html\">Goodbye, Amazon</a> - AdGuard Blog</li>\n<li><strong>2018 年 7 月, iOS 版本由于 App Store 政策更新而陷入停滞:</strong> <a href=\"https://adguard.com/en/blog/adguard-pro-discontinued.html\">AdGuard Pro for iOS in its current form will be discontinued due to Apple's policy</a> - AdGuard Blog\n<ul>\n<li><strong>2019 年 7 月, App Store 的 Pro 版本功能改动(移除网络层级过滤), 与另外一个与 Safari 专用的 iOS 版本开始趋近:</strong> <a href=\"https://adguard.com/en/blog/adguard-pro-is-back.html\">AdGuard Pro for iOS is back to block ads across the system once again!</a> - AdGuard Blog</li>\n</ul>\n</li>\n</ul>\n<p>所以, 很久之前, 至少 2018 年以前, iOS 版本的 AdGuard 都能和如今的 Android, MacOS, Windows 版本的 AdGuard 一样进行系统层面 HTTPS 过滤, 那时的 AdGuard Pro for iOS 对于 AdGuard for iOS 来说的确是具有更高级功能的版本. 但如今的 App Store 上的两个 AdGuard 客户端都只是 Safari 专用的内容拦截器而已.</p>\n<blockquote>\n<p>这三家控制着全球移动应用发行的巨头不仅是软件或硬件巨头, 更是广告巨头, 他们比任何软件开发者都清楚广告到底对自己, 对互联网究竟意味着什么.</p>\n</blockquote>\n<h3 id=\"起源于俄罗斯的麻烦\"><a href=\"#起源于俄罗斯的麻烦\"></a>起源于俄罗斯的麻烦</h3>\n<p>互联网不是一个独立于现实世界的虚拟世界, 因此互联网产品也无法完全摆脱来自现实世界的引力, 特别是现实之中来自人的恶意.</p>\n<p>起源于俄罗斯的 AdGuard 很早就明白了这一点, 他们在 2009 年成立公司的 5 年后就将总部迁移到了欧洲的塞浦路斯, 一方面为了避免原始身份的猜疑, 另一方面也趋近了来自政策层面对它们主营业务的适宜: GDPR.</p>\n<blockquote>\n<p>除去一些人尽皆知的商业和资本考量, 塞浦路斯本身的地理位置和政治占位也是商业公司总部的好选择之一, 特别是经营全球性产品的公司.</p>\n</blockquote>\n<p>AdGuard 虽然运营的位置变化了, 但运营和开发的人员依旧不少是俄罗斯籍的, 这一点无法轻易改变.</p>\n<p>时间来到了 2022 年, 乌俄战争又让这个隐患爆发, 这场战争不仅卷入了士兵和平民, 还包括无数与这两个国家直接或间接关联的其他国家, 无数人被要求表态, 被要求站队, 现实的战争动员了一场互联网的战争, 没有人知道他们的动机和目的.</p>\n<p>来自乌克兰的软件公司 MacPaw 运营的 MacOS 软件套装订阅服务 Setapp 在 3 月 10 日突然<a href=\"https://www.reddit.com/r/macapps/comments/tackvn/received_this_from_setapp/\">告知用户</a>, &quot;为了应对俄罗斯对乌克兰的入侵&quot; Setapp 在他们的软件套装中下架了 AdGuard, 意味着订阅 Setapp 的用户无法再获得 AdGuard 的授权, 随后有用户表示需要 AdGuard 尽快解决这个 &quot;俄罗斯问题&quot;:</p>\n<blockquote>\n<p><a href=\"https://www.reddit.com/r/Adguard/comments/taf1y4/adguard_needs_to_address_the_russian_issue_now/\">AdGuard needs to address the Russian issue now : r/Adguard</a> - Reddit</p>\n</blockquote>\n<p>当天 AdGuard 团队就发表了事件回复, 表示在这个时间前订阅了 Setapp 的客户可以凭收据兑换为期一年的 AdGuard 个人许可:</p>\n<blockquote>\n<ul>\n<li><a href=\"https://adguard.com/en/blog/official-response-to-setapp.html\">Official response from AdGuard to SetApp allegations.</a> - AdGuard Blog</li>\n<li><a href=\"https://www.reddit.com/r/Adguard/comments/tal21m/official_response_from_adguard_to_setapp/\">Official response from AdGuard to SetApp allegations : r/Adguard</a> - Reddit</li>\n</ul>\n</blockquote>\n<p>虽然 AdGuard 很早(2 月 25 日)就对乌俄战争的话题发表了看法, 表示希望尽快结束战争, 因为乌克兰有他们的支持团队以及他们的家人和朋友:</p>\n<blockquote>\n<p><a href=\"https://www.reddit.com/r/Adguard/comments/t15gr4/announcement_on_the_topic_of_the_war_in_ukraine/\">Announcement on the Topic of the War in Ukraine : r/Adguard</a> - Reddit</p>\n</blockquote>\n<p>而 Setapp 自此之后也就没有了回应, AdGuard 自 2020 年加入 Setapp 订阅一直到该事件, 没人会意想到会闹出这样的问题.</p>\n<hr>\n<p>之后过去了 6 个月, 事态依旧如此. 有 Mac 用户发现 Setapp 运营公司 MacPaw 开发的某款 MacOS 文件清理软件(CleanMyMac X)还将 AdGuard for Safari 例为了 &quot;可疑&quot; 软件, 并且明确显示了这是由于 &quot;该软件由俄罗斯人开发或拥有&quot; 导致:</p>\n<blockquote>\n<p><a href=\"https://forums.macrumors.com/threads/is-adguard-spyware.2358658/\">Is AdGuard Spyware? | MacRumors Forums</a></p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/image-20231117063939626.webp\" alt=\"Is AdGuard Spyware?\" loading='lazy'></p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/from-the-adguard-product-line-talking-about-what-the-next-generation-of-content-blocking-technology-will-be/image-20231117084129410.webp\" alt=\"&quot;这些应用程序与俄罗斯或白俄罗斯的开发商有关联或归其所有, 政府当局可以根据请求直接访问他们的数据, 而无需法院命令.&quot;\" loading='lazy'></p>\n<hr>\n<p>直到现在, Setapp 也没有任何表示, 他们的订阅软件列表中依旧没有 AdGuard.</p>\n<h2 id=\"结语-内容拦截的「无限战争」\"><a href=\"#结语-内容拦截的「无限战争」\"></a>结语: 内容拦截的「无限战争」</h2>\n<p>广告拦截已经从最初的 Hosts, DNS, 到达了浏览器扩展. 如今越来越复杂和无孔不入的广告和追踪证明 DNS 层面的过滤只是一种越来越难起到作用的手段, 直到浏览器扩展的出现大多数人都度过了和广告相安无事的几年, 直到一些隐私和安全的话题把广告拦截升级成一种意识上的正义, 广告拦截自此变得广泛, 更应该用 &quot;内容拦截&quot; 来形容. 从田园年代的贴片广告到现在的 Cookies, 追踪器, 侵入式内容, 深度包检测, 内容拦截逐渐从被动接受后清除, 变为主动从网络层面直接剔除和修改, 内容拦截的深度和广度已经不是如今的浏览器扩展能够完全胜任的了, 很多的 &quot;广告&quot; 已经从浏览器逃离, 藏进了更深的地方, 藏进了浏览器内核, 藏进了操作系统, 藏进了智能家居, 藏进了网络运营商...</p>\n<p>内容拦截对互联网用户来说意味着一种选择的主动权利, 能够选择「我愿意看到什么」和「我希望给你什么」的权利. 如果隐私是种资产, 那么资产所有者理应拥有控制资产和把它作为交易「筹码」进行讨价还价的权利, 在主动出让之前不应该被无缘无故浪费和挟持. 如果互联网完全赋予了用户这种权利, 那么内容拦截器自然就没有了存在的必要, 可惜现在它的必要性已经越发凸显, 甚至多次推到了风口浪尖.</p>\n<p>即使是如今采用更加激进过滤手段的 AdGuard 客户端, 依旧还在和 &quot;广告&quot; 苦斗着, 除了软件开发者还有无数默默无闻的过滤器规则维护者, 虽然苦战没有尽头, 但证明了浏览器扩展之后的下一代内容拦截技术或许应该是: <strong>MiTM 代理过滤</strong>.</p>\n<p>虽然不完美, 甚至手段可能遭人质疑正当性, 但至少现在有了新的办法, 并且是有效的.</p>\n<p>那么在这之后, 内容拦截技术的下一代又会是什么呢?</p>\n","content_text":"Manifest V3 对于 AdGuard 来说完全是大顺风的优势, 因为它们不靠浏览器扩展盈利, 而是靠客户端程序许可. 虽然浏览器扩展几乎能应对 80% 的拦截需求, 但 Google 起了一个底, YouTube 开了个头, 对浏览器扩展的限制越来越大, 虽然 MV3 不是为了针对广告拦截器而设下的, 但来自 Chromium 主线的推动足够开始改变很多东西. 对广告拦截的目标也应该尽快提升到内容拦截. AdGuard 是商业化运作的软件公司, 创办于俄罗斯莫斯科(虽然目前总部已经迁到塞浦路斯), 旗下的所有免费产品都是开源的, 包括浏览器扩展, AdGuard Home 和 Android 专用内容拦截器. 它们的主要营利产品是: AdGuard 客户端, 私人 DNS, VPN. 对浏览器操作了如指掌的电脑玩家绝对不会落下广告拦截器插件, 但要真正将广告拦截运用自如就要更加深入地了解了广告拦截器, 否则最后只能靠其他人写的过滤器规则来拦截自己看到的广告. 当广告拦截器和互联网广告主的对决开始, 这种滞后性会变得非常突出, 况且广告主自己还控制着浏览器内核的主导权呢? 第一个网页广告和第一个广告拦截器几乎是统一时间出现, 自上世纪九十年代至今, 广告和广告拦截器的对决从没有停止过. 但是互联网广告诞生至今已经变得越来越多样, 而广告拦截器似乎始终以浏览器扩展插件为阵地, 这种滞后的局面何时才能迎来转变? 如果说在浏览器里用浏览器扩展拦截广告就是在 &quot;八角笼&quot; 里和广告公平互殴, 偶有优势. 但似乎已经让人忘记了, 这场广告和反广告的比赛的目的本应该是让我们保持主动并占据上风且不将对手打垮, 而不该是在对手的规则里展开他口里的 &quot;公平对决&quot;. 笔者将依照自身亲身经验分析市场上最流行的, 依靠广告拦截而商业化运作的公司 AdGuard 的主要产品的特性, 得出它们产品的相对如今其他流量广告拦截器之间的区别, 以及存在的独特优势和依旧存在的局限性. 比赛在即, 裁判却决定将跑道越缩越窄, 以前在赛场上还能靠人数优势, 前赴后继接力才能和另外骑着自行车的选手一较高下. 但现在跑道越来越窄, 人数优势变得越来越小, 回头发现对手的自行车已经早就换成了摩托, 细看之下, 这位选手似乎和刚才还在闲聊的裁判长得似乎有点相似... 比赛开始了, 你应该如果取胜? 内容拦截类 内容拦截是 AdGuard 主线产品的核心功能, 它们又分为浏览器扩展和客户端两类, 按照对过滤器特性的兼容性, 可以得出其内容拦截能力的高低: &quot;CoreLibs Apps&quot; 是指使用闭源库 CoreLibs 开发的 AdGuard 客户端, 目前在 Windows, MacOS 和 Android 进行了适配和发行, 是付费产品. 如果只使用基础的规则和修饰符, 那么基本上它们之间除了来自浏览器内核和程序访问能力的限制外, 没有什么区别. 但是如果过滤的需求范围来到了浏览器扩展的能力范围之外, 区别就开始逐渐显现. 以下是 AdGuard 内容过滤规则高级修饰符的兼容性对比: 看起来优势也并不多? 但别忘了, 现在对比的范围已经离开浏览器这个约束范围了, 浏览器扩展和 CoreLibs 应用已经处于不同的应用层面. 对内容的 &quot;拦截&quot; 开始过渡到 &quot;重写&quot; 和 &quot;替换&quot; 时, 就需要深入到传输层. 目前在传输层才能实现的高级修饰符有: $hls: 移除 HLS 列表中的内容片段. $jsonprune: 移除 JSON 响应中的片段. $network: 阻止特定 IP 的连接. $referrerpolicy: 替换页面的 referrer 策略(Referrer-Policy). $replace: 使用正则表达式替换响应内容中的字串符. 如果需要让 AdGuard 实现这些功能, 或者需要在浏览器之外继续过滤内容, 那么就需要使用基于 CoreLibs 的 AdGuard 客户端. AdGuard 浏览器扩展 这里的浏览器扩展仅指能够独立实现内容拦截的浏览器扩展. 这是对抗同类产品而设置的「守门员」, 免费且开源, 它的目标应当是引导用户去购买它们的付费产品. 与 uBlock Origin 之类的浏览器扩展来说并没有突出的优势, 在本章节开头的规则兼容性对比中就能看出, 浏览器扩展能力范围的拦截任务与其他流行的内容拦截扩展并没有什么独特优势. 但是文章如开头所说, MV3 的推进可能会改变这类扩展的格局. AdGuard 浏览器扩展源代码:AdguardTeam/AdguardBrowserExtension: AdGuard browser extension - GitHub AdGuard for Safari 源代码: AdguardTeam/AdGuardForSafari: AdGuard for Safari app extension - GitHub 局限性 虽然浏览器扩展适配了主流的浏览器, 但实际上由于浏览器内核的限制, 只有 Chrome, 类 Chromium 和 Firefox 的扩展才能实现完整过滤器特性, 而例如 Safari 由于 WebKit 的限制无法实现完整的过滤器特性, AdGuard 也专门为 Safari 版本的 AdGuard 浏览器扩展开设了独立的发布页面: Safari Release - AdGuard versions AdGuard 团队也在它们的博客文章中解释了为什么 Safari 中的内容拦截扩展能力受限: YouTube ads in Safari: why do you see them and can they be removed? - AdGuard Blog 当然这是所有的内容拦截类浏览器扩展都要面对的问题, 如果需要完全限制这一类扩展, 那么就需要在浏览器内核上动手. 比如 Google 起头以 Chromium 为首而计划实施的: Web Environment Integrity: &quot;Web Environment Integrity&quot;: Locking Down the Web | Brave Manifest v3: How ad blocking is done and why it's in danger — AdGuard 虽然这两个提议并没有人认为就是为了针对广告拦截器, Google 对 Chromium 中实现 Web Environment Integrity 的计划也已经破产. 但很明显, 广告主对如今的广告拦截器很不满意, YouTube 也突然开始了反广告拦截器的行动. 虽然它肯定不是第一个反广告拦截的, 但 YouTube 的背后恰好还是 Google. YouTube's Crackdown Spurs Record Uninstalls of Ad Blockers | WIRED 连 FBI 都推荐使用广告拦截器去拦截搜索引擎中搜索结果的广告, 很明显这场「战斗」已经变得异常胶着, Google 的身影已经变得无处不在, 毕竟 Google 的大部分收入都是靠广告. 如果不能阻止浏览器扩展拦截浏览器广告, 那么就离开浏览器, 比传统网页更适合广告的是移动互联网. AdGuard 客户端 AdGuard 的客户端是相对于浏览器扩展而强化内容过滤能力的独立应用, 它实际上更类似于一个工作在浏览器之上的专用防火墙. 在使用 HTTPS 过滤前, 它的过滤能力还不如浏览器扩展, 因为浏览器中的扩展一样都工作在 OSI 模型中的第七层: 应用层, 并且它独立于浏览器也无法使用浏览器扩展才能使用的的 API. 此外如果不使用过滤功能, 它也还能仅充当 DNS 代理客户端. 当配置完成 HTTPS 过滤, 系统默认信任来自 AdGuard 客户端的 SSL 证书后, 此时的它就开始使用 MiTM(中间人攻击) 的方式进行内容过滤, 工作方式开始介入 OSI 模型中的第四层: 传输层. 对仅使用 HTTPS 加密的流量实行完全的解密, 过滤和重写. 桌面客户端与之配套的有一个用于控制客户端的浏览器扩展, 名叫 &quot;AdGuard 浏览器助手&quot;, 可以在浏览器里控制外部客户端对指定站点的过滤, 还能充当元素选择器生成规则即时拦截广告元素. 闭源之路 AdGuard 客户端目前仅在 Windows, Mac 和 Android 操作系统适配并发行. 它们都是基于的闭源库 AdGuard CoreLibs 开发的闭源产品. 要注意的是, 目前 AdGuard 的 iOS 客户端只是用于应对移动端 Safari 内容拦截的应用, 它作用类似于 Safari 浏览器扩展, 定位接近于 Android 平台的「内容拦截器」, 它也不开源, 也不是基于 CoreLibs 开发的产品. 因此现在 AdGuard 唯一支持全功能过滤的移动端应用只有 Android 平台. AdGuard Core C/C++ libraries 的 Issues 仓库: AdguardTeam/CoreLibs: Core Adguard libraries - GitHub 规则优先 客户端在多平台都有实现和发行, 它的过滤能力也完全取决于规则. 虽然客户端中有许多功能都是能以开关或选择的形式让用户轻松就能使用, 比如 DNS 代理, 隐形模式和浏览安全等模块, 但这些功能实际上都与内容拦截没有关系, 它们并不能帮助用户拦截更多的广告, 更多的只能算是一种加强上网安全和隐私的附加功能. 为了让不精通技术的用户也能轻松使用客户端, 在 DNS 过滤器和内容过滤器的都添加了可供用户直接选择并使用的过滤规则订阅, 并且客户端安装完毕实际上就默认启用了数个 AdGuard 直接负责维护的过滤器订阅. 这种配置能够应对绝大多数的内容过滤的需求. 以规则为核心实现过滤的客户端中, 有两种类似但并不完全相同的 AdGuard 规则: DNS 过滤规则 内容过滤规则 前者仅作用于 DNS, 在 DNS 层面的域名解析过滤拦截和重写. 后者作用于 HTTP 和 HTTPS, 对应用的请求和内容进行过滤拦截和重写, 它们使用相似的规则语法, 主要区别在于修饰符, DNS 过滤规则语法中拥有与其能力符合的独有修饰符, 比如 $dnstype 和 $dnsrewrite, 详见 DNS 过滤规则文档: DNS 过滤规则语法 | AdGuard DNS Knowledge Base 又由于 DNS 规则语法几乎与内容拦截规则相同(除了部分修饰符), 所以基本上来说 DNS 过滤规则也能直接当作内容过滤规则使用. 在实际使用的时候, 如果能够在 DNS 请求时就能将内容进行拦截, 那么就应该优先使用 DNS 过滤. 过滤行为 本节开头说过, AdGuard 客户端实际上是一个防火墙. 但目前在 Windows 上只会选择性过滤在其设置中加入过滤名单中的应用程序, 客户端会默认添加部分常见的需要过滤的引用程序, 比如各种浏览器(Edge, Chrome/类 Chromium, IE), Steam, Potplayer 等等. 如果需要过滤不在其中的应用程序, 那么就需要手动添加到名单中, . 在 Android 平台上略有不同, AdGuard 的 Android 客户端是一个更全面的防火墙, 不仅支持网络访问控制, 还会默认对所有应用程序进行过滤, 包括新安装的甚至系统应用, 而 MacOS 上的 AdGuard 过滤行为介于 Windows 和 Android 之间, 这是由于不同操作系统使用的不同过滤技术导致的: Windows: WFP, TDI, HTTP 代理. Android: VPNService, HTTP 代理. MacOS: Network Extension, Kernel Extension, HTTP 代理. 基本上来说, AdGuard 所有已实现并发行的客户端中, Android 和 MacOS 都在使用 VPN 或类 VPN 的方式过滤网络流量, 这在配置完成 HTTPS 过滤的情况下能够提供最全面的过滤特性和效果. 但 Windows 是一个例外, 如果没有在名单之中的程序就不会被过滤, 即使现在使用的 WFP 技术完全能够直接做到防火墙一样的控制, 但不知道为什么没有做. 不过在 Windows 客户端的 GitHub Issue 队列中, 过滤全局的流量已经被提上 v8.0 版本的日程, 而且优先级非常高(P2): Filter All Traffic By Default · Issue #4732 · AdguardTeam/AdguardForWindows - GitHub 局限性 面对深入到传输层的内容拦截, 难道就是广告拦截的万能钥匙了吗? 并不, 如果了解了 AdGuard 实现过滤的方式就能「反击」: SSL 证书验证: HTTPS 过滤依赖 SSL 证书, 应用可以实行证书验证, 比如常见的 HPKP(HTTP公钥固定) 和证书透明度(CT)检查. 重新编码或使用加密协议: AdGuard 虽然能够解密 HTTPS, 但目前无法对解密后的响应或请求再次解码, 只需要对重要的响应内容进行二次编码, 即使是 base64 都能够使用于处理数据的过滤器规则失去作用. 或者进一步直接使用加密协议, 在端对端加密盛行的今天, 这也是很常见的做法. 提前代理流量: 应用能够直接内置 VPN 或其他技术的流量代理, 在流量到达拦截器之前进行伪装或加密. 当然任何应用也能做到要求用户通过 VPN 才能访问对应的网络资源. 专用内容拦截器 专用内容拦截器主要是指通过其他软件提供的 API 而非通过系统网络流量进行针对性内容拦截的拦截器, 虽然 AdGuard 自己只把 Android 上还存在于 Google Play 上的那款 AdGuard 叫做 &quot;内容拦截器&quot;(目前仅适用于三星浏览器和 Yandex 浏览器). 实际上 iOS 平台的 AdGuard 客户端也是这类专用内容拦截器, 因为它只能用于 Safari 浏览器内的内容拦截, 通过规则修饰符的兼容性来看, Android 专用内容拦截器是所有 AdGuard 内容拦截器中效果最差的, 其次就是 iOS 客户端和 Safari 浏览器扩展(两者一模一样). 在 Google 的 Manifest v3 执行后, 浏览器扩展类的内容拦截器按照运行方式或许应该也要被归类为专用内容拦截器. 不过在浏览器内这种特定环境下, MV3 的浏览器扩展拦截器对过滤规则修饰符的兼容性依旧比现有的专用内容拦截器好得多. AdGuard DNS &amp; AdGuard Home AdGuard DNS 是由 AdGuard 托管的私人 DNS 服务, 同类产品有 NextDNS, RethinkDNS, NovaXNS. 提供 DoT, DoH, DoQ 加密 DNS 服务, 可设置自定义的 DNS 过滤规则. 计费方式是订阅制, 也提供免费使用的基本层. AdGuard C++ DNS libraries 源代码: AdguardTeam/DnsLibs: DNS filtering library that's used in AdGuard products - GitHub DNS Proxy 源代码: AdguardTeam/dnsproxy: Simple DNS proxy with DoH, DoT, DoQ and DNSCrypt support - GitHub AdGuard Home 源代码: AdguardTeam/AdGuardHome: Network-wide ads &amp; trackers blocking DNS server - GitHub 同宗同源不同样 AdGuard Home 是 AdGuard DNS 依赖软件的简化版, 它们都基于相同的开源库, 但在其基础上构建的产品一个是闭源的专有软件, 一个是使用 GPL 3.0 开源许可发行的自由软件. Home 是供自托管使用的 DNS 中继服务器软件, 对个人和家庭局域网环境还提供了 DHCP 服务器的功能, Home 支持几乎所有的传统和现代安全 DNS(除了 DNSCrypt): 明文, DoT, DoH, DoQ; 支持 DNSSEC, RDNS 和 ECS 扩展. 同样 Home 也支持自定义 DNS 过滤规则, 甚至能够订阅 DNS 过滤规则列表(闭源产品 AdGuard DNS 仅支持订阅通过验证的列表). 虽然 Home 是 AdGuard DNS 的简化版, 但并不是用来和它竞争的对手, Home 仅提供软件, 而 AdGuard DNS 是一整套的解决方案, Home 经常被拿来比较的对手是 Pi-Hole. 如果只是内容拦截 DNS 简单又脆弱, 是网络攻击中经常被针对的互联网基础设施. 它是人类访问互联网的必经之路, 为了让 DNS 正确无误且安静地工作, 延伸出了很多安全 DNS 协议, 发展到现在 DoH(H2/H3) 和 DoQ, 也终于在速度, 安全和易用之间达到了平衡. 由于大部分的广告, 追踪和恶意内容为了在线上传播也要依靠 DNS, 所以基于 DNS 最初原型 &quot;Hosts 文件&quot; 衍生发展出了如今的 DNS 过滤规则, 对通过 DNS 的恶意域名进行错误的 DNS 响应, 这样就能针对性地利用 DNS 的特性实现了粗略的内容拦截功能. 所谓粗略, 因为 DNS 只能对域名进行作用, 如果所有的广告都用自己的独特域名, 那么在 DNS 层面进行内容拦截早该屡试不爽. 但实际情况是越来越多的恶意内容被掺杂在正常的域名中进行请求和响应, 更有的广告直接被写在了请求响应中, 和正常内容混杂在一起. 对于这种情况如果还使用 DNS 进行拦截只会 &quot;杀敌一千自损八百&quot;, 破坏整个在线服务体验的完整性. DNS 设计之初并没有考虑内容拦截, 它只是恰好能做到在应用层进行 &quot;疏&quot; 和 &quot;堵&quot; 的系统而已. 早年的 HTTP 时代, DNS 非常容易被 &quot;污染&quot; 导致流量被劫持传输恶意内容, 并且难以被及时发现. 后来 TLS/SSL 让 HTTP 进化到了 HTTPS, 结合 CA 证书链已经很少让 DNS 污染导致流量劫持难以察觉了. 到了如今 &quot;合法&quot; 的恶意内容盛行的现代互联网, DNS 劫持开始被部分互联网用户反向利用来针对这些恶意在线服务的域名, 也促使诞生了 AdGuard DNS, NextDNS, Quad9 等等一类 &quot;安全 DNS&quot;, 这些依旧是内容拦截对抗广告的一个战场, 只是战况不太激烈而已. DNS 也随着 HTTP 进化, 越来越难以被干扰, DNS 查询特征也变得越来越隐秘, 大多数的操作系统甚至应用都开始内置 DNS 客户端进行独立的 DNS 查询. AdGuard VPN 这可能是相对同类竞争对手来说最没有特色的产品, 市面上的 VPN 已经早就变得泛滥. AdGuard 的 VPN 并没有被宣传为突破审查, 突破封锁, 也没有标榜流媒体专用, 也没有明说可以用于 P2P 传输. 对于这类产品的爱好者来说, AdGuard VPN 几乎不会被考虑, 更多的时候是和 AdGuard 的其他产品捆绑销售, 比如 AdGuard DNS. 当然, 在 AdGuard 品牌和营销的加持下, 也会变成泛泛之辈中值得留意的选择. AdGuard VPN 自一开始就没有选择主流的协议, 而是使用了自己开发的新协议, 他们也在自己的文章中表示了自己的独特协议具有的优势, 并且表示相比其他协议更难以被探测. 协议如何运行：深入了解独特的 AdGuard VPN 协议 - AdGuard VPN Blog 但是, 在众多加密网络代理协议的实践下证明: 私有协议难以被检测不是因为它完美无瑕, 只不过是因为这种协议普及程度不及针对性检测和封锁的必要性. 笔者并不认为 AdGuard 会加入这场猫鼠游戏之中, 把它当作是自卖自夸的口号足矣. AdGuard 也在他们客户端系列产品中加入对 VPN 产品的联通, 在移动设备上和桌面设备上 AdGuard 客户端和 AdGuard VPN 相互配合使用也比其他的 VPN 组合更顺畅一些. 这类平庸的 VPN 产品最大的对手应该是 Cloudflare Warp, 以及各类安全产品中提供网络保护用途的 VPN, 比如 Apple One 提供的 VPN, Google One 提供的 VPN; 以及传统反病毒公司 Norton(诺顿), Kaspersky(卡巴斯基)在高级订阅套餐中提供的网络保护功能. 对于软件集成, 交互体验和服务体验, 这些实力干瘪的独立 VPN 服务几乎没有优势(或许独立销售也是种优势). 有趣的事实 Google, Amazon 和 Apple 拒绝上架 AdGuard 客户端的原因是相同的 现在去 Google Play, Amazon Appstore 和 App Store 上下载 AdGuard 是不可能下载到包含全功能的 AdGuard 客户端的. Amazon Appstore 是 Amazon(亚马逊) 运营的一个 Android 软件商店, 类似于 Google Play. 2014 年开始到 2018 年的 5 年间, 三家主要的移动应用商店先后实行了新政策使得 AdGuard 的全功能客户端被迫下架, 这三位给出的政策理由如出一辙: 干扰或阻止其他第三方应用程序显示广告的应用程序不会被允许上架. 2014 年, Google Play 下架: Why is AdGuard not on Google Play? - AdGuard Blog 2018 年 5 月, Amazon Appstore 下架: Goodbye, Amazon - AdGuard Blog 2018 年 7 月, iOS 版本由于 App Store 政策更新而陷入停滞: AdGuard Pro for iOS in its current form will be discontinued due to Apple's policy - AdGuard Blog 2019 年 7 月, App Store 的 Pro 版本功能改动(移除网络层级过滤), 与另外一个与 Safari 专用的 iOS 版本开始趋近: AdGuard Pro for iOS is back to block ads across the system once again! - AdGuard Blog 所以, 很久之前, 至少 2018 年以前, iOS 版本的 AdGuard 都能和如今的 Android, MacOS, Windows 版本的 AdGuard 一样进行系统层面 HTTPS 过滤, 那时的 AdGuard Pro for iOS 对于 AdGuard for iOS 来说的确是具有更高级功能的版本. 但如今的 App Store 上的两个 AdGuard 客户端都只是 Safari 专用的内容拦截器而已. 这三家控制着全球移动应用发行的巨头不仅是软件或硬件巨头, 更是广告巨头, 他们比任何软件开发者都清楚广告到底对自己, 对互联网究竟意味着什么. 起源于俄罗斯的麻烦 互联网不是一个独立于现实世界的虚拟世界, 因此互联网产品也无法完全摆脱来自现实世界的引力, 特别是现实之中来自人的恶意. 起源于俄罗斯的 AdGuard 很早就明白了这一点, 他们在 2009 年成立公司的 5 年后就将总部迁移到了欧洲的塞浦路斯, 一方面为了避免原始身份的猜疑, 另一方面也趋近了来自政策层面对它们主营业务的适宜: GDPR. 除去一些人尽皆知的商业和资本考量, 塞浦路斯本身的地理位置和政治占位也是商业公司总部的好选择之一, 特别是经营全球性产品的公司. AdGuard 虽然运营的位置变化了, 但运营和开发的人员依旧不少是俄罗斯籍的, 这一点无法轻易改变. 时间来到了 2022 年, 乌俄战争又让这个隐患爆发, 这场战争不仅卷入了士兵和平民, 还包括无数与这两个国家直接或间接关联的其他国家, 无数人被要求表态, 被要求站队, 现实的战争动员了一场互联网的战争, 没有人知道他们的动机和目的. 来自乌克兰的软件公司 MacPaw 运营的 MacOS 软件套装订阅服务 Setapp 在 3 月 10 日突然告知用户, &quot;为了应对俄罗斯对乌克兰的入侵&quot; Setapp 在他们的软件套装中下架了 AdGuard, 意味着订阅 Setapp 的用户无法再获得 AdGuard 的授权, 随后有用户表示需要 AdGuard 尽快解决这个 &quot;俄罗斯问题&quot;: AdGuard needs to address the Russian issue now : r/Adguard - Reddit 当天 AdGuard 团队就发表了事件回复, 表示在这个时间前订阅了 Setapp 的客户可以凭收据兑换为期一年的 AdGuard 个人许可: Official response from AdGuard to SetApp allegations. - AdGuard Blog Official response from AdGuard to SetApp allegations : r/Adguard - Reddit 虽然 AdGuard 很早(2 月 25 日)就对乌俄战争的话题发表了看法, 表示希望尽快结束战争, 因为乌克兰有他们的支持团队以及他们的家人和朋友: Announcement on the Topic of the War in Ukraine : r/Adguard - Reddit 而 Setapp 自此之后也就没有了回应, AdGuard 自 2020 年加入 Setapp 订阅一直到该事件, 没人会意想到会闹出这样的问题. 之后过去了 6 个月, 事态依旧如此. 有 Mac 用户发现 Setapp 运营公司 MacPaw 开发的某款 MacOS 文件清理软件(CleanMyMac X)还将 AdGuard for Safari 例为了 &quot;可疑&quot; 软件, 并且明确显示了这是由于 &quot;该软件由俄罗斯人开发或拥有&quot; 导致: Is AdGuard Spyware? | MacRumors Forums 直到现在, Setapp 也没有任何表示, 他们的订阅软件列表中依旧没有 AdGuard. 结语: 内容拦截的「无限战争」 广告拦截已经从最初的 Hosts, DNS, 到达了浏览器扩展. 如今越来越复杂和无孔不入的广告和追踪证明 DNS 层面的过滤只是一种越来越难起到作用的手段, 直到浏览器扩展的出现大多数人都度过了和广告相安无事的几年, 直到一些隐私和安全的话题把广告拦截升级成一种意识上的正义, 广告拦截自此变得广泛, 更应该用 &quot;内容拦截&quot; 来形容. 从田园年代的贴片广告到现在的 Cookies, 追踪器, 侵入式内容, 深度包检测, 内容拦截逐渐从被动接受后清除, 变为主动从网络层面直接剔除和修改, 内容拦截的深度和广度已经不是如今的浏览器扩展能够完全胜任的了, 很多的 &quot;广告&quot; 已经从浏览器逃离, 藏进了更深的地方, 藏进了浏览器内核, 藏进了操作系统, 藏进了智能家居, 藏进了网络运营商... 内容拦截对互联网用户来说意味着一种选择的主动权利, 能够选择「我愿意看到什么」和「我希望给你什么」的权利. 如果隐私是种资产, 那么资产所有者理应拥有控制资产和把它作为交易「筹码」进行讨价还价的权利, 在主动出让之前不应该被无缘无故浪费和挟持. 如果互联网完全赋予了用户这种权利, 那么内容拦截器自然就没有了存在的必要, 可惜现在它的必要性已经越发凸显, 甚至多次推到了风口浪尖. 即使是如今采用更加激进过滤手段的 AdGuard 客户端, 依旧还在和 &quot;广告&quot; 苦斗着, 除了软件开发者还有无数默默无闻的过滤器规则维护者, 虽然苦战没有尽头, 但证明了浏览器扩展之后的下一代内容拦截技术或许应该是: MiTM 代理过滤. 虽然不完美, 甚至手段可能遭人质疑正当性, 但至少现在有了新的办法, 并且是有效的. 那么在这之后, 内容拦截技术的下一代又会是什么呢?","summary":"Manifest V3 对于 AdGuard 来说完全是大顺风的优势, 因为它们不靠浏览器扩展盈利, 而是靠客户端程序许可. 虽然浏览器扩展几乎能应对 80% 的拦截需求, 但 Google 起了一个底, YouTube 开了个头, 对浏览器扩展的限制越来越大, 虽然 MV3 不是为了针对广告拦截器而设下的, 但来自 Chromium 主线的推动足够开始改变很多东西. 对广告拦截的目标也应该尽快提升到内容拦截. AdGuard 是商业化运作的软件公司, 创办于俄罗斯莫斯科(虽然目前总部已经迁到塞浦路斯), 旗下的所有免费产品都是开源的, 包括浏览器扩展, AdGuard Home 和 Android 专用内容拦截器. 它们的主要营利产品是: AdGuard 客户端, 私人 DNS, VPN. 对浏览器操作了如指掌的电脑玩家绝对不会落下广告拦截器插件, 但要真正将广告拦截运用自如就要更加深入地了解了广告拦截器, 否则最后只能靠其他人写的过滤器规则来拦截自己看到的广告. 当广告拦截器和互联网广告主的对决开始, 这种滞后性会变得非常突出, 况且广告主自己还控制着浏览器内核的主导权呢? 第一个网页广告和第一个广告拦截器几乎是统一时间出现, 自上世纪九十年代至今, 广告和广告拦截器的对决从没有停止过. 但是互联网广告诞生至今已经变得越来越多样, 而广告拦截器似乎始终以浏览器扩展插件为阵地, 这种滞后的局面何时才能迎来转变? 如果说在浏览器里用浏览器扩展拦截广告就是在 &quot;八角笼&quot; 里和广告公平互殴, 偶有优势. 但似乎已经让人忘记了, 这场广告和反广告的比赛的目的本应该是让我们保持主动并占据上风且不将对手打垮, 而不该是在对手的规则里展开他口里的 &quot;公平对决&quot;. 笔者将依照自身亲身经验分析市场上最流行的, 依靠广告拦截而商业化运作的公司 AdGuard 的主要产品的特性, 得出它们产品的相对如今其他流量广告拦截器之间的区别, 以及存在的独特优势和依旧存在的局限性. 比赛在即, 裁判却决定将跑道越缩越窄, 以前在赛场上还能靠人数优势, 前赴后继接力才能和另外骑着自行车的选手一较高下. 但现在跑道越来越窄, 人数优势变得越来越小, 回头发现对手的自行车已经早就换成了摩托, 细看之下, 这位选手似乎和刚才还在闲聊的裁判长得似乎有点相似... 比赛开始了, 你应该如果取胜? 内容拦截类 内容拦截是 AdGuard 主线产品的核心功能, 它们又分为浏览器扩展和客户端两类, 按照对过滤器特性的兼容性, 可以得出其内容拦截能力的高低: &quot;CoreLibs Apps&quot; 是指使用闭源库 CoreLibs 开发的 AdGuard 客户端, 目前在 Windows, MacOS 和 Android 进行了适配和发行, 是付费产品. 如果只使用基础的规则和修饰符, 那么基本上它们之间除了来自浏览器内核和程序访问能力的限制外, 没有什么区别. 但是如果过滤的需求范围来到了浏览器扩展的能力范围之外, 区别就开始逐渐显现. 以下是 AdGuard 内容过滤规则高级修饰符的兼容性对比: 看起来优势也并不多? 但别忘了, 现在对比的范围已经离开浏览器这个约束范围了, 浏览器扩展和 CoreLibs 应用已经处于不同的应用层面. 对内容的 &quot;拦截&quot; 开始过渡到 &quot;重写&quot; 和 &quot;替换&quot; 时, 就需要深入到传输层. 目前在传输层才能实现的高级修饰符有: $hls: 移除 HLS 列表中的内容片段. $jsonprune: 移除 JSON 响应中的片段. $network: 阻止特定 IP 的连接. $referrerpolicy: 替换页面的 referrer 策略(Referrer-Policy). $replace: 使用正则表达式替换响应内容中的字串符. 如果需要让 AdGuard 实现这些功能, 或者需要在浏览器之外继续过滤内容, 那么就需要使用基于 CoreLibs 的 AdGuard 客户端. AdGuard 浏览器扩展 这里的浏览器扩展仅指能够独立实现内容拦截的浏览器扩展. 这是对抗同类产品而设置的「守门员」, 免费且开源, 它的目标应当是引导用户去购买它们的付费产品. 与 uBlock Origin 之类的浏览器扩展来说并没有突出的优势, 在本章节开头的规则兼容性对比中就能看出, 浏览器扩展能力范围的拦截任务与其他流行的内容拦截扩展并没有什么独特优势. 但是文章如开头所说, MV3 的推进可能会改变这类扩展的格局. AdGuard 浏览器扩展源代码:AdguardTeam/AdguardBrowserExtension: AdGuard browser extension - GitHub AdGuard for Safari 源代码: AdguardTeam/AdGuardForSafari: AdGuard for Safari app extension - GitHub 局限性 虽然浏览器扩展适配了主流的浏览器, 但实际上由于浏览器内核的限制, 只有 Chrome, 类 Chromium 和 Firefox 的扩展才能实现完整过滤器特性, 而例如 Safari 由于 WebKit 的限制无法实现完整的过滤器特性, AdGuard 也专门为 Safari 版本的 AdGuard 浏览器扩展开设了独立的发布页面: Safari Release - AdGuard versions AdGuard 团队也在它们的博客文章中解释了为什么 Safari 中的内容拦截扩展能力受限: YouTube ads in Safari: why do you see them and can they be removed? - AdGuard Blog 当然这是所有的内容拦截类浏览器扩展都要面对的问题, 如果需要完全限制这一类扩展, 那么就需要在浏览器内核上动手. 比如 Google 起头以 Chromium 为首而计划实施的: Web Environment Integrity: &quot;Web Environment Integrity&quot;: Locking Down the Web | Brave Manifest v3: How ad blocking is done and why it's in danger — AdGuard 虽然这两个提议并没有人认为就是为了针对广告拦截器, Google 对 Chromium 中实现 Web Environment Integrity 的计划也已经破产. 但很明显, 广告主对如今的广告拦截器很不满意, YouTube 也突然开始了反广告拦截器的行动. 虽然它肯定不是第一个反广告拦截的, 但 YouTube 的背后恰好还是 Google. YouTube's Crackdown Spurs Record Uninstalls of Ad Blockers | WIRED 连 FBI 都推荐使用广告拦截器去拦截搜索引擎中搜索结果的广告, 很明显这场「战斗」已经变得异常胶着, Google 的身影已经变得无处不在, 毕竟 Google 的大部分收入都是靠广告. 如果不能阻止浏览器扩展拦截浏览器广告, 那么就离开浏览器, 比传统网页更适合广告的是移动互联网. AdGuard 客户端 AdGuard 的客户端是相对于浏览器扩展而强化内容过滤能力的独立应用, 它实际上更类似于一个工作在浏览器之上的专用防火墙. 在使用 HTTPS 过滤前, 它的过滤能力还不如浏览器扩展, 因为浏览器中的扩展一样都工作在 OSI 模型中的第七层: 应用层, 并且它独立于浏览器也无法使用浏览器扩展才能使用的的 API. 此外如果不使用过滤功能, 它也还能仅充当 DNS 代理客户端. 当配置完成 HTTPS 过滤, 系统默认信任来自 AdGuard 客户端的 SSL 证书后, 此时的它就开始使用 MiTM(中间人攻击) 的方式进行内容过滤, 工作方式开始介入 OSI 模型中的第四层: 传输层. 对仅使用 HTTPS 加密的流量实行完全的解密, 过滤和重写. 桌面客户端与之配套的有一个用于控制客户端的浏览器扩展, 名叫 &quot;AdGuard 浏览器助手&quot;, 可以在浏览器里控制外部客户端对指定站点的过滤, 还能充当元素选择器生成规则即时拦截广告元素. 闭源之路 AdGuard 客户端目前仅在 Windows, Mac 和 Android 操作系统适配并发行. 它们都是基于的闭源库 AdGuard CoreLibs 开发的闭源产品. 要注意的是, 目前 AdGuard 的 iOS 客户端只是用于应对移动端 Safari 内容拦截的应用, 它作用类似于 Safari 浏览器扩展, 定位接近于 Android 平台的「内容拦截器」, 它也不开源, 也不是基于 CoreLibs 开发的产品. 因此现在 AdGuard 唯一支持全功能过滤的移动端应用只有 Android 平台. AdGuard Core C/C++ libraries 的 Issues 仓库: AdguardTeam/CoreLibs: Core Adguard libraries - GitHub 规则优先 客户端在多平台都有实现和发行, 它的过滤能力也完全取决于规则. 虽然客户端中有许多功能都是能以开关或选择的形式让用户轻松就能使用, 比如 DNS 代理, 隐形模式和浏览安全等模块, 但这些功能实际上都与内容拦截没有关系, 它们并不能帮助用户拦截更多的广告, 更多的只能算是一种加强上网安全和隐私的附加功能. 为了让不精通技术的用户也能轻松使用客户端, 在 DNS 过滤器和内容过滤器的都添加了可供用户直接选择并使用的过滤规则订阅, 并且客户端安装完毕实际上就默认启用了数个 AdGuard 直接负责维护的过滤器订阅. 这种配置能够应对绝大多数的内容过滤的需求. 以规则为核心实现过滤的客户端中, 有两种类似但并不完全相同的 AdGuard 规则: DNS 过滤规则 内容过滤规则 前者仅作用于 DNS, 在 DNS 层面的域名解析过滤拦截和重写. 后者作用于 HTTP 和 HTTPS, 对应用的请求和内容进行过滤拦截和重写, 它们使用相似的规则语法, 主要区别在于修饰符, DNS 过滤规则语法中拥有与其能力符合的独有修饰符, 比如 $dnstype 和 $dnsrewrite, 详见 DNS 过滤规则文档: DNS 过滤规则语法 | AdGuard DNS Knowledge Base 又由于 DNS 规则语法几乎与内容拦截规则相同(除了部分修饰符), 所以基本上来说 DNS 过滤规则也能直接当作内容过滤规则使用. 在实际使用的时候, 如果能够在 DNS 请求时就能将内容进行拦截, 那么就应该优先使用 DNS 过滤. 过滤行为 本节开头说过, AdGuard 客户端实际上是一个防火墙. 但目前在 Windows 上只会选择性过滤在其设置中加入过滤名单中的应用程序, 客户端会默认添加部分常见的需要过滤的引用程序, 比如各种浏览器(Edge, Chrome/类 Chromium, IE), Steam, Potplayer 等等. 如果需要过滤不在其中的应用程序, 那么就需要手动添加到名单中, . 在 Android 平台上略有不同, AdGuard 的 Android 客户端是一个更全面的防火墙, 不仅支持网络访问控制, 还会默认对所有应用程序进行过滤, 包括新安装的甚至系统应用, 而 MacOS 上的 AdGuard 过滤行为介于 Windows 和 Android 之间, 这是由于不同操作系统使用的不同过滤技术导致的: Windows: WFP, TDI, HTTP 代理. Android: VPNService, HTTP 代理. MacOS: Network Extension, Kernel Extension, HTTP 代理. 基本上来说, AdGuard 所有已实现并发行的客户端中, Android 和 MacOS 都在使用 VPN 或类 VPN 的方式过滤网络流量, 这在配置完成 HTTPS 过滤的情况下能够提供最全面的过滤特性和效果. 但 Windows 是一个例外, 如果没有在名单之中的程序就不会被过滤, 即使现在使用的 WFP 技术完全能够直接做到防火墙一样的控制, 但不知道为什么没有做. 不过在 Windows 客户端的 GitHub Issue 队列中, 过滤全局的流量已经被提上 v8.0 版本的日程, 而且优先级非常高(P2): Filter All Traffic By Default · Issue #4732 · AdguardTeam/AdguardForWindows - GitHub 局限性 面对深入到传输层的内容拦截, 难道就是广告拦截的万能钥匙了吗? 并不, 如果了解了 AdGuard 实现过滤的方式就能「反击」: SSL 证书验证: HTTPS 过滤依赖 SSL 证书, 应用可以实行证书验证, 比如常见的 HPKP(HTTP公钥固定) 和证书透明度(CT)检查. 重新编码或使用加密协议: AdGuard 虽然能够解密 HTTPS, 但目前无法对解密后的响应或请求再次解码, 只需要对重要的响应内容进行二次编码, 即使是 base64 都能够使用于处理数据的过滤器规则失去作用. 或者进一步直接使用加密协议, 在端对端加密盛行的今天, 这也是很常见的做法. 提前代理流量: 应用能够直接内置 VPN 或其他技术的流量代理, 在流量到达拦截器之前进行伪装或加密. 当然任何应用也能做到要求用户通过 VPN 才能访问对应的网络资源. 专用内容拦截器 专用内容拦截器主要是指通过其他软件提供的 API 而非通过系统网络流量进行针对性内容拦截的拦截器, 虽然 AdGuard 自己只把 Android 上还存在于 Google Play 上的那款 AdGuard 叫做 &quot;内容拦截器&quot;(目前仅适用于三星浏览器和 Yandex 浏览器). 实际上 iOS 平台的 AdGuard 客户端也是这类专用内容拦截器, 因为它只能用于 Safari 浏览器内的内容拦截, 通过规则修饰符的兼容性来看, Android 专用内容拦截器是所有 AdGuard 内容拦截器中效果最差的, 其次就是 iOS 客户端和 Safari 浏览器扩展(两者一模一样). 在 Google 的 Manifest v3 执行后, 浏览器扩展类的内容拦截器按照运行方式或许应该也要被归类为专用内容拦截器. 不过在浏览器内这种特定环境下, MV3 的浏览器扩展拦截器对过滤规则修饰符的兼容性依旧比现有的专用内容拦截器好得多. AdGuard DNS &amp; AdGuard Home AdGuard DNS 是由 AdGuard 托管的私人 DNS 服务, 同类产品有 NextDNS, RethinkDNS, NovaXNS. 提供 DoT, DoH, DoQ 加密 DNS 服务, 可设置自定义的 DNS 过滤规则. 计费方式是订阅制, 也提供免费使用的基本层. AdGuard C++ DNS libraries 源代码: AdguardTeam/DnsLibs: DNS filtering library that's used in AdGuard products - GitHub DNS Proxy 源代码: AdguardTeam/dnsproxy: Simple DNS proxy with DoH, DoT, DoQ and DNSCrypt support - GitHub AdGuard Home 源代码: AdguardTeam/AdGuardHome: Network-wide ads &amp; trackers blocking DNS server - GitHub 同宗同源不同样 AdGuard Home 是 AdGuard DNS 依赖软件的简化版, 它们都基于相同的开源库, 但在其基础上构建的产品一个是闭源的专有软件, 一个是使用 GPL 3.0 开源许可发行的自由软件. Home 是供自托管使用的 DNS 中继服务器软件, 对个人和家庭局域网环境还提供了 DHCP 服务器的功能, Home 支持几乎所有的传统和现代安全 DNS(除了 DNSCrypt): 明文, DoT, DoH, DoQ; 支持 DNSSEC, RDNS 和 ECS 扩展. 同样 Home 也支持自定义 DNS 过滤规则, 甚至能够订阅 DNS 过滤规则列表(闭源产品 AdGuard DNS 仅支持订阅通过验证的列表). 虽然 Home 是 AdGuard DNS 的简化版, 但并不是用来和它竞争的对手, Home 仅提供软件, 而 AdGuard DNS 是一整套的解决方案, Home 经常被拿来比较的对手是 Pi-Hole. 如果只是内容拦截 DNS 简单又脆弱, 是网络攻击中经常被针对的互联网基础设施. 它是人类访问互联网的必经之路, 为了让 DNS 正确无误且安静地工作, 延伸出了很多安全 DNS 协议, 发展到现在 DoH(H2/H3) 和 DoQ, 也终于在速度, 安全和易用之间达到了平衡. 由于大部分的广告, 追踪和恶意内容为了在线上传播也要依靠 DNS, 所以基于 DNS 最初原型 &quot;Hosts 文件&quot; 衍生发展出了如今的 DNS 过滤规则, 对通过 DNS 的恶意域名进行错误的 DNS 响应, 这样就能针对性地利用 DNS 的特性实现了粗略的内容拦截功能. 所谓粗略, 因为 DNS 只能对域名进行作用, 如果所有的广告都用自己的独特域名, 那么在 DNS 层面进行内容拦截早该屡试不爽. 但实际情况是越来越多的恶意内容被掺杂在正常的域名中进行请求和响应, 更有的广告直接被写在了请求响应中, 和正常内容混杂在一起. 对于这种情况如果还使用 DNS 进行拦截只会 &quot;杀敌一千自损八百&quot;, 破坏整个在线服务体验的完整性. DNS 设计之初并没有考虑内容拦截, 它只是恰好能做到在应用层进行 &quot;疏&quot; 和 &quot;堵&quot; 的系统而已. 早年的 HTTP 时代, DNS 非常容易被 &quot;污染&quot; 导致流量被劫持传输恶意内容, 并且难以被及时发现. 后来 TLS/SSL 让 HTTP 进化到了 HTTPS, 结合 CA 证书链已经很少让 DNS 污染导致流量劫持难以察觉了. 到了如今 &quot;合法&quot; 的恶意内容盛行的现代互联网, DNS 劫持开始被部分互联网用户反向利用来针对这些恶意在线服务的域名, 也促使诞生了 AdGuard DNS, NextDNS, Quad9 等等一类 &quot;安全 DNS&quot;, 这些依旧是内容拦截对抗广告的一个战场, 只是战况不太激烈而已. DNS 也随着 HTTP 进化, 越来越难以被干扰, DNS 查询特征也变得越来越隐秘, 大多数的操作系统甚至应用都开始内置 DNS 客户端进行独立的 DNS 查询. AdGuard VPN 这可能是相对同类竞争对手来说最没有特色的产品, 市面上的 VPN 已经早就变得泛滥. AdGuard 的 VPN 并没有被宣传为突破审查, 突破封锁, 也没有标榜流媒体专用, 也没有明说可以用于 P2P 传输. 对于这类产品的爱好者来说, AdGuard VPN 几乎不会被考虑, 更多的时候是和 AdGuard 的其他产品捆绑销售, 比如 AdGuard DNS. 当然, 在 AdGuard 品牌和营销的加持下, 也会变成泛泛之辈中值得留意的选择. AdGuard VPN 自一开始就没有选择主流的协议, 而是使用了自己开发的新协议, 他们也在自己的文章中表示了自己的独特协议具有的优势, 并且表示相比其他协议更难以被探测. 协议如何运行：深入了解独特的 AdGuard VPN 协议 - AdGuard VPN Blog 但是, 在众多加密网络代理协议的实践下证明: 私有协议难以被检测不是因为它完美无瑕, 只不过是因为这种协议普及程度不及针对性检测和封锁的必要性. 笔者并不认为 AdGuard 会加入这场猫鼠游戏之中, 把它当作是自卖自夸的口号足矣. AdGuard 也在他们客户端系列产品中加入对 VPN 产品的联通, 在移动设备上和桌面设备上 AdGuard 客户端和 AdGuard VPN 相互配合使用也比其他的 VPN 组合更顺畅一些. 这类平庸的 VPN 产品最大的对手应该是 Cloudflare Warp, 以及各类安全产品中提供网络保护用途的 VPN, 比如 Apple One 提供的 VPN, Google One 提供的 VPN; 以及传统反病毒公司 Norton(诺顿), Kaspersky(卡巴斯基)在高级订阅套餐中提供的网络保护功能. 对于软件集成, 交互体验和服务体验, 这些实力干瘪的独立 VPN 服务几乎没有优势(或许独立销售也是种优势). 有趣的事实 Google, Amazon 和 Apple 拒绝上架 AdGuard 客户端的原因是相同的 现在去 Google Play, Amazon Appstore 和 App Store 上下载 AdGuard 是不可能下载到包含全功能的 AdGuard 客户端的. Amazon Appstore 是 Amazon(亚马逊) 运营的一个 Android 软件商店, 类似于 Google Play. 2014 年开始到 2018 年的 5 年间, 三家主要的移动应用商店先后实行了新政策使得 AdGuard 的全功能客户端被迫下架, 这三位给出的政策理由如出一辙: 干扰或阻止其他第三方应用程序显示广告的应用程序不会被允许上架. 2014 年, Google Play 下架: Why is AdGuard not on Google Play? - AdGuard Blog 2018 年 5 月, Amazon Appstore 下架: Goodbye, Amazon - AdGuard Blog 2018 年 7 月, iOS 版本由于 App Store 政策更新而陷入停滞: AdGuard Pro for iOS in its current form will be discontinued due to Apple's policy - AdGuard Blog 2019 年 7 月, App Store 的 Pro 版本功能改动(移除网络层级过滤), 与另外一个与 Safari 专用的 iOS 版本开始趋近: AdGuard Pro for iOS is back to block ads across the system once again! - AdGuard Blog 所以, 很久之前, 至少 2018 年以前, iOS 版本的 AdGuard 都能和如今的 Android, MacOS, Windows 版本的 AdGuard 一样进行系统层面 HTTPS 过滤, 那时的 AdGuard Pro for iOS 对于 AdGuard for iOS 来说的确是具有更高级功能的版本. 但如今的 App Store 上的两个 AdGuard 客户端都只是 Safari 专用的内容拦截器而已. 这三家控制着全球移动应用发行的巨头不仅是软件或硬件巨头, 更是广告巨头, 他们比任何软件开发者都清楚广告到底对自己, 对互联网究竟意味着什么. 起源于俄罗斯的麻烦 互联网不是一个独立于现实世界的虚拟世界, 因此互联网产品也无法完全摆脱来自现实世界的引力, 特别是现实之中来自人的恶意. 起源于俄罗斯的 AdGuard 很早就明白了这一点, 他们在 2009 年成立公司的 5 年后就将总部迁移到了欧洲的塞浦路斯, 一方面为了避免原始身份的猜疑, 另一方面也趋近了来自政策层面对它们主营业务的适宜: GDPR. 除去一些人尽皆知的商业和资本考量, 塞浦路斯本身的地理位置和政治占位也是商业公司总部的好选择之一, 特别是经营全球性产品的公司. AdGuard 虽然运营的位置变化了, 但运营和开发的人员依旧不少是俄罗斯籍的, 这一点无法轻易改变. 时间来到了 2022 年, 乌俄战争又让这个隐患爆发, 这场战争不仅卷入了士兵和平民, 还包括无数与这两个国家直接或间接关联的其他国家, 无数人被要求表态, 被要求站队, 现实的战争动员了一场互联网的战争, 没有人知道他们的动机和目的. 来自乌克兰的软件公司 MacPaw 运营的 MacOS 软件套装订阅服务 Setapp 在 3 月 10 日突然告知用户, &quot;为了应对俄罗斯对乌克兰的入侵&quot; Setapp 在他们的软件套装中下架了 AdGuard, 意味着订阅 Setapp 的用户无法再获得 AdGuard 的授权, 随后有用户表示需要 AdGuard 尽快解决这个 &quot;俄罗斯问题&quot;: AdGuard needs to address the Russian issue now : r/Adguard - Reddit 当天 AdGuard 团队就发表了事件回复, 表示在这个时间前订阅了 Setapp 的客户可以凭收据兑换为期一年的 AdGuard 个人许可: Official response from AdGuard to SetApp allegations. - AdGuard Blog Official response from AdGuard to SetApp allegations : r/Adguard - Reddit 虽然 AdGuard 很早(2 月 25 日)就对乌俄战争的话题发表了看法, 表示希望尽快结束战争, 因为乌克兰有他们的支持团队以及他们的家人和朋友: Announcement on the Topic of the War in Ukraine : r/Adguard - Reddit 而 Setapp 自此之后也就没有了回应, AdGuard 自 2020 年加入 Setapp 订阅一直到该事件, 没人会意想到会闹出这样的问题. 之后过去了 6 个月, 事态依旧如此. 有 Mac 用户发现 Setapp 运营公司 MacPaw 开发的某款 MacOS 文件清理软件(CleanMyMac X)还将 AdGuard for Safari 例为了 &quot;可疑&quot; 软件, 并且明确显示了这是由于 &quot;该软件由俄罗斯人开发或拥有&quot; 导致: Is AdGuard Spyware? | MacRumors Forums 直到现在, Setapp 也没有任何表示, 他们的订阅软件列表中依旧没有 AdGuard. 结语: 内容拦截的「无限战争」 广告拦截已经从最初的 Hosts, DNS, 到达了浏览器扩展. 如今越来越复杂和无孔不入的广告和追踪证明 DNS 层面的过滤只是一种越来越难起到作用的手段, 直到浏览器扩展的出现大多数人都度过了和广告相安无事的几年, 直到一些隐私和安全的话题把广告拦截升级成一种意识上的正义, 广告拦截自此变得广泛, 更应该用 &quot;内容拦截&quot; 来形容. 从田园年代的贴片广告到现在的 Cookies, 追踪器, 侵入式内容, 深度包检测, 内容拦截逐渐从被动接受后清除, 变为主动从网络层面直接剔除和修改, 内容拦截的深度和广度已经不是如今的浏览器扩展能够完全胜任的了, 很多的 &quot;广告&quot; 已经从浏览器逃离, 藏进了更深的地方, 藏进了浏览器内核, 藏进了操作系统, 藏进了智能家居, 藏进了网络运营商... 内容拦截对互联网用户来说意味着一种选择的主动权利, 能够选择「我愿意看到什么」和「我希望给你什么」的权利. 如果隐私是种资产, 那么资产所有者理应拥有控制资产和把它作为交易「筹码」进行讨价还价的权利, 在主动出让之前不应该被无缘无故浪费和挟持. 如果互联网完全赋予了用户这种权利, 那么内容拦截器自然就没有了存在的必要, 可惜现在它的必要性已经越发凸显, 甚至多次推到了风口浪尖. 即使是如今采用更加激进过滤手段的 AdGuard 客户端, 依旧还在和 &quot;广告&quot; 苦斗着, 除了软件开发者还有无数默默无闻的过滤器规则维护者, 虽然苦战没有尽头, 但证明了浏览器扩展之后的下一代内容拦截技术或许应该是: MiTM 代理过滤. 虽然不完美, 甚至手段可能遭人质疑正当性, 但至少现在有了新的办法, 并且是有效的. 那么在这之后, 内容拦截技术的下一代又会是什么呢?","date_published":"2023-11-17T00:29:23.000Z","tags":["博物志","软件","AdGuard","广告拦截器","内容拦截"]},{"id":"https://blog.cxplay.org/works/why-dont-i-recommend-use-nostr-and-fediverse/","url":"https://blog.cxplay.org/works/why-dont-i-recommend-use-nostr-and-fediverse/","title":"为什么我不推荐 Nostr 和 Fediverse?","content_html":"<h2 id=\"前言\"><a href=\"#前言\"></a>前言</h2>\n<p>笔者先介绍一下文章的背景:</p>\n<ul>\n<li>2 月份, 笔者发现了 Nostr 这样一个新兴的社交网络, 并开始尝试使用一些客户端实现.</li>\n<li>5 月份, 笔者开始使用某 Nostr 的服务端(中继)实现, 开始深入体验.</li>\n<li>7 月份, 大致生态和软件设计体验完成, 也正好发生了一次账号数据 &quot;事故&quot;.</li>\n<li>今天(28 日)受到账号数据事故的启发, 决定针对 Nostr 写一些东西(但读者朋友们可能看到这篇文章是好几天后了🤣).</li>\n</ul>\n<p>这篇文章的目的是「劝退」, 笔者会描述这几个月之间使用 Nostr 中遇到的问题, 当然只是 &quot;丑话说在前&quot;, 文末也会介绍这种社交网络的优势, 相比如今流行的 ActivityPub 组成的 Fediverse 之间究竟有什么理由选择新的.</p>\n<p>本文只提供笔者主观的体验感受, 其中提到的问题对我而言是问题, 而对读者朋友们不一定是, 请不要因为我的只言片语就放弃亲自探索新事物😉.</p>\n<h2 id=\"什么是-nostr\"><a href=\"#什么是-nostr\"></a>什么是 Nostr</h2>\n<p>Nostr 全称 &quot;Notes and Other Stuff Transmitted by Relays&quot;, 即 &quot;通过中继传输的笔记和其他东西&quot;. 是一种用于设计实现<strong>全球性</strong>, <strong>去中心化</strong>与<strong>抗审查</strong>的社交网络的<strong>通讯协议</strong>.</p>\n<blockquote>\n<p>Nostr 也直接代指由这种协议组成的分布式社交网络.</p>\n</blockquote>\n<p>Nostr 的协议规范被称为 &quot;NIPs&quot;, 全称 Nostr Implementation Possibilities, 即 &quot;Nostr 可能性实现&quot;. 每一个可能的具体实现经过社区讨论和实践后会被合并进入 NIPs 作为 Nostr 的标准进一步壮大规模, 已和并的 NIPs 依旧接受社区改进和讨论, 通过 NIPs 传输的具体响应内容依照不同用途分为不同类型(kind)的事件(event), 一个 NIPs 可能是作为一个超集包含其他的 NIPs, 也可能是一个完全独立的使用全新类型的 NIPs.</p>\n<p>比如:</p>\n<ul>\n<li><strong><a href=\"https://github.com/nostr-protocol/nips/blob/master/01.md\">NIP-01</a></strong>: 定义了基础事件, 包含三个事件类型(0, 1, 2), 其中:\n<ul>\n<li>kind:0 是描述秘钥元数据(可读的用户名, 个人资料,资料横幅等等 )的类型;</li>\n<li>kind:1 是社交网络中最基本的文字贴 <code>text_note</code> 的类型;</li>\n<li>kind:2 是一种向获取事件的客户端发送首选中继的类型.</li>\n</ul>\n</li>\n<li><strong><a href=\"https://github.com/nostr-protocol/nips/blob/master/02.md\">NIP-02</a></strong>: 定义联系人列表, 用于传递用户关注列表和追随者列表, 使用 <code>kind:3</code> 类型.</li>\n<li><strong><a href=\"https://github.com/nostr-protocol/nips/blob/master/05.md\">NIP-05</a></strong>: 定义了如何将域名通过 DNS 和 HTTP 映射到秘钥, 用于身份认证和声明偏好中继列表, 使用 <code>kind:0</code> 类型.</li>\n</ul>\n<p>Nostr 的服务端的实现如它的名字一样为 &quot;中继(Relay)&quot;. 客户端通过一对密钥标识一个用户, 公钥用于定位一个基本用户, 私钥用于发布事件时计算消息签名进行签署发布; 中继(Relay)与客户端使用 WebSocket 协议进行通讯, 中继只负责储存和传递事件并且各中继间互不通讯, 几乎所有的计算渲染都发生在客户端.</p>\n<h2 id=\"nostr-的发展\"><a href=\"#nostr-的发展\"></a>Nostr 的发展</h2>\n<p>Nostr 最早由 fiatjaf 创建, 根据 GitHub 仓库 nostr-protocol/nostr 的提交历史, 最早可以追溯到 2020 年 11 月 8 日:</p>\n<blockquote>\n<p><a href=\"https://github.com/nostr-protocol/nostr/commit/6158017db0b12686218113232fff175a45953e2f\">basic server relay code. · nostr-protocol/nostr@6158017</a></p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/why-dont-i-recommend-use-nostr-and-fediverse/IMG_20230728-220905524.png\" alt=\"nostr-protocol/nost 仓库的 master 分支中最早的提交记录\" loading='lazy'></p>\n<p>引来 Nostr 第一次爆发的是 Twitter 联合创始人兼前 Twitter 首席执行官: 杰克·多西(<a href=\"https://zh.wikipedia.org/wiki/%E6%9D%B0%E5%85%8B%C2%B7%E5%A4%9A%E8%A5%BF\">Jack Dorsey</a>).</p>\n<p>2022 年 11 月底 12 月初, 正值埃隆·马斯克(<a href=\"https://zh.wikipedia.org/zh-hans/%E5%9F%83%E9%9A%86%C2%B7%E9%A9%AC%E6%96%AF%E5%85%8B\">Elon Musk</a>)完成 Twitter 的收购<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup>, 马斯克当即就向记者提供了 Twitter 内部的一些文件, 宣称是为了揭露 Twitter 存在的内容审核中的偏见和政府施加的影响<sup class=\"footnote-ref\"><a href=\"#fn2\" id=\"fnref2\">[2]</a></sup>, 随后与之相关的 Twitter 话题 &quot;#TwitterFiles&quot; 登上了流行趋势.</p>\n<p>2022 年 12 月 14 日, 杰克·多西在自己的 Twitter 账号上发表了动态参与了 #TwitterFiles 话题的讨论, 谈到了关于马斯克揭开这块 &quot;遮羞布&quot; 后的问题的想法, 他认为社交媒体应该:</p>\n<ul>\n<li>社交媒体必须能够抵抗企业和政府的控制.</li>\n<li>只有(内容)原作者才能删除他们自己制造的内容.</li>\n<li>反极端的审查(moderation)应该通过算法实现(algorithmic choice)<sup class=\"footnote-ref\"><a href=\"#fn3\" id=\"fnref3\">[3]</a></sup>.</li>\n</ul>\n<blockquote>\n<p>在下文将统称为三大 &quot;原则&quot;.</p>\n</blockquote>\n<blockquote>\n<p><a href=\"https://web.archive.org/web/20230104063843/https://getrevue.co/profile/jackjack/issues/a-native-internet-protocol-for-social-media-1503112\">a native internet protocol for social media | Revue</a> (存档)</p>\n</blockquote>\n<p>为了达到他心中的愿景, 他提到自己提供资助的 <a href=\"https://blueskyweb.xyz/\">Bluesky</a> 社交网络和其使用的 &quot;<a href=\"https://atproto.com/\">AT Protocol</a>(AT协议)&quot;, 为了让更多人展现可能性, 他发起了一个 Twitter 话题 &quot;#startsmall&quot; 和与之关联的 &quot;open internet development(开放互联网开发)&quot; 奖金, 这笔奖金将会用于 &quot;致力于社交媒体和私有隐私通讯协议, 比特币, 纯 web 移动操作系统的工程团队&quot; 提供现金和股权投资, 还说下周就会向加密通讯软件 Signal 提供资助.</p>\n<p>同一天, 杰克·多西在 Twitter 线程下跟帖发送了 Nostr 的 GitHub 仓库表示这是个 &quot;优秀的问题阐述和解决办法的文章&quot;:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/why-dont-i-recommend-use-nostr-and-fediverse/IMG_20230730-225254085.png\" alt=\"提及 Nostr\" loading='lazy'></p>\n<blockquote>\n<p><a href=\"https://twitter.com/jack/status/1602853632512516096\">jack: &quot;excellent writeup of problems and solutions...excited to see this. and public domain:&quot; — https://t.co/bCXRwQNC0p</a></p>\n</blockquote>\n<p>两天后, 也就是 2022 年 12 月 16 日, 杰克·多西简短地再次跟帖, 表示自己已经为这个项目向 fiatjaf 发送了 14 个比特币(当时价值约 $245000):</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/why-dont-i-recommend-use-nostr-and-fediverse/IMG_20230730-225551599.png\" alt=\"宣布捐助\" loading='lazy'></p>\n<blockquote>\n<p><a href=\"https://twitter.com/jack/status/1603535971114487816\">jack: &quot;14 BTC deployed to @fiatjaf for #nostr&quot;</a></p>\n</blockquote>\n<p>时间快进到两个月后的 2023 年 2 月 1 日, Nostr 社区依靠杰克·多西的影响以惊人的速度完成了不可思议的成就: 成功实现并在应用商店发行 Nostr 的移动平台客户端以及网页客户端:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/why-dont-i-recommend-use-nostr-and-fediverse/IMG_20230730-225745590.png\" alt=\"Damus\" loading='lazy'></p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/why-dont-i-recommend-use-nostr-and-fediverse/IMG_20230730-230058046.png\" alt=\"Amethyst\" loading='lazy'></p>\n<hr>\n<p>这其中的三个客户端分別是:</p>\n<ul>\n<li>iOS: <a href=\"https://damus.io/\">Damus</a></li>\n<li>Android: <a href=\"https://github.com/vitorpamplona/amethyst\">Amethyst</a> (GitHub)</li>\n<li>Web: <a href=\"https://snort.social/\">Snort.Social</a></li>\n</ul>\n<blockquote>\n<p>在下文中将通称为 &quot;三大客户端&quot;.</p>\n</blockquote>\n<p>随后的事件便是海外媒体争先报道了这个 &quot;钞能力&quot; 事件.</p>\n<blockquote>\n<p>中国大陆自然也是包含在内的, 直到后来 Damus 被中国区 App Store 下架, 但那也是后话了.</p>\n</blockquote>\n<h2 id=\"nostr-的现状\"><a href=\"#nostr-的现状\"></a>Nostr 的现状</h2>\n<p>两个月就能让一个产品迅速从概念成形到上线主要市场, 14 BTC 功不可没, 但笔者认为, 除了金钱的灌注, 先进的产品概念, 还有其 BTC 为代表的 Web3 世界.</p>\n<p>2023 年 2 月份应该算是 Nostr 产品的首次启动, 这个月全世界的网友争先涌入 Nostr 网络, 和 Fediverse 火热时期一样: 在其他社交媒体上公布自己的 Nostr 公钥.</p>\n<blockquote>\n<p>甚至还有分不清什么是公钥私钥, 不明利害关系的网友公布了自己的私钥.</p>\n</blockquote>\n<h3 id=\"垃圾內容\"><a href=\"#垃圾內容\"></a>垃圾內容</h3>\n<p>大量的用户涌入再加上 Nostr 以抗审查为前提的自由之下, 垃圾信息自然也是泛滥成灾.</p>\n<p>Nostr 自身确实实现了杰克·多西社交媒体三原则的前两条:</p>\n<ul>\n<li>社交媒体必须能够抵抗企业和政府的控制.</li>\n<li>只有(内容)原作者才能删除他们自己制造的内容.</li>\n</ul>\n<p>唯独最后一条, 直到现在依旧是困扰 Nostr 社区的严重问题:</p>\n<ul>\n<li>反极端的审查应该通过算法实现.</li>\n</ul>\n<blockquote>\n<ul>\n<li><a href=\"https://mp.weixin.qq.com/s/AK_wGuYg6qIzaPP_rhXzvg\">最火的 Web3 社交平台 Damus，一周就变成了「互联网厕所」</a> - 微信公众号 [<a href=\"https://archive.md/5fLOL\">存档</a>]</li>\n<li><a href=\"https://m.huxiu.com/article/790506.html\">推特“杀手” Damus 在中国：拉群、卖片、英语角-虎嗅网</a></li>\n</ul>\n</blockquote>\n<p>Nostr 定义了几乎所有, 但没有定义什么是 &quot;极端&quot;, 这存在于人与人之间的行为, 很显然是不可能通过简单的计算机代码来定义的, 至少到现在还没有一个能够 &quot;反极端&quot; 的计算机 &quot;算法&quot;, 发现垃圾信息大多也是要依靠传统的用户举报和服务端中的过滤器. 再加之第二条原则, 垃圾信息自然也是只能被原作者删除, 因此在客户端中用户还是会接收到垃圾信息, 只不过它们都被隐藏了起来.</p>\n<h3 id=\"审查与封锁-vs-加密与隐私\"><a href=\"#审查与封锁-vs-加密与隐私\"></a>审查与封锁 vs 加密与隐私</h3>\n<p>Nostr 网络设计为抗审查, 不是规避审查. 客户端, 服务端以及用于实现和分发的基础设施不抗审查, 具体的案例就是中国大陆地区的 App Store 下架 Damus:</p>\n<blockquote>\n<p><a href=\"https://news.marsbit.co/20230205092952686316.html\">进驻两天后，Damus 在中国大陆苹果应用商店被下架_MarsBit</a></p>\n</blockquote>\n<p>中继也就是服务端的所有者, 他们拥有完全地审查传递到中继储存到服务器的数据的能力, 这是杰克·多西三条原则中未实现的第三条带来的悖论: <strong>如果要审查就必须要得到原始信息, 但是为了隐私如果带来的加密阻止了获得原文, 你就无法实行审查, 你不能指望人们自我审查, 就和当初泛滥成灾的垃圾信息一样.</strong> 这也同时存在破坏第二原则 &quot;只有原作者能删除内容&quot; 的灾害, 因为从中继传递过来储存到设施后, 设施所有者完全有能力操纵数据库删除他们想删除的任何东西.</p>\n<p>那么 Nostr 到底有没有加密? 其实是有的, 那就是 NIP-04: 加密私信.</p>\n<blockquote>\n<p><a href=\"https://github.com/nostr-protocol/nips/blob/master/04.md\">nips/04.md at master · nostr-protocol/nips</a></p>\n</blockquote>\n<p>而它也和使用传统电子邮件传输加密内容一样, 加密并不完整, 存在元数据泄露的问题:</p>\n<blockquote>\n<p>This standard does not go anywhere near what is considered the state-of-the-art in encrypted communication between peers, and it leaks metadata in the events, therefore it must not be used for anything you really need to keep secret, and only with relays that use <code>AUTH</code> to restrict who can fetch your <code>kind:4</code> events.</p>\n</blockquote>\n<p>如果互联网中人与人之间的信任崩塌, 最后就只能靠计算机完成信任网络的建立, 端到端加密的兴起就是最好的例子.</p>\n<blockquote>\n<p>如果真的存在一种反极端算法, 读者朋友们愿意为了 &quot;更好的互联网&quot; 交出自己的私钥给那台计算机吗?</p>\n</blockquote>\n<h3 id=\"与-web3-的融合\"><a href=\"#与-web3-的融合\"></a>与 Web3 的融合</h3>\n<blockquote>\n<p>鉴于 Web 1.0, Web 2.0 到传说中的 Web3 的各个簇拥者团体间战争不断, 为了 &quot;更好的互联网&quot;, 笔者认为使用何种世代的 Web 技术应该能成为部分人选择互联网消费产品的重要指标.</p>\n</blockquote>\n<p>Nostr 本身不是 Web3 的产品, 它是一个完全基于 Web2 网络的, 没有设计来使用诸如  Web3 设施的更加 &quot;去中心化&quot; 的元素之一.</p>\n<p>但是 Nostr 的三大客户端已经和比特币下的闪电网络形成了融合, 都支持使用闪电网络记账付款, 甚至可以为任何类型的事件附上这种货币化属性, 利用的也是闪电网络.  按照 NIPs 的合并标准, 必须要先实践再在社区中提出讨论最后才有机会合并到仓库中, Web3 的融合已经跨过了这条线, 就在 NIP-57 中:</p>\n<blockquote>\n<p><a href=\"https://github.com/nostr-protocol/nips/blob/master/57.md\">nips/57.md at master · nostr-protocol/nips</a></p>\n</blockquote>\n<p>最后, 可能是由于杰克·多西发给 Nostr 的这笔钱用的是比特币或是其他什么原因, 现在 Nostr 中也同时存在数量庞大的 Web3 拥护者群体, 与加密货币和区块链相关的内容也是随处可见的(当然也包括垃圾信息).</p>\n<h3 id=\"数据不一致和攻击事件\"><a href=\"#数据不一致和攻击事件\"></a>数据不一致和攻击事件</h3>\n<p>Nostr 使用分散的中继实现了去中心化和抗审查, 意味着使用私钥签署的信息发给每个中继的都是完全相同的一个副本, 使用相同的签名来确保数据不被仿冒和篡改, 这些都是 Nostr 为了保持数据一致性做出的努力.</p>\n<p>读者可能会问: 那为什么你还说还存在数据不一致?</p>\n<p>此时就需要我们复习一下杰克·多西的三原则其二:</p>\n<ul>\n<li>只有(内容)原作者才能删除他们自己制造的内容.</li>\n</ul>\n<p>没错, 只有保证传递完整性是不符合原则二的, 这些数据还要能够被删除. Nostr 为此专门设计了一个事件删除协议标准: NIP-09.</p>\n<blockquote>\n<p><a href=\"https://github.com/nostr-protocol/nips/blob/master/09.md\">nips/09.md at master · nostr-protocol/nips</a></p>\n</blockquote>\n<p>为了删除已经传递到中继中的内容, 需要使用 NIP-09 定义的事件类型 <code>kind:5</code> 向中继发送引用删除标记, 中继在收到后会将已存在的对应内容标记为隐藏, 客户端会对这些被删除事件引用的事件做出对应的动作, 一般是直接不显示. 并且在文档里明确说明: 删除事件无法被再次删除, 即使发出引用 <code>kind:5</code> 的新 <code>kind:5</code> 事件, 也是无效的.</p>\n<blockquote>\n<p>Publishing a deletion event against a deletion has no effect. Clients and relays are not obliged to support &quot;undelete&quot; functionality.</p>\n</blockquote>\n<p>现在条件列出完毕, 让我来给你讲一个 Nostr 小故事:</p>\n<blockquote>\n<p>你用客户端向一百个中继都发送了一条 &quot;我是靓仔&quot;, 一年时间过去了, 这期间你了解到在客户端使用这么多中继简直是手机电池克星, 于是逐渐清理掉了一些中继, 最后剩下了十二个速度和覆盖范围都不错的中继. 又过了一年, 你无意间从网络上得知两年前发的那条 &quot;我是靓仔&quot; 在部分方言里居然和 &quot;我是sb&quot; 别无二致, 你还跨越了两年的时间线找到了这条动态, 顿时感觉浑身上下有蚂蚁在爬, 于是决定里利用 <code>kind:5</code> 删除这条动态, 你也做了, 你在时间线里确实看不到这条动态了, 你心满意足地发了一条新动态说明了这件事, 于是你的关注者们立马圣地巡礼, 精准地找到了那条 &quot;我是靓仔&quot;, 但有些网友和你自己一样还是找不到. 有的网友还表示你这条消息会 &quot;闪现&quot;, 时而出现时而消失.</p>\n</blockquote>\n<p>为什么?</p>\n<ul>\n<li><strong>为什么你找不到别人却能找到?</strong> 因为你只是向当初一百个已经接收到这条消息的中继中的十二个发了删除事件, 删除没有覆盖到全部.</li>\n<li><strong>为什么有人找得到有人找不到?</strong> 因为找不到的人恰好用了和你相同的十二个中继中的几个或者全部所以他们找不到, 而有的恰好用的是另外的八十八个中继, 所以他们没有收到删除事件自然就找得到.</li>\n<li><strong>为什么有人说这条消息时而出现时而消失?</strong> 因为他们使用的中继里既有你使用的十二个中继, 也有另外的八十八个中继, 所以完全看客户端先收到的是有删除事件引用的还是没有删除事件引用的, 对应的客户端也会让这条消息时而消失(收到了删除事件), 时而出现(没有收到删除事件).</li>\n</ul>\n<p>接下来还有一个故事, 改编自真人真事:</p>\n<blockquote>\n<p>你很喜欢 Nostr, 这一年以来你在上面记录一切, 也积攒了上万粉丝, 这里面也同时有你上千条的动态, 点赞数量超过十万的也不是少数. 今天你的粉丝向你推荐了一个全新的 Nostr 客户端, 说它是专门为你这样的博主设计的, 有很多小惊喜. 你当然很高兴有适合自己的客户端, 于是毫不迟疑地从网友的私信里发给你的网盘链接下载了这个客户端开始试用. 你用私钥登录了客户端, 准备发一条动态试试感觉怎么样, 突然软件弹出一个窗口说你的公钥下消息太多需要优化本地缓存索引, 是否优化? 你觉得听起来很有道理, 毫不犹豫地点了一下 &quot;确认&quot;, 于是弹窗消失了, 什么都没有发生, 你无聊地随便点击客户端里的功能浏览着, 看着自己的粉丝数量每刷新一下就多几个很是开心. 几分钟后, 你突然收到了新粉丝的私信问你为什么你的动态都没有了. 你很疑惑, 因为你刚刚才检查过你的主页并没有什么变化, 于是你用手机打开你最常用的 Nostr 客户端重新进到你的主页, 发现你的所有动态真的全部消失了. 你想要想要恢复数据, 但无力回天. 那位向你发送客户端的那位粉丝早早删除了自己的聊天记录逃之夭夭.</p>\n</blockquote>\n<p>为什么?</p>\n<ul>\n<li><strong>到底发生了什么?</strong> 那个粉丝发给你的客户端的那个弹窗就是个钓鱼攻击, 看似优化本地索引实则是缓存了你的所有动态并且通过你自己的确认签署了对所有动态的删除事件.</li>\n<li><strong>为什么客户端能做到这么大的破坏?</strong> 因为整个 Nostr 网络中掌管私钥的客户端拥有所有权限, 只要你愿意, 你就能用私钥对你的公钥账户做任何事情, 只需要轻轻点击一下, 包括删除你的所有动态.</li>\n<li><strong>为什么无法恢复数据?</strong> 因为 NIP-09 定义的删除事件无法被再次删除.</li>\n</ul>\n<hr>\n<p>以上的两个小故事阐述了这在一对公私钥和中继器控制的 Nostr 社交网络会出现的数据不一致问题; 以及拥有私钥后的客户端能够做到的事情.</p>\n<p>面对未来针对 Nostr 社交网络的攻击, 在这对最大弱点在人的公私钥系统中要利用 NIP-09 损害账户数据简直效果拔群, 攻击者只需要做到一次就能造成无法挽回的损失, 而用户需要时时刻刻提防自己的私钥泄露.</p>\n<p>对于删除事件导致的中继数据不一致问题, 现在社区之中也有类似讨论:</p>\n<blockquote>\n<p><a href=\"https://github.com/nostr-protocol/nips/issues/669\">Better deletions / Tombstones · Issue #669 · nostr-protocol/nips</a></p>\n</blockquote>\n<p>然而 NIP-09 导致的这系列问题也只是冰山一角, 如何保护主密钥和控制私钥权限才是真正这座冰山下最大的那一部分. 针对权限控制, 现在实际上也存在解决办法, 那就是 NIP-26: 委托签署事件.</p>\n<blockquote>\n<p><a href=\"https://github.com/nostr-protocol/nips/blob/master/26.md\">nips/26.md at master · nostr-protocol/nips</a></p>\n</blockquote>\n<p>这个 NIP 定义了一种委托人和被委托人的密钥对关系, 可以做到类似 GPG 那样签署子密钥代表主秘钥进行专门和限时的行动. 减少使用主秘钥就直接降低的主密钥被攻击的概率, 甚至还能完全禁止子密钥签署发布特定类型的事件, 比如可以禁止发布 <code>kind:5</code> 的删除事件防止被失误操作和钓鱼攻击发布无法挽回的删除事件.</p>\n<p>但 NIP-26 发布至今三大客户端也没有支持进行这样的子密钥委托签署, 流行的客户端中笔者也只看到了 Gossip 支持发布这样的事件, 但也都要手写 JSON 内容, 这对于没有任何技术经验的用户来说无疑就是一大使用阻碍.</p>\n<h3 id=\"割裂的多媒体内容存储\"><a href=\"#割裂的多媒体内容存储\"></a>割裂的多媒体内容存储</h3>\n<blockquote>\n<p>社交媒体从来不是只有纯文字组成. 图片和视频虽然不足以撼动文字的地位, 但两者的力量任何一个都足以和它相提并论, 如果需要打一架, 那现在就是 1 vs 2 的局势.</p>\n</blockquote>\n<p>Nostr 的协议特性让它非常擅长处理纯文本内容, 当然它也几乎没有考虑过多媒体的问题.</p>\n<p>现在在 Nostr 上发送一张图片应该怎么做? 假设你使用了三大客户端, 你的体验或许会好很多, 只需要像大多数社交平台上一样选择好图片后就会自动上传, 然后你就会得到一个图片链接自动插入到正文中, 再发送出去就可以了.</p>\n<p>读者们可能会发现问题所在: <strong>图片传送到了外部服务器, 使用了外部链接嵌入到正文才实现了发送图片.</strong></p>\n<blockquote>\n<p>这些专门用于储存和外链图片的服务器在中文互联网有一个更熟知的名字: 图床. 类似的, 如果是专门存储视频的, 就是「视频床」, 更加广义一些, 储存文件的, 都可以叫「文件床」.</p>\n</blockquote>\n<p>问题就变成了: <strong>一个去中心化的社交网络要依赖于另一个中心化的关键基础服务</strong></p>\n<p>Nostr 的服务端本身就无法存储非结构化的二进制文件, 社区自然也发现了矛盾所在, 于是为了解决这个问题提出了 NIP-94 和 NIP-95, 它们分别是:</p>\n<ul>\n<li><strong><a href=\"https://github.com/nostr-protocol/nips/blob/master/94.md\">NIP-94</a></strong>: 文件元数据. 用于记录文件的原始信息, 包括文件链接, MIME 类型,  SHA-256 等等, 用于向客户端提供获取文件的方式和类型并且确保文件提供方提供的文件没有发生篡改. 使用 <code>kind:1063</code> 事件类型.</li>\n<li><strong><a href=\"https://github.com/nostr-protocol/nips/commit/a090de2b90f9fd83e49fa39ff4c7ef52750e904b#diff-319c9bcebb9aa1a6bb2187a77604e2cb409af365a9f504e6d38f01627b6810d4\">NIP-95</a></strong>: 中继文件存储草案(现已废除). 通过将二进制文件编码为 base64 实现直接在关系数据库中存储非结构化数据. 中继器也可以选择通过其他 NO-SQL 数据库如  MongoDB 存储文件. 使用 <code>kind:30064</code> 事件类型.</li>\n</ul>\n<p>NIP-94 实际上主要定义了另外一个 &quot;客户端&quot; 的运作方式, 也就是负责存储多媒体文件的存储服务器. 多媒体服务器需要自己支持使用 NIP-94 中定义的数据结构存储文件元信息并且使用用户私钥签署生成事件, 然后用户客户端才能使用媒体服务器签署的事件以引用事件的方式在另外一个事件中嵌入多媒体.</p>\n<p>至于被废弃的 NIP-95, 它解决文件储存的方式并不算高明, 使用 base64 编码存储二进制文件很有可能使文件体积进一步增大, 徒增中继服务器数据库的存储压力. 还好, 它后来被废弃了.</p>\n<p>很显然, Web3 宣扬的「去中心化的存储」到现在都没有真正广泛应用, 而本身基于 Web2 的 Nostr 更不可能跳出这个圈. 「多媒体如何存储?」这个问题在三大客户端两个月的冲刺之中做出了抉择: <strong>默认</strong>使用<strong>中心化</strong>的存储. 客户端它们也会让用户能够选择自己的文件会被传送到哪一个预设文件服务器.</p>\n<p>问题并没有被完全解决, NIP-94 只解决了媒体服务器和用户间的信任问题, 没有解决存储问题. 并且这些公共的媒体服务器就算无法篡改你的多媒体文件, 而只需要让你的文件无法访问就足以造成难以估量的损失.</p>\n<blockquote>\n<p>为此 NIP-94 甚至支持使用磁力(magnet)链接作为文件的提供方式, 当然最终还是要看客户端愿不愿把自己变成一个 Torrent 或者 <a href=\"https://en.wikipedia.org/wiki/WebTorrent\">WebTorrent</a> 客户端.</p>\n</blockquote>\n<blockquote>\n<p>当然了, 这个问题很可能永远无法完美解决, 毕竟人类到现在都没有找到永久记录信息的办法, 而要在人造的互联网上 &quot;永久存储&quot; 就更是不可能的事情. 并且在社交媒体上, &quot;永久存在的信息&quot; 有时候甚至会变成一种麻烦.</p>\n</blockquote>\n<p>相对地, 要更加长久地且足够 &quot;去中心化&quot; 可控地储存媒体文件, 精明的用户当然也能找到解决办法, 那就是建设自己的媒体服务器, 解析为自己的域名, 自己托管自己的媒体, 从而直接解决信任和存储问题. 不过, 客户端使用自己的媒体服务器就不会像预设的那些媒体服务器那样一键式操作, 并且目前三大客户端也没有开放自定义设置媒体服务器的选项.</p>\n<h3 id=\"永远无法到达的角落\"><a href=\"#永远无法到达的角落\"></a>永远无法到达的角落</h3>\n<blockquote>\n<p>多数人喜欢群居, 于是去中心化社交网络总是会有受欢迎的某几个节点或节点网络, 大多数的人都会选择聚集于此, 于是在去中心化网络中又催生出了新的中心化网络.</p>\n</blockquote>\n<p>在文章开头就提到, Nostr 网络中的 &quot;服务端&quot; —— 中继器, 只承担传递和存储的任务. 而客户端则需要承担从中继中取得, 缓存, 对比, 解析, 渲染再到签署和发送的所有计算任务.</p>\n<p>好吧, 如果只是与一个中继进行这样的结构化数据交换, 也并不算是 &quot;不公平&quot;. 但是如果是十个, 五十个, 一百个呢? 如果你想到达 Nostr 世界的每一个角落, 就必须与所有已知和未知的中继建立通讯.</p>\n<blockquote>\n<p><a href=\"https://nostr.watch/relays/find#public\">nostr.watch</a> (Nostr 中继状态监测)</p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/why-dont-i-recommend-use-nostr-and-fediverse/IMG_20230730-150105740.png\" alt=\"目前公共中继的数量\" loading='lazy'></p>\n<p>上方截图中的只是已知的公共中继的数量, 并没有包括那些需要付费使用的以及私有的中继. 为了实现 &quot;去中心化&quot; 理想上应该每个人都应该有自己的中继器, 但由于中继器之间互不通讯就只能通过其他方式比如手动添加或通过 NIP-05 检索.</p>\n<p>如果你希望自己的发送的动态被大多数人看到, 那么应该选择最热门的几个中继而不是往所有中继上都发一遍. 而相对的, 也有人只是用 Nostr 保存一下自己的琐事, 顺便和其他人分享一下, 那么他们也不会选择把自己的东西往所有中继上都发一遍, 而是选择自己的中继或者朋友们都在用的中继. 中心化在人与人之间的聚集后又再次诞生了.</p>\n<p>那么, 这样的由去中心化为前提造就的中心化, 算是去中心化的失败吗?</p>\n<h3 id=\"不广泛支持-rss\"><a href=\"#不广泛支持-rss\"></a>不广泛支持 RSS</h3>\n<p>截止 2023 年 11 月, Nostr 中的 RSS/Atom 问题已经得到很大的改善. Nostr 开发者 dtonon 对 <a href=\"https://github.com/fiatjaf/njump\">njump</a>(一个 Nostr 静态网关) <a href=\"https://github.com/fiatjaf/njump/commit/ac2bf854dfaecc3488d2fc914154d87305abc70b\">添加了用户时间线的 Atom 输出功能</a>, 并且阅读体验非常优秀.</p>\n\n<blockquote>\n<p>以下是本节修订前的原文:</p>\n</blockquote>\n<pre class=\"language-markdown\"><code>其实是笔者我的奇怪需求: 用 RSS 阅读器浏览用户主页时间线. 三大客户端都不支持 RSS, 当然 Nostr 的这个问题应该要交给客户端来处理, 目前 web 客户端没有一个支持 RSS/Atom 数据输出的. 仅有的一个 nostr.band 提供的付费 RSS 订阅生成服务生成的 RSS Feed 实际阅读体验很差. 甚至已经有将 RSS Feed 转换成秘钥对账户的, 但就是很多没有反其道而行之的.\n\n&gt; 笔者认为 RSS/Atom 等代表着一种通用数据交换格式, 可以和支持通过这种格式交换数据的软件很好地联合使用. 反观 Fediverse 这边, 基本上都支持 RSS 输出.😥\n</code></pre>\n<h2 id=\"nostr-vs-fediverse\"><a href=\"#nostr-vs-fediverse\"></a>Nostr vs Fediverse</h2>\n<blockquote>\n<p>社交是件非常昂贵的事情, 互联网上也是如此.</p>\n</blockquote>\n<p>笔者是个人类, 这样说可能比较诡异, 不过事实确实如此. 因为我喜欢用 &quot;人类是群居动物&quot; 来描述人需要社交的原因和必要性, 但同时我也会用 &quot;人与人之间无法相互理解&quot; 来尝试降级甚至消灭社交这种需求.</p>\n<p>社交这种需求自互联网产生就已经转移到了上面, 当时间和空间变得不再是限制社交的阻碍, 最后就只剩下了选择, 就像超市货架上的方便面, 玲琅满目的选择, 多到足以让人打消想吃方便面的的想法. 如今互联网上的社交方式就如同这货架上的方便面一样, 触手可及, 当你只有一个人的时候也许会选择困难甚至放弃选择, 但是当有一群人的时候呢? 你会选择人人都会拿的的那些方便面, 还是选择只有小孩子才会拿的方便面, 还是选择那些角落上无人问津的方便面呢?</p>\n<p>而对我来说, Nostr 就像是这货架上刚上架并且还在超市门外宣传过的那批新方便面, 有人买但不会是大多数, 就连超市自己都不知道自己进的这些货到底会不会好卖, 因为他们周围的现有产品 &quot;看起来&quot; 都太好卖了. 这些好卖的有叫 Twitter 和 Instagram 的, 也有叫 Mastodon 和 Misskey 的, 甚至还有独特风味的微信, 微博, VK 等等, 还有曾经也畅销过但现在没有什么新顾客问津的 Facebook.</p>\n<p>笔者也曾经都尝试过上面的畅销产品, 最后选择了... 放弃了买方便面!</p>\n<p>是的, 一方面上选择实在太多了, 另一方面上自觉没有什么值得发表给其他人观赏的内容, 早年的我认为自知技不如人还表露出来是种愚蠢, 但现在发生了转变, 现在的我认为技不如人还表露出来是种能开口说话的勇气, 现在如果能有人批评指出我的错误或是赞同和理解甚至因此受益我会倍感荣幸, 能给予不断前进的动力.</p>\n<p>所以, 现在我社交的目的就变成了认识更多的人, 而不再是表露自己, 不再是希望一味地寻求认同和赞美以及更多人的注视.</p>\n<hr>\n<p>在上一个章节说了那么多问题, 那么还选择 Nostr 是到底为什么?</p>\n<p>按照 Nostr 的特性将上面的 &quot;畅销产品&quot; 分类, 只存在中心化和去中心化两类产品, 至于中心化产品的优劣好坏笔者已经不再想赘述, 能够找到并且阅读到本篇这里的读者应该明白为什么有了中心化还要有去中心化这种逆向操作.</p>\n<p>接下来我会以去中心化类社交产品的明星产品与 Nostr 进行比较, 并给出 Nostr 的优势.</p>\n<h3 id=\"与域名解绑的身份标识\"><a href=\"#与域名解绑的身份标识\"></a>与域名解绑的身份标识</h3>\n<blockquote>\n<p>Nostr 网络依赖公钥定位用户, 唯一名称不需要使用域名. 但如果你愿意照样可以把域名映射到自己的公钥上, 使得拥有一个更加可读的用户名.</p>\n</blockquote>\n<p>在 Fediverse 中, 域名是一切实例实现联邦的基础, 如果你没有域名你就无法在 Fediverse 中标记出自己.</p>\n<p>作为互联网的基础设施, 域名系统从 1983 年开始到现在 2023 年的 40 年间依旧是人类进入互联网最大的那扇门, 但是随着互联网世界板块的分裂和漂移, 域名系统变得更加不平等, 不自由. 针对互联网的权力和资源争夺已经把域名系统变成了一种筹码, 随意地破坏这类基础设施已经成了夺得互联网权利过程中的一部分.</p>\n<p>真正能够让互联网居民们能够接触到的这些 &quot;门&quot; 严格上来说是门上开的无数小门, 域名持有者拥有的实际上都只是顶级域名下二级域名, 真正控制所有门的是 ICANN, 被委托或指定的域名管理局, 被域名管理局认证的域名注册商, 以及提供域名解析的解析服务器所有者, 最后就是掌握十三台根服务器以及镜像服务器的国家.</p>\n<p>可以这么说, 域名系统就是互联网最大的中心化基础设施并且极其容易被蓄意破坏. 意在实现去中心化的 ActivityPub 却选择将用户最重要的唯一标识与域名绑定, 这本来就是一个问题. 并且还让用户无法在不同域名标识下自由移动数据, 甚至连镜像都做不到, 如同一个不允许用户转移邮件的电子邮箱.</p>\n<blockquote>\n<p>想象一下你使用的电子邮箱域名由于各种原因无法再提供邮件服务, 并且你无法镜像你的数据到新的电子邮箱, 你只能设置一个堂而皇之的跳转链接 &quot;墓碑&quot; 让来到这里的人看到后还要经过一次跳转才能找到你.</p>\n</blockquote>\n<p>尽管 Nostr 网络中的中继器也依赖于域名系统, 但在这里用户转移数据比重新发贴还要简单: 删掉不再使用的中继, 添加你想要使用的中继, 重新把你事件广播到中继.</p>\n<p>并且说回到问题上: 如果你自己有一个中继, 失去对 Nostr 中继域名的控制并不会让你失去身份标识.</p>\n<h3 id=\"协议级别支持货币化\"><a href=\"#协议级别支持货币化\"></a>协议级别支持货币化</h3>\n<p>Nostr 可以随意收取代币, 中继托管者可以为服务设置为付费使用, 域名投资人可以出售域名标识认证服务, 甚至通过任何事件都能获得打赏.</p>\n<p>Web3 融合带来的一大特性就是可以将任何操作都货币化, Nostr 的货币化基于比特币下的闪电网络. 如果想通过 Nostr 营利, 实际实施起来相比 Fediverse 要更加快捷.</p>\n<blockquote>\n<p>如果服务的客户不是用户, 那这个服务很可能就是在用爱发电.</p>\n</blockquote>\n<p>如果用户没办法成为客户, 服务的托管商原则上就不会对用户负责, 反之这类服务商提供服务吸引来的真正客户大部分会是广告主和赞助商, 交易的「货物」就成了用户数据和隐私. 能够逆转这种利益关系的只有非营利服务(公益或自托管).</p>\n<p><strong>而 Fediverse 从没考虑过如何让实例营利, 以及提供营利便利的通用协议甚至功能.</strong></p>\n<p>人人都知道生存是第一要义, 人也要靠物质支持才能「用爱发电」.</p>\n<p>杰克·多西的 14 BTC 成功把 Nostr 带进了互联网, 走到了用户手上, 这是无法否认的事实. Fediverse 同样收到了众多赞助, 不少的实例直到今天依旧靠邀请制或赞助者模式走到了现在.</p>\n<p>也许会有人反驳说不想要让 Fediverse 被商业化, 不想被被资本操纵, 那么笔者会说: <strong>让产品流通于市场目的是让产品参与到市场竞争中快速成长, 而不是被所谓 &quot;资本操纵&quot;. 不参与市场竞争的产品永远只能存在于「象牙塔」之中, 这类产品永远只会讨好这些「象牙塔」里的人, 而不是作为用户的我们, 我们只是恰好迎合了他们的价值观以及需求. 想要他们变得更适合用户? 我们能做的只有: 要么试图加入他们, 要么找到新的替代品, 要么成为他们的「象牙塔」.</strong></p>\n<blockquote>\n<p>值得注意的是, Web3 中喜欢将一切操作进行货币化, 这样做的目的除了赚取收益更重要的一点通过记账操作能将一切计记入区块链中.</p>\n</blockquote>\n<p>虽然笔者并不认为 Nostr 与 Web3 结合是件讨好市场上<strong>所有用户</strong>的行为, 但至少它走出了市场化的第一步.</p>\n<h3 id=\"分布式而不是联邦\"><a href=\"#分布式而不是联邦\"></a>分布式而不是联邦</h3>\n<blockquote>\n<p>如果你有一个小团体那就非常适合 Fediverse, 但如果你是一个人, 那么就轮到需要为成为或者直接为其他小团体付出努力的时候了, 因为你一个人就是一个联邦.</p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/why-dont-i-recommend-use-nostr-and-fediverse/IMG_20230730-193138337.jpg\" alt=\"从左到右依次为: 集中式, 联邦式, 分布式.\" loading='lazy'></p>\n<blockquote>\n<p><a href=\"https://docs.joinmastodon.org/#federation\">Mastodon documentation</a> - What is federation?</p>\n</blockquote>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center\">中心化等级</th>\n<th style=\"text-align:left\">示例</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:center\">中心化</td>\n<td style=\"text-align:left\">Twitter, Facebook, Instagram</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">联邦式</td>\n<td style=\"text-align:left\">电子邮件, XMPP, 电话网络, 邮政服务</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">分布式</td>\n<td style=\"text-align:left\">BitTorrent, IPFS, Scuttlebutt</td>\n</tr>\n</tbody>\n</table>\n<p>如果要把 Nostr 放在上面三种网络模型中, 那么就是 &quot;分布式&quot;, 如果要找一个与之相似的产品来类比从而便于理解的话, 那么就是 BitTorrent: Nostr 客户端是 Torrent 客户端, Nostr 中继是 DHT 网络或 &quot;Tracker&quot;.</p>\n<blockquote>\n<p>由于 Nostr 客户端与客户端间隔了一个中继传递消息, 所以 Nostr 实际并没有真正 P2P 意义上的 Tracker.</p>\n</blockquote>\n<p>Fediverse 被称为「联邦宇宙」, 建立这样一个宇宙的前提是要有群体首先组成联邦然后再互联. 那么只有一个人如何加入 Fediverse 呢? 只能要么加入一个已经建立的联邦, 要么自己一个人成为联邦.</p>\n<p>几乎所有的 Fediverse 实例软件都被设计成适用于多人乃至大型社区, 他们 &quot;强大&quot; 到需要应对与无数联邦互联(当然前提是足够的计算资源), 但没有考虑过单人 &quot;联邦&quot; 的感受: 他们只是用 Fediverse 保存一下自己的琐事, 顺便和其他人分享一下. 僵化的联邦制思维导致 Fediverse 的最小单位无法再细分, 每个实例都默认是一个联邦, 它就必须得承受来自与其他实例互联的 &quot;人肉 DDoS&quot;, 更多的计算资源被花在了组成和维护联邦上, 而不是社交本身.</p>\n<blockquote>\n<p>这样的联邦思维再加上贯彻到底的软件设计似乎在揪着你的脑袋说: &quot;嘿! 想要加入 Fediverse 吗? 先把你的 4 核 4 G 服务器和你所有的磁盘空间贡献给联邦宇宙吧! 哦对了, 流量自理哦. 别忘了, 这是去中心化计划的一部分.&quot;</p>\n</blockquote>\n<h3 id=\"更低的服务端运行成本\"><a href=\"#更低的服务端运行成本\"></a>更低的服务端运行成本</h3>\n<p>在 Nostr 里你不用为刻意的与他人互联提前准备一切, &quot;人肉 DDoS&quot; 来到了 Nostr 身上的时候(如果真的有)你也不需要担心, 你甚至可以关闭你的私人中继器, 你的数据照常会通过其他大型公共中继或你购买的付费中继为你分发数据; 如果你不愿意做转嫁矛盾的事情, 那么你也可以为自己多设置几个专用的中继, 重新将你的事件广播到你的这些中继上分散到来的流量, 如果不够, 那就再加几个.</p>\n<p>不过实际 Nostr 网络中面对这种社交媒体大流量的拥塞最该优先考虑保护的目标是托管媒体文件的服务器.</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/why-dont-i-recommend-use-nostr-and-fediverse/IMG_20230730-195259147.png\" alt=\"Nostr 三大客户端之一的 Android 客户端 Amethyst 今年三月份通过代理处理用户资料图片开销 $932.90, 近 2200 万请求数量, 4.76 TB 流量消耗.\" loading='lazy'></p>\n<blockquote>\n<p><a href=\"https://njump.me/nevent1qqsyu3sntgj320fpueshj2gcjhv9hw7ew6lshs4xd5rtu3r6grjvujgzyprqcf0xst760qet2tglytfay2e3wmvh9asdehpjztkceyh0s5r9cnky39c\">Last month, Amethyst's Image proxy processed ~22,000,000 requests with ~4.76 TB of ...</a></p>\n</blockquote>\n<p>Nostr 得益于简单的服务端设计以及便捷的数据转移, 对其进行扩充或迁移都是非常轻松的事情. 维护一个中继就和使用 BitTorrent 软件一样轻松, 没有复杂的服务端依赖和数据结构, 只有一个 Torrent 客户端(中继程序), 一个 BitTorrent 种子(保存你数据的数据库). 流行的中继实现也已经被三大客户端验证过了性能可行性.</p>\n<p>运行 Nostr 中继的开销比 Fediverse 服务端开销低得多得多, 中继不进行大规模负载计算, 只需要将数据传递给客户端. 而客户端才是 Nostr 网络中计算密集的主要位置.</p>\n<hr>\n<p>以笔者举例, 我运行了一个私人中继以及一直在使用的图片托管, 中继使用的服务器有 1 个<strong>动态</strong>(没错不是全时的🤣) CPU 核心, 256 MB 内存, 由于使用外部数据库, 所以也只有运行中继程序的 Docker 容器占用, 本机不需要数据持久化, 除此之外还有一个个人持有的域名.</p>\n<p>早期测试用的远程数据库使用的是 Supbase 结果也一直用到了现在, 因为免费层的限制对于私人中继来说实在是太慷慨了(只要你不用 NIP-95 存文件).</p>\n<blockquote>\n<p>实际中继程序占用近三个月平均 60 MB, CPU 核心约 1%(可能有统计问题). 当然主要的原因可能是时间线上到删号之前都只有二三十条动态, 数据库存储体积为 77MB(PostgresSQL, v15.1).</p>\n</blockquote>\n<p>如果你不介意使用公共或者付费的中继, 那么这部分的时间成本甚至金钱成本都能降到更低. 当然笔者还是建议自己花钱托管多媒体文件, 并且使用自己的域名, 因为普通的 Nostr 事件发出去之后都是无法修改内容的, 包括事件内容的外链引用.</p>\n<h2 id=\"结语-去中心化的未来\"><a href=\"#结语-去中心化的未来\"></a>结语: 去中心化的未来</h2>\n<p>Nostr 只是去中心化行动中的一个声音, 如果把所有去中心化的产品放在一起看, 会发现他们的命运几乎都是沦为小众爱好或者小众需求者的玩物. 笔者不敢说现在真的人人都会用电子邮件, 人人都会下载 BitTorrent, 人人都会写独立博客, 人人都会正确使用加密货币, 类似的还有 XMPP, IRC, Matrix 等等.</p>\n<p>这逃不开的结局究竟是命中注定还是「墙倒众人推」?</p>\n<p>Nostr 并不应该设计用于取代 Fediverse, 过于超前的概念并不一定适应得了当下的环境, 况且两者都还有自己没有解决的棘手问题, 作为用户应当选择适合自己的, 你可以听从本文的一些建议去选择 Nostr 或者 Fediverse, 但要记住本文的内容实际上对两者都是「不推荐」, 他们对于没有计算机技术经验的用户并不友好, 如果你不了解其背后的设计原理和目的, 甚至没有基本的使用和理解互联网知识的能力, 请不要过早考虑他们 —— 去中心化产品.</p>\n<p>笔者认为, 当真正大多数人掌握互联网基础设施工作原理和设计理念后, 去中心化, 在互联网的去中心化才能真正成为主流. 然而, 这需要倾注一个人大量的时间, 金钱和精力去了解那些对现实生活几乎毫无实用价值的基础知识, 最后很大概率走进一个新的「象牙塔」永远止步于此.</p>\n<p>现实世界的人是否真的需要去中心化的互联网产品, 这真的还需要「人民群众」的检验.</p>\n<h2 id=\"注释\"><a href=\"#注释\"></a>注释</h2>\n<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p><a href=\"https://zh.wikipedia.org/zh-hans/%E5%9F%83%E9%9A%86%C2%B7%E9%A9%AC%E6%96%AF%E5%85%8B%E6%94%B6%E8%B4%AD%E6%8E%A8%E7%89%B9%E6%A1%88\">埃隆·马斯克收购推特案 - 维基百科，自由的百科全书</a> <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn2\" class=\"footnote-item\"><p><a href=\"https://zh.wikipedia.org/zh-cn/%E6%8E%A8%E7%89%B9%E6%96%87%E4%BB%B6\">推特文件 - 维基百科，自由的百科全书</a> <a href=\"#fnref2\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn3\" class=\"footnote-item\"><p><a href=\"https://blueskyweb.xyz/blog/3-30-2023-algorithmic-choice\">Algorithmic choice - Bluesky</a> <a href=\"#fnref3\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n","content_text":"前言 笔者先介绍一下文章的背景: 2 月份, 笔者发现了 Nostr 这样一个新兴的社交网络, 并开始尝试使用一些客户端实现. 5 月份, 笔者开始使用某 Nostr 的服务端(中继)实现, 开始深入体验. 7 月份, 大致生态和软件设计体验完成, 也正好发生了一次账号数据 &quot;事故&quot;. 今天(28 日)受到账号数据事故的启发, 决定针对 Nostr 写一些东西(但读者朋友们可能看到这篇文章是好几天后了🤣). 这篇文章的目的是「劝退」, 笔者会描述这几个月之间使用 Nostr 中遇到的问题, 当然只是 &quot;丑话说在前&quot;, 文末也会介绍这种社交网络的优势, 相比如今流行的 ActivityPub 组成的 Fediverse 之间究竟有什么理由选择新的. 本文只提供笔者主观的体验感受, 其中提到的问题对我而言是问题, 而对读者朋友们不一定是, 请不要因为我的只言片语就放弃亲自探索新事物😉. 什么是 Nostr Nostr 全称 &quot;Notes and Other Stuff Transmitted by Relays&quot;, 即 &quot;通过中继传输的笔记和其他东西&quot;. 是一种用于设计实现全球性, 去中心化与抗审查的社交网络的通讯协议. Nostr 也直接代指由这种协议组成的分布式社交网络. Nostr 的协议规范被称为 &quot;NIPs&quot;, 全称 Nostr Implementation Possibilities, 即 &quot;Nostr 可能性实现&quot;. 每一个可能的具体实现经过社区讨论和实践后会被合并进入 NIPs 作为 Nostr 的标准进一步壮大规模, 已和并的 NIPs 依旧接受社区改进和讨论, 通过 NIPs 传输的具体响应内容依照不同用途分为不同类型(kind)的事件(event), 一个 NIPs 可能是作为一个超集包含其他的 NIPs, 也可能是一个完全独立的使用全新类型的 NIPs. 比如: NIP-01: 定义了基础事件, 包含三个事件类型(0, 1, 2), 其中: kind:0 是描述秘钥元数据(可读的用户名, 个人资料,资料横幅等等 )的类型; kind:1 是社交网络中最基本的文字贴 text_note 的类型; kind:2 是一种向获取事件的客户端发送首选中继的类型. NIP-02: 定义联系人列表, 用于传递用户关注列表和追随者列表, 使用 kind:3 类型. NIP-05: 定义了如何将域名通过 DNS 和 HTTP 映射到秘钥, 用于身份认证和声明偏好中继列表, 使用 kind:0 类型. Nostr 的服务端的实现如它的名字一样为 &quot;中继(Relay)&quot;. 客户端通过一对密钥标识一个用户, 公钥用于定位一个基本用户, 私钥用于发布事件时计算消息签名进行签署发布; 中继(Relay)与客户端使用 WebSocket 协议进行通讯, 中继只负责储存和传递事件并且各中继间互不通讯, 几乎所有的计算渲染都发生在客户端. Nostr 的发展 Nostr 最早由 fiatjaf 创建, 根据 GitHub 仓库 nostr-protocol/nostr 的提交历史, 最早可以追溯到 2020 年 11 月 8 日: basic server relay code. · nostr-protocol/nostr@6158017 引来 Nostr 第一次爆发的是 Twitter 联合创始人兼前 Twitter 首席执行官: 杰克·多西(Jack Dorsey). 2022 年 11 月底 12 月初, 正值埃隆·马斯克(Elon Musk)完成 Twitter 的收购[1], 马斯克当即就向记者提供了 Twitter 内部的一些文件, 宣称是为了揭露 Twitter 存在的内容审核中的偏见和政府施加的影响[2], 随后与之相关的 Twitter 话题 &quot;#TwitterFiles&quot; 登上了流行趋势. 2022 年 12 月 14 日, 杰克·多西在自己的 Twitter 账号上发表了动态参与了 #TwitterFiles 话题的讨论, 谈到了关于马斯克揭开这块 &quot;遮羞布&quot; 后的问题的想法, 他认为社交媒体应该: 社交媒体必须能够抵抗企业和政府的控制. 只有(内容)原作者才能删除他们自己制造的内容. 反极端的审查(moderation)应该通过算法实现(algorithmic choice)[3]. 在下文将统称为三大 &quot;原则&quot;. a native internet protocol for social media | Revue (存档) 为了达到他心中的愿景, 他提到自己提供资助的 Bluesky 社交网络和其使用的 &quot;AT Protocol(AT协议)&quot;, 为了让更多人展现可能性, 他发起了一个 Twitter 话题 &quot;#startsmall&quot; 和与之关联的 &quot;open internet development(开放互联网开发)&quot; 奖金, 这笔奖金将会用于 &quot;致力于社交媒体和私有隐私通讯协议, 比特币, 纯 web 移动操作系统的工程团队&quot; 提供现金和股权投资, 还说下周就会向加密通讯软件 Signal 提供资助. 同一天, 杰克·多西在 Twitter 线程下跟帖发送了 Nostr 的 GitHub 仓库表示这是个 &quot;优秀的问题阐述和解决办法的文章&quot;: jack: &quot;excellent writeup of problems and solutions...excited to see this. and public domain:&quot; — https://t.co/bCXRwQNC0p 两天后, 也就是 2022 年 12 月 16 日, 杰克·多西简短地再次跟帖, 表示自己已经为这个项目向 fiatjaf 发送了 14 个比特币(当时价值约 $245000): jack: &quot;14 BTC deployed to @fiatjaf for #nostr&quot; 时间快进到两个月后的 2023 年 2 月 1 日, Nostr 社区依靠杰克·多西的影响以惊人的速度完成了不可思议的成就: 成功实现并在应用商店发行 Nostr 的移动平台客户端以及网页客户端: 这其中的三个客户端分別是: iOS: Damus Android: Amethyst (GitHub) Web: Snort.Social 在下文中将通称为 &quot;三大客户端&quot;. 随后的事件便是海外媒体争先报道了这个 &quot;钞能力&quot; 事件. 中国大陆自然也是包含在内的, 直到后来 Damus 被中国区 App Store 下架, 但那也是后话了. Nostr 的现状 两个月就能让一个产品迅速从概念成形到上线主要市场, 14 BTC 功不可没, 但笔者认为, 除了金钱的灌注, 先进的产品概念, 还有其 BTC 为代表的 Web3 世界. 2023 年 2 月份应该算是 Nostr 产品的首次启动, 这个月全世界的网友争先涌入 Nostr 网络, 和 Fediverse 火热时期一样: 在其他社交媒体上公布自己的 Nostr 公钥. 甚至还有分不清什么是公钥私钥, 不明利害关系的网友公布了自己的私钥. 垃圾內容 大量的用户涌入再加上 Nostr 以抗审查为前提的自由之下, 垃圾信息自然也是泛滥成灾. Nostr 自身确实实现了杰克·多西社交媒体三原则的前两条: 社交媒体必须能够抵抗企业和政府的控制. 只有(内容)原作者才能删除他们自己制造的内容. 唯独最后一条, 直到现在依旧是困扰 Nostr 社区的严重问题: 反极端的审查应该通过算法实现. 最火的 Web3 社交平台 Damus，一周就变成了「互联网厕所」 - 微信公众号 [存档] 推特“杀手” Damus 在中国：拉群、卖片、英语角-虎嗅网 Nostr 定义了几乎所有, 但没有定义什么是 &quot;极端&quot;, 这存在于人与人之间的行为, 很显然是不可能通过简单的计算机代码来定义的, 至少到现在还没有一个能够 &quot;反极端&quot; 的计算机 &quot;算法&quot;, 发现垃圾信息大多也是要依靠传统的用户举报和服务端中的过滤器. 再加之第二条原则, 垃圾信息自然也是只能被原作者删除, 因此在客户端中用户还是会接收到垃圾信息, 只不过它们都被隐藏了起来. 审查与封锁 vs 加密与隐私 Nostr 网络设计为抗审查, 不是规避审查. 客户端, 服务端以及用于实现和分发的基础设施不抗审查, 具体的案例就是中国大陆地区的 App Store 下架 Damus: 进驻两天后，Damus 在中国大陆苹果应用商店被下架_MarsBit 中继也就是服务端的所有者, 他们拥有完全地审查传递到中继储存到服务器的数据的能力, 这是杰克·多西三条原则中未实现的第三条带来的悖论: 如果要审查就必须要得到原始信息, 但是为了隐私如果带来的加密阻止了获得原文, 你就无法实行审查, 你不能指望人们自我审查, 就和当初泛滥成灾的垃圾信息一样. 这也同时存在破坏第二原则 &quot;只有原作者能删除内容&quot; 的灾害, 因为从中继传递过来储存到设施后, 设施所有者完全有能力操纵数据库删除他们想删除的任何东西. 那么 Nostr 到底有没有加密? 其实是有的, 那就是 NIP-04: 加密私信. nips/04.md at master · nostr-protocol/nips 而它也和使用传统电子邮件传输加密内容一样, 加密并不完整, 存在元数据泄露的问题: This standard does not go anywhere near what is considered the state-of-the-art in encrypted communication between peers, and it leaks metadata in the events, therefore it must not be used for anything you really need to keep secret, and only with relays that use AUTH to restrict who can fetch your kind:4 events. 如果互联网中人与人之间的信任崩塌, 最后就只能靠计算机完成信任网络的建立, 端到端加密的兴起就是最好的例子. 如果真的存在一种反极端算法, 读者朋友们愿意为了 &quot;更好的互联网&quot; 交出自己的私钥给那台计算机吗? 与 Web3 的融合 鉴于 Web 1.0, Web 2.0 到传说中的 Web3 的各个簇拥者团体间战争不断, 为了 &quot;更好的互联网&quot;, 笔者认为使用何种世代的 Web 技术应该能成为部分人选择互联网消费产品的重要指标. Nostr 本身不是 Web3 的产品, 它是一个完全基于 Web2 网络的, 没有设计来使用诸如 Web3 设施的更加 &quot;去中心化&quot; 的元素之一. 但是 Nostr 的三大客户端已经和比特币下的闪电网络形成了融合, 都支持使用闪电网络记账付款, 甚至可以为任何类型的事件附上这种货币化属性, 利用的也是闪电网络. 按照 NIPs 的合并标准, 必须要先实践再在社区中提出讨论最后才有机会合并到仓库中, Web3 的融合已经跨过了这条线, 就在 NIP-57 中: nips/57.md at master · nostr-protocol/nips 最后, 可能是由于杰克·多西发给 Nostr 的这笔钱用的是比特币或是其他什么原因, 现在 Nostr 中也同时存在数量庞大的 Web3 拥护者群体, 与加密货币和区块链相关的内容也是随处可见的(当然也包括垃圾信息). 数据不一致和攻击事件 Nostr 使用分散的中继实现了去中心化和抗审查, 意味着使用私钥签署的信息发给每个中继的都是完全相同的一个副本, 使用相同的签名来确保数据不被仿冒和篡改, 这些都是 Nostr 为了保持数据一致性做出的努力. 读者可能会问: 那为什么你还说还存在数据不一致? 此时就需要我们复习一下杰克·多西的三原则其二: 只有(内容)原作者才能删除他们自己制造的内容. 没错, 只有保证传递完整性是不符合原则二的, 这些数据还要能够被删除. Nostr 为此专门设计了一个事件删除协议标准: NIP-09. nips/09.md at master · nostr-protocol/nips 为了删除已经传递到中继中的内容, 需要使用 NIP-09 定义的事件类型 kind:5 向中继发送引用删除标记, 中继在收到后会将已存在的对应内容标记为隐藏, 客户端会对这些被删除事件引用的事件做出对应的动作, 一般是直接不显示. 并且在文档里明确说明: 删除事件无法被再次删除, 即使发出引用 kind:5 的新 kind:5 事件, 也是无效的. Publishing a deletion event against a deletion has no effect. Clients and relays are not obliged to support &quot;undelete&quot; functionality. 现在条件列出完毕, 让我来给你讲一个 Nostr 小故事: 你用客户端向一百个中继都发送了一条 &quot;我是靓仔&quot;, 一年时间过去了, 这期间你了解到在客户端使用这么多中继简直是手机电池克星, 于是逐渐清理掉了一些中继, 最后剩下了十二个速度和覆盖范围都不错的中继. 又过了一年, 你无意间从网络上得知两年前发的那条 &quot;我是靓仔&quot; 在部分方言里居然和 &quot;我是sb&quot; 别无二致, 你还跨越了两年的时间线找到了这条动态, 顿时感觉浑身上下有蚂蚁在爬, 于是决定里利用 kind:5 删除这条动态, 你也做了, 你在时间线里确实看不到这条动态了, 你心满意足地发了一条新动态说明了这件事, 于是你的关注者们立马圣地巡礼, 精准地找到了那条 &quot;我是靓仔&quot;, 但有些网友和你自己一样还是找不到. 有的网友还表示你这条消息会 &quot;闪现&quot;, 时而出现时而消失. 为什么? 为什么你找不到别人却能找到? 因为你只是向当初一百个已经接收到这条消息的中继中的十二个发了删除事件, 删除没有覆盖到全部. 为什么有人找得到有人找不到? 因为找不到的人恰好用了和你相同的十二个中继中的几个或者全部所以他们找不到, 而有的恰好用的是另外的八十八个中继, 所以他们没有收到删除事件自然就找得到. 为什么有人说这条消息时而出现时而消失? 因为他们使用的中继里既有你使用的十二个中继, 也有另外的八十八个中继, 所以完全看客户端先收到的是有删除事件引用的还是没有删除事件引用的, 对应的客户端也会让这条消息时而消失(收到了删除事件), 时而出现(没有收到删除事件). 接下来还有一个故事, 改编自真人真事: 你很喜欢 Nostr, 这一年以来你在上面记录一切, 也积攒了上万粉丝, 这里面也同时有你上千条的动态, 点赞数量超过十万的也不是少数. 今天你的粉丝向你推荐了一个全新的 Nostr 客户端, 说它是专门为你这样的博主设计的, 有很多小惊喜. 你当然很高兴有适合自己的客户端, 于是毫不迟疑地从网友的私信里发给你的网盘链接下载了这个客户端开始试用. 你用私钥登录了客户端, 准备发一条动态试试感觉怎么样, 突然软件弹出一个窗口说你的公钥下消息太多需要优化本地缓存索引, 是否优化? 你觉得听起来很有道理, 毫不犹豫地点了一下 &quot;确认&quot;, 于是弹窗消失了, 什么都没有发生, 你无聊地随便点击客户端里的功能浏览着, 看着自己的粉丝数量每刷新一下就多几个很是开心. 几分钟后, 你突然收到了新粉丝的私信问你为什么你的动态都没有了. 你很疑惑, 因为你刚刚才检查过你的主页并没有什么变化, 于是你用手机打开你最常用的 Nostr 客户端重新进到你的主页, 发现你的所有动态真的全部消失了. 你想要想要恢复数据, 但无力回天. 那位向你发送客户端的那位粉丝早早删除了自己的聊天记录逃之夭夭. 为什么? 到底发生了什么? 那个粉丝发给你的客户端的那个弹窗就是个钓鱼攻击, 看似优化本地索引实则是缓存了你的所有动态并且通过你自己的确认签署了对所有动态的删除事件. 为什么客户端能做到这么大的破坏? 因为整个 Nostr 网络中掌管私钥的客户端拥有所有权限, 只要你愿意, 你就能用私钥对你的公钥账户做任何事情, 只需要轻轻点击一下, 包括删除你的所有动态. 为什么无法恢复数据? 因为 NIP-09 定义的删除事件无法被再次删除. 以上的两个小故事阐述了这在一对公私钥和中继器控制的 Nostr 社交网络会出现的数据不一致问题; 以及拥有私钥后的客户端能够做到的事情. 面对未来针对 Nostr 社交网络的攻击, 在这对最大弱点在人的公私钥系统中要利用 NIP-09 损害账户数据简直效果拔群, 攻击者只需要做到一次就能造成无法挽回的损失, 而用户需要时时刻刻提防自己的私钥泄露. 对于删除事件导致的中继数据不一致问题, 现在社区之中也有类似讨论: Better deletions / Tombstones · Issue #669 · nostr-protocol/nips 然而 NIP-09 导致的这系列问题也只是冰山一角, 如何保护主密钥和控制私钥权限才是真正这座冰山下最大的那一部分. 针对权限控制, 现在实际上也存在解决办法, 那就是 NIP-26: 委托签署事件. nips/26.md at master · nostr-protocol/nips 这个 NIP 定义了一种委托人和被委托人的密钥对关系, 可以做到类似 GPG 那样签署子密钥代表主秘钥进行专门和限时的行动. 减少使用主秘钥就直接降低的主密钥被攻击的概率, 甚至还能完全禁止子密钥签署发布特定类型的事件, 比如可以禁止发布 kind:5 的删除事件防止被失误操作和钓鱼攻击发布无法挽回的删除事件. 但 NIP-26 发布至今三大客户端也没有支持进行这样的子密钥委托签署, 流行的客户端中笔者也只看到了 Gossip 支持发布这样的事件, 但也都要手写 JSON 内容, 这对于没有任何技术经验的用户来说无疑就是一大使用阻碍. 割裂的多媒体内容存储 社交媒体从来不是只有纯文字组成. 图片和视频虽然不足以撼动文字的地位, 但两者的力量任何一个都足以和它相提并论, 如果需要打一架, 那现在就是 1 vs 2 的局势. Nostr 的协议特性让它非常擅长处理纯文本内容, 当然它也几乎没有考虑过多媒体的问题. 现在在 Nostr 上发送一张图片应该怎么做? 假设你使用了三大客户端, 你的体验或许会好很多, 只需要像大多数社交平台上一样选择好图片后就会自动上传, 然后你就会得到一个图片链接自动插入到正文中, 再发送出去就可以了. 读者们可能会发现问题所在: 图片传送到了外部服务器, 使用了外部链接嵌入到正文才实现了发送图片. 这些专门用于储存和外链图片的服务器在中文互联网有一个更熟知的名字: 图床. 类似的, 如果是专门存储视频的, 就是「视频床」, 更加广义一些, 储存文件的, 都可以叫「文件床」. 问题就变成了: 一个去中心化的社交网络要依赖于另一个中心化的关键基础服务 Nostr 的服务端本身就无法存储非结构化的二进制文件, 社区自然也发现了矛盾所在, 于是为了解决这个问题提出了 NIP-94 和 NIP-95, 它们分别是: NIP-94: 文件元数据. 用于记录文件的原始信息, 包括文件链接, MIME 类型, SHA-256 等等, 用于向客户端提供获取文件的方式和类型并且确保文件提供方提供的文件没有发生篡改. 使用 kind:1063 事件类型. NIP-95: 中继文件存储草案(现已废除). 通过将二进制文件编码为 base64 实现直接在关系数据库中存储非结构化数据. 中继器也可以选择通过其他 NO-SQL 数据库如 MongoDB 存储文件. 使用 kind:30064 事件类型. NIP-94 实际上主要定义了另外一个 &quot;客户端&quot; 的运作方式, 也就是负责存储多媒体文件的存储服务器. 多媒体服务器需要自己支持使用 NIP-94 中定义的数据结构存储文件元信息并且使用用户私钥签署生成事件, 然后用户客户端才能使用媒体服务器签署的事件以引用事件的方式在另外一个事件中嵌入多媒体. 至于被废弃的 NIP-95, 它解决文件储存的方式并不算高明, 使用 base64 编码存储二进制文件很有可能使文件体积进一步增大, 徒增中继服务器数据库的存储压力. 还好, 它后来被废弃了. 很显然, Web3 宣扬的「去中心化的存储」到现在都没有真正广泛应用, 而本身基于 Web2 的 Nostr 更不可能跳出这个圈. 「多媒体如何存储?」这个问题在三大客户端两个月的冲刺之中做出了抉择: 默认使用中心化的存储. 客户端它们也会让用户能够选择自己的文件会被传送到哪一个预设文件服务器. 问题并没有被完全解决, NIP-94 只解决了媒体服务器和用户间的信任问题, 没有解决存储问题. 并且这些公共的媒体服务器就算无法篡改你的多媒体文件, 而只需要让你的文件无法访问就足以造成难以估量的损失. 为此 NIP-94 甚至支持使用磁力(magnet)链接作为文件的提供方式, 当然最终还是要看客户端愿不愿把自己变成一个 Torrent 或者 WebTorrent 客户端. 当然了, 这个问题很可能永远无法完美解决, 毕竟人类到现在都没有找到永久记录信息的办法, 而要在人造的互联网上 &quot;永久存储&quot; 就更是不可能的事情. 并且在社交媒体上, &quot;永久存在的信息&quot; 有时候甚至会变成一种麻烦. 相对地, 要更加长久地且足够 &quot;去中心化&quot; 可控地储存媒体文件, 精明的用户当然也能找到解决办法, 那就是建设自己的媒体服务器, 解析为自己的域名, 自己托管自己的媒体, 从而直接解决信任和存储问题. 不过, 客户端使用自己的媒体服务器就不会像预设的那些媒体服务器那样一键式操作, 并且目前三大客户端也没有开放自定义设置媒体服务器的选项. 永远无法到达的角落 多数人喜欢群居, 于是去中心化社交网络总是会有受欢迎的某几个节点或节点网络, 大多数的人都会选择聚集于此, 于是在去中心化网络中又催生出了新的中心化网络. 在文章开头就提到, Nostr 网络中的 &quot;服务端&quot; —— 中继器, 只承担传递和存储的任务. 而客户端则需要承担从中继中取得, 缓存, 对比, 解析, 渲染再到签署和发送的所有计算任务. 好吧, 如果只是与一个中继进行这样的结构化数据交换, 也并不算是 &quot;不公平&quot;. 但是如果是十个, 五十个, 一百个呢? 如果你想到达 Nostr 世界的每一个角落, 就必须与所有已知和未知的中继建立通讯. nostr.watch (Nostr 中继状态监测) 上方截图中的只是已知的公共中继的数量, 并没有包括那些需要付费使用的以及私有的中继. 为了实现 &quot;去中心化&quot; 理想上应该每个人都应该有自己的中继器, 但由于中继器之间互不通讯就只能通过其他方式比如手动添加或通过 NIP-05 检索. 如果你希望自己的发送的动态被大多数人看到, 那么应该选择最热门的几个中继而不是往所有中继上都发一遍. 而相对的, 也有人只是用 Nostr 保存一下自己的琐事, 顺便和其他人分享一下, 那么他们也不会选择把自己的东西往所有中继上都发一遍, 而是选择自己的中继或者朋友们都在用的中继. 中心化在人与人之间的聚集后又再次诞生了. 那么, 这样的由去中心化为前提造就的中心化, 算是去中心化的失败吗? 不广泛支持 RSS 截止 2023 年 11 月, Nostr 中的 RSS/Atom 问题已经得到很大的改善. Nostr 开发者 dtonon 对 njump(一个 Nostr 静态网关) 添加了用户时间线的 Atom 输出功能, 并且阅读体验非常优秀. 以下是本节修订前的原文: 123其实是笔者我的奇怪需求: 用 RSS 阅读器浏览用户主页时间线. 三大客户端都不支持 RSS, 当然 Nostr 的这个问题应该要交给客户端来处理, 目前 web 客户端没有一个支持 RSS/Atom 数据输出的. 仅有的一个 nostr.band 提供的付费 RSS 订阅生成服务生成的 RSS Feed 实际阅读体验很差. 甚至已经有将 RSS Feed 转换成秘钥对账户的, 但就是很多没有反其道而行之的.&gt; 笔者认为 RSS/Atom 等代表着一种通用数据交换格式, 可以和支持通过这种格式交换数据的软件很好地联合使用. 反观 Fediverse 这边, 基本上都支持 RSS 输出.😥 Nostr vs Fediverse 社交是件非常昂贵的事情, 互联网上也是如此. 笔者是个人类, 这样说可能比较诡异, 不过事实确实如此. 因为我喜欢用 &quot;人类是群居动物&quot; 来描述人需要社交的原因和必要性, 但同时我也会用 &quot;人与人之间无法相互理解&quot; 来尝试降级甚至消灭社交这种需求. 社交这种需求自互联网产生就已经转移到了上面, 当时间和空间变得不再是限制社交的阻碍, 最后就只剩下了选择, 就像超市货架上的方便面, 玲琅满目的选择, 多到足以让人打消想吃方便面的的想法. 如今互联网上的社交方式就如同这货架上的方便面一样, 触手可及, 当你只有一个人的时候也许会选择困难甚至放弃选择, 但是当有一群人的时候呢? 你会选择人人都会拿的的那些方便面, 还是选择只有小孩子才会拿的方便面, 还是选择那些角落上无人问津的方便面呢? 而对我来说, Nostr 就像是这货架上刚上架并且还在超市门外宣传过的那批新方便面, 有人买但不会是大多数, 就连超市自己都不知道自己进的这些货到底会不会好卖, 因为他们周围的现有产品 &quot;看起来&quot; 都太好卖了. 这些好卖的有叫 Twitter 和 Instagram 的, 也有叫 Mastodon 和 Misskey 的, 甚至还有独特风味的微信, 微博, VK 等等, 还有曾经也畅销过但现在没有什么新顾客问津的 Facebook. 笔者也曾经都尝试过上面的畅销产品, 最后选择了... 放弃了买方便面! 是的, 一方面上选择实在太多了, 另一方面上自觉没有什么值得发表给其他人观赏的内容, 早年的我认为自知技不如人还表露出来是种愚蠢, 但现在发生了转变, 现在的我认为技不如人还表露出来是种能开口说话的勇气, 现在如果能有人批评指出我的错误或是赞同和理解甚至因此受益我会倍感荣幸, 能给予不断前进的动力. 所以, 现在我社交的目的就变成了认识更多的人, 而不再是表露自己, 不再是希望一味地寻求认同和赞美以及更多人的注视. 在上一个章节说了那么多问题, 那么还选择 Nostr 是到底为什么? 按照 Nostr 的特性将上面的 &quot;畅销产品&quot; 分类, 只存在中心化和去中心化两类产品, 至于中心化产品的优劣好坏笔者已经不再想赘述, 能够找到并且阅读到本篇这里的读者应该明白为什么有了中心化还要有去中心化这种逆向操作. 接下来我会以去中心化类社交产品的明星产品与 Nostr 进行比较, 并给出 Nostr 的优势. 与域名解绑的身份标识 Nostr 网络依赖公钥定位用户, 唯一名称不需要使用域名. 但如果你愿意照样可以把域名映射到自己的公钥上, 使得拥有一个更加可读的用户名. 在 Fediverse 中, 域名是一切实例实现联邦的基础, 如果你没有域名你就无法在 Fediverse 中标记出自己. 作为互联网的基础设施, 域名系统从 1983 年开始到现在 2023 年的 40 年间依旧是人类进入互联网最大的那扇门, 但是随着互联网世界板块的分裂和漂移, 域名系统变得更加不平等, 不自由. 针对互联网的权力和资源争夺已经把域名系统变成了一种筹码, 随意地破坏这类基础设施已经成了夺得互联网权利过程中的一部分. 真正能够让互联网居民们能够接触到的这些 &quot;门&quot; 严格上来说是门上开的无数小门, 域名持有者拥有的实际上都只是顶级域名下二级域名, 真正控制所有门的是 ICANN, 被委托或指定的域名管理局, 被域名管理局认证的域名注册商, 以及提供域名解析的解析服务器所有者, 最后就是掌握十三台根服务器以及镜像服务器的国家. 可以这么说, 域名系统就是互联网最大的中心化基础设施并且极其容易被蓄意破坏. 意在实现去中心化的 ActivityPub 却选择将用户最重要的唯一标识与域名绑定, 这本来就是一个问题. 并且还让用户无法在不同域名标识下自由移动数据, 甚至连镜像都做不到, 如同一个不允许用户转移邮件的电子邮箱. 想象一下你使用的电子邮箱域名由于各种原因无法再提供邮件服务, 并且你无法镜像你的数据到新的电子邮箱, 你只能设置一个堂而皇之的跳转链接 &quot;墓碑&quot; 让来到这里的人看到后还要经过一次跳转才能找到你. 尽管 Nostr 网络中的中继器也依赖于域名系统, 但在这里用户转移数据比重新发贴还要简单: 删掉不再使用的中继, 添加你想要使用的中继, 重新把你事件广播到中继. 并且说回到问题上: 如果你自己有一个中继, 失去对 Nostr 中继域名的控制并不会让你失去身份标识. 协议级别支持货币化 Nostr 可以随意收取代币, 中继托管者可以为服务设置为付费使用, 域名投资人可以出售域名标识认证服务, 甚至通过任何事件都能获得打赏. Web3 融合带来的一大特性就是可以将任何操作都货币化, Nostr 的货币化基于比特币下的闪电网络. 如果想通过 Nostr 营利, 实际实施起来相比 Fediverse 要更加快捷. 如果服务的客户不是用户, 那这个服务很可能就是在用爱发电. 如果用户没办法成为客户, 服务的托管商原则上就不会对用户负责, 反之这类服务商提供服务吸引来的真正客户大部分会是广告主和赞助商, 交易的「货物」就成了用户数据和隐私. 能够逆转这种利益关系的只有非营利服务(公益或自托管). 而 Fediverse 从没考虑过如何让实例营利, 以及提供营利便利的通用协议甚至功能. 人人都知道生存是第一要义, 人也要靠物质支持才能「用爱发电」. 杰克·多西的 14 BTC 成功把 Nostr 带进了互联网, 走到了用户手上, 这是无法否认的事实. Fediverse 同样收到了众多赞助, 不少的实例直到今天依旧靠邀请制或赞助者模式走到了现在. 也许会有人反驳说不想要让 Fediverse 被商业化, 不想被被资本操纵, 那么笔者会说: 让产品流通于市场目的是让产品参与到市场竞争中快速成长, 而不是被所谓 &quot;资本操纵&quot;. 不参与市场竞争的产品永远只能存在于「象牙塔」之中, 这类产品永远只会讨好这些「象牙塔」里的人, 而不是作为用户的我们, 我们只是恰好迎合了他们的价值观以及需求. 想要他们变得更适合用户? 我们能做的只有: 要么试图加入他们, 要么找到新的替代品, 要么成为他们的「象牙塔」. 值得注意的是, Web3 中喜欢将一切操作进行货币化, 这样做的目的除了赚取收益更重要的一点通过记账操作能将一切计记入区块链中. 虽然笔者并不认为 Nostr 与 Web3 结合是件讨好市场上所有用户的行为, 但至少它走出了市场化的第一步. 分布式而不是联邦 如果你有一个小团体那就非常适合 Fediverse, 但如果你是一个人, 那么就轮到需要为成为或者直接为其他小团体付出努力的时候了, 因为你一个人就是一个联邦. Mastodon documentation - What is federation? 中心化等级 示例 中心化 Twitter, Facebook, Instagram 联邦式 电子邮件, XMPP, 电话网络, 邮政服务 分布式 BitTorrent, IPFS, Scuttlebutt 如果要把 Nostr 放在上面三种网络模型中, 那么就是 &quot;分布式&quot;, 如果要找一个与之相似的产品来类比从而便于理解的话, 那么就是 BitTorrent: Nostr 客户端是 Torrent 客户端, Nostr 中继是 DHT 网络或 &quot;Tracker&quot;. 由于 Nostr 客户端与客户端间隔了一个中继传递消息, 所以 Nostr 实际并没有真正 P2P 意义上的 Tracker. Fediverse 被称为「联邦宇宙」, 建立这样一个宇宙的前提是要有群体首先组成联邦然后再互联. 那么只有一个人如何加入 Fediverse 呢? 只能要么加入一个已经建立的联邦, 要么自己一个人成为联邦. 几乎所有的 Fediverse 实例软件都被设计成适用于多人乃至大型社区, 他们 &quot;强大&quot; 到需要应对与无数联邦互联(当然前提是足够的计算资源), 但没有考虑过单人 &quot;联邦&quot; 的感受: 他们只是用 Fediverse 保存一下自己的琐事, 顺便和其他人分享一下. 僵化的联邦制思维导致 Fediverse 的最小单位无法再细分, 每个实例都默认是一个联邦, 它就必须得承受来自与其他实例互联的 &quot;人肉 DDoS&quot;, 更多的计算资源被花在了组成和维护联邦上, 而不是社交本身. 这样的联邦思维再加上贯彻到底的软件设计似乎在揪着你的脑袋说: &quot;嘿! 想要加入 Fediverse 吗? 先把你的 4 核 4 G 服务器和你所有的磁盘空间贡献给联邦宇宙吧! 哦对了, 流量自理哦. 别忘了, 这是去中心化计划的一部分.&quot; 更低的服务端运行成本 在 Nostr 里你不用为刻意的与他人互联提前准备一切, &quot;人肉 DDoS&quot; 来到了 Nostr 身上的时候(如果真的有)你也不需要担心, 你甚至可以关闭你的私人中继器, 你的数据照常会通过其他大型公共中继或你购买的付费中继为你分发数据; 如果你不愿意做转嫁矛盾的事情, 那么你也可以为自己多设置几个专用的中继, 重新将你的事件广播到你的这些中继上分散到来的流量, 如果不够, 那就再加几个. 不过实际 Nostr 网络中面对这种社交媒体大流量的拥塞最该优先考虑保护的目标是托管媒体文件的服务器. Last month, Amethyst's Image proxy processed ~22,000,000 requests with ~4.76 TB of ... Nostr 得益于简单的服务端设计以及便捷的数据转移, 对其进行扩充或迁移都是非常轻松的事情. 维护一个中继就和使用 BitTorrent 软件一样轻松, 没有复杂的服务端依赖和数据结构, 只有一个 Torrent 客户端(中继程序), 一个 BitTorrent 种子(保存你数据的数据库). 流行的中继实现也已经被三大客户端验证过了性能可行性. 运行 Nostr 中继的开销比 Fediverse 服务端开销低得多得多, 中继不进行大规模负载计算, 只需要将数据传递给客户端. 而客户端才是 Nostr 网络中计算密集的主要位置. 以笔者举例, 我运行了一个私人中继以及一直在使用的图片托管, 中继使用的服务器有 1 个动态(没错不是全时的🤣) CPU 核心, 256 MB 内存, 由于使用外部数据库, 所以也只有运行中继程序的 Docker 容器占用, 本机不需要数据持久化, 除此之外还有一个个人持有的域名. 早期测试用的远程数据库使用的是 Supbase 结果也一直用到了现在, 因为免费层的限制对于私人中继来说实在是太慷慨了(只要你不用 NIP-95 存文件). 实际中继程序占用近三个月平均 60 MB, CPU 核心约 1%(可能有统计问题). 当然主要的原因可能是时间线上到删号之前都只有二三十条动态, 数据库存储体积为 77MB(PostgresSQL, v15.1). 如果你不介意使用公共或者付费的中继, 那么这部分的时间成本甚至金钱成本都能降到更低. 当然笔者还是建议自己花钱托管多媒体文件, 并且使用自己的域名, 因为普通的 Nostr 事件发出去之后都是无法修改内容的, 包括事件内容的外链引用. 结语: 去中心化的未来 Nostr 只是去中心化行动中的一个声音, 如果把所有去中心化的产品放在一起看, 会发现他们的命运几乎都是沦为小众爱好或者小众需求者的玩物. 笔者不敢说现在真的人人都会用电子邮件, 人人都会下载 BitTorrent, 人人都会写独立博客, 人人都会正确使用加密货币, 类似的还有 XMPP, IRC, Matrix 等等. 这逃不开的结局究竟是命中注定还是「墙倒众人推」? Nostr 并不应该设计用于取代 Fediverse, 过于超前的概念并不一定适应得了当下的环境, 况且两者都还有自己没有解决的棘手问题, 作为用户应当选择适合自己的, 你可以听从本文的一些建议去选择 Nostr 或者 Fediverse, 但要记住本文的内容实际上对两者都是「不推荐」, 他们对于没有计算机技术经验的用户并不友好, 如果你不了解其背后的设计原理和目的, 甚至没有基本的使用和理解互联网知识的能力, 请不要过早考虑他们 —— 去中心化产品. 笔者认为, 当真正大多数人掌握互联网基础设施工作原理和设计理念后, 去中心化, 在互联网的去中心化才能真正成为主流. 然而, 这需要倾注一个人大量的时间, 金钱和精力去了解那些对现实生活几乎毫无实用价值的基础知识, 最后很大概率走进一个新的「象牙塔」永远止步于此. 现实世界的人是否真的需要去中心化的互联网产品, 这真的还需要「人民群众」的检验. 注释 埃隆·马斯克收购推特案 - 维基百科，自由的百科全书 ↩︎ 推特文件 - 维基百科，自由的百科全书 ↩︎ Algorithmic choice - Bluesky ↩︎","summary":"前言 笔者先介绍一下文章的背景: 2 月份, 笔者发现了 Nostr 这样一个新兴的社交网络, 并开始尝试使用一些客户端实现. 5 月份, 笔者开始使用某 Nostr 的服务端(中继)实现, 开始深入体验. 7 月份, 大致生态和软件设计体验完成, 也正好发生了一次账号数据 &quot;事故&quot;. 今天(28 日)受到账号数据事故的启发, 决定针对 Nostr 写一些东西(但读者朋友们可能看到这篇文章是好几天后了🤣). 这篇文章的目的是「劝退」, 笔者会描述这几个月之间使用 Nostr 中遇到的问题, 当然只是 &quot;丑话说在前&quot;, 文末也会介绍这种社交网络的优势, 相比如今流行的 ActivityPub 组成的 Fediverse 之间究竟有什么理由选择新的. 本文只提供笔者主观的体验感受, 其中提到的问题对我而言是问题, 而对读者朋友们不一定是, 请不要因为我的只言片语就放弃亲自探索新事物😉. 什么是 Nostr Nostr 全称 &quot;Notes and Other Stuff Transmitted by Relays&quot;, 即 &quot;通过中继传输的笔记和其他东西&quot;. 是一种用于设计实现全球性, 去中心化与抗审查的社交网络的通讯协议. Nostr 也直接代指由这种协议组成的分布式社交网络. Nostr 的协议规范被称为 &quot;NIPs&quot;, 全称 Nostr Implementation Possibilities, 即 &quot;Nostr 可能性实现&quot;. 每一个可能的具体实现经过社区讨论和实践后会被合并进入 NIPs 作为 Nostr 的标准进一步壮大规模, 已和并的 NIPs 依旧接受社区改进和讨论, 通过 NIPs 传输的具体响应内容依照不同用途分为不同类型(kind)的事件(event), 一个 NIPs 可能是作为一个超集包含其他的 NIPs, 也可能是一个完全独立的使用全新类型的 NIPs. 比如: NIP-01: 定义了基础事件, 包含三个事件类型(0, 1, 2), 其中: kind:0 是描述秘钥元数据(可读的用户名, 个人资料,资料横幅等等 )的类型; kind:1 是社交网络中最基本的文字贴 text_note 的类型; kind:2 是一种向获取事件的客户端发送首选中继的类型. NIP-02: 定义联系人列表, 用于传递用户关注列表和追随者列表, 使用 kind:3 类型. NIP-05: 定义了如何将域名通过 DNS 和 HTTP 映射到秘钥, 用于身份认证和声明偏好中继列表, 使用 kind:0 类型. Nostr 的服务端的实现如它的名字一样为 &quot;中继(Relay)&quot;. 客户端通过一对密钥标识一个用户, 公钥用于定位一个基本用户, 私钥用于发布事件时计算消息签名进行签署发布; 中继(Relay)与客户端使用 WebSocket 协议进行通讯, 中继只负责储存和传递事件并且各中继间互不通讯, 几乎所有的计算渲染都发生在客户端. Nostr 的发展 Nostr 最早由 fiatjaf 创建, 根据 GitHub 仓库 nostr-protocol/nostr 的提交历史, 最早可以追溯到 2020 年 11 月 8 日: basic server relay code. · nostr-protocol/nostr@6158017 引来 Nostr 第一次爆发的是 Twitter 联合创始人兼前 Twitter 首席执行官: 杰克·多西(Jack Dorsey). 2022 年 11 月底 12 月初, 正值埃隆·马斯克(Elon Musk)完成 Twitter 的收购[1], 马斯克当即就向记者提供了 Twitter 内部的一些文件, 宣称是为了揭露 Twitter 存在的内容审核中的偏见和政府施加的影响[2], 随后与之相关的 Twitter 话题 &quot;#TwitterFiles&quot; 登上了流行趋势. 2022 年 12 月 14 日, 杰克·多西在自己的 Twitter 账号上发表了动态参与了 #TwitterFiles 话题的讨论, 谈到了关于马斯克揭开这块 &quot;遮羞布&quot; 后的问题的想法, 他认为社交媒体应该: 社交媒体必须能够抵抗企业和政府的控制. 只有(内容)原作者才能删除他们自己制造的内容. 反极端的审查(moderation)应该通过算法实现(algorithmic choice)[3]. 在下文将统称为三大 &quot;原则&quot;. a native internet protocol for social media | Revue (存档) 为了达到他心中的愿景, 他提到自己提供资助的 Bluesky 社交网络和其使用的 &quot;AT Protocol(AT协议)&quot;, 为了让更多人展现可能性, 他发起了一个 Twitter 话题 &quot;#startsmall&quot; 和与之关联的 &quot;open internet development(开放互联网开发)&quot; 奖金, 这笔奖金将会用于 &quot;致力于社交媒体和私有隐私通讯协议, 比特币, 纯 web 移动操作系统的工程团队&quot; 提供现金和股权投资, 还说下周就会向加密通讯软件 Signal 提供资助. 同一天, 杰克·多西在 Twitter 线程下跟帖发送了 Nostr 的 GitHub 仓库表示这是个 &quot;优秀的问题阐述和解决办法的文章&quot;: jack: &quot;excellent writeup of problems and solutions...excited to see this. and public domain:&quot; — https://t.co/bCXRwQNC0p 两天后, 也就是 2022 年 12 月 16 日, 杰克·多西简短地再次跟帖, 表示自己已经为这个项目向 fiatjaf 发送了 14 个比特币(当时价值约 $245000): jack: &quot;14 BTC deployed to @fiatjaf for #nostr&quot; 时间快进到两个月后的 2023 年 2 月 1 日, Nostr 社区依靠杰克·多西的影响以惊人的速度完成了不可思议的成就: 成功实现并在应用商店发行 Nostr 的移动平台客户端以及网页客户端: 这其中的三个客户端分別是: iOS: Damus Android: Amethyst (GitHub) Web: Snort.Social 在下文中将通称为 &quot;三大客户端&quot;. 随后的事件便是海外媒体争先报道了这个 &quot;钞能力&quot; 事件. 中国大陆自然也是包含在内的, 直到后来 Damus 被中国区 App Store 下架, 但那也是后话了. Nostr 的现状 两个月就能让一个产品迅速从概念成形到上线主要市场, 14 BTC 功不可没, 但笔者认为, 除了金钱的灌注, 先进的产品概念, 还有其 BTC 为代表的 Web3 世界. 2023 年 2 月份应该算是 Nostr 产品的首次启动, 这个月全世界的网友争先涌入 Nostr 网络, 和 Fediverse 火热时期一样: 在其他社交媒体上公布自己的 Nostr 公钥. 甚至还有分不清什么是公钥私钥, 不明利害关系的网友公布了自己的私钥. 垃圾內容 大量的用户涌入再加上 Nostr 以抗审查为前提的自由之下, 垃圾信息自然也是泛滥成灾. Nostr 自身确实实现了杰克·多西社交媒体三原则的前两条: 社交媒体必须能够抵抗企业和政府的控制. 只有(内容)原作者才能删除他们自己制造的内容. 唯独最后一条, 直到现在依旧是困扰 Nostr 社区的严重问题: 反极端的审查应该通过算法实现. 最火的 Web3 社交平台 Damus，一周就变成了「互联网厕所」 - 微信公众号 [存档] 推特“杀手” Damus 在中国：拉群、卖片、英语角-虎嗅网 Nostr 定义了几乎所有, 但没有定义什么是 &quot;极端&quot;, 这存在于人与人之间的行为, 很显然是不可能通过简单的计算机代码来定义的, 至少到现在还没有一个能够 &quot;反极端&quot; 的计算机 &quot;算法&quot;, 发现垃圾信息大多也是要依靠传统的用户举报和服务端中的过滤器. 再加之第二条原则, 垃圾信息自然也是只能被原作者删除, 因此在客户端中用户还是会接收到垃圾信息, 只不过它们都被隐藏了起来. 审查与封锁 vs 加密与隐私 Nostr 网络设计为抗审查, 不是规避审查. 客户端, 服务端以及用于实现和分发的基础设施不抗审查, 具体的案例就是中国大陆地区的 App Store 下架 Damus: 进驻两天后，Damus 在中国大陆苹果应用商店被下架_MarsBit 中继也就是服务端的所有者, 他们拥有完全地审查传递到中继储存到服务器的数据的能力, 这是杰克·多西三条原则中未实现的第三条带来的悖论: 如果要审查就必须要得到原始信息, 但是为了隐私如果带来的加密阻止了获得原文, 你就无法实行审查, 你不能指望人们自我审查, 就和当初泛滥成灾的垃圾信息一样. 这也同时存在破坏第二原则 &quot;只有原作者能删除内容&quot; 的灾害, 因为从中继传递过来储存到设施后, 设施所有者完全有能力操纵数据库删除他们想删除的任何东西. 那么 Nostr 到底有没有加密? 其实是有的, 那就是 NIP-04: 加密私信. nips/04.md at master · nostr-protocol/nips 而它也和使用传统电子邮件传输加密内容一样, 加密并不完整, 存在元数据泄露的问题: This standard does not go anywhere near what is considered the state-of-the-art in encrypted communication between peers, and it leaks metadata in the events, therefore it must not be used for anything you really need to keep secret, and only with relays that use AUTH to restrict who can fetch your kind:4 events. 如果互联网中人与人之间的信任崩塌, 最后就只能靠计算机完成信任网络的建立, 端到端加密的兴起就是最好的例子. 如果真的存在一种反极端算法, 读者朋友们愿意为了 &quot;更好的互联网&quot; 交出自己的私钥给那台计算机吗? 与 Web3 的融合 鉴于 Web 1.0, Web 2.0 到传说中的 Web3 的各个簇拥者团体间战争不断, 为了 &quot;更好的互联网&quot;, 笔者认为使用何种世代的 Web 技术应该能成为部分人选择互联网消费产品的重要指标. Nostr 本身不是 Web3 的产品, 它是一个完全基于 Web2 网络的, 没有设计来使用诸如 Web3 设施的更加 &quot;去中心化&quot; 的元素之一. 但是 Nostr 的三大客户端已经和比特币下的闪电网络形成了融合, 都支持使用闪电网络记账付款, 甚至可以为任何类型的事件附上这种货币化属性, 利用的也是闪电网络. 按照 NIPs 的合并标准, 必须要先实践再在社区中提出讨论最后才有机会合并到仓库中, Web3 的融合已经跨过了这条线, 就在 NIP-57 中: nips/57.md at master · nostr-protocol/nips 最后, 可能是由于杰克·多西发给 Nostr 的这笔钱用的是比特币或是其他什么原因, 现在 Nostr 中也同时存在数量庞大的 Web3 拥护者群体, 与加密货币和区块链相关的内容也是随处可见的(当然也包括垃圾信息). 数据不一致和攻击事件 Nostr 使用分散的中继实现了去中心化和抗审查, 意味着使用私钥签署的信息发给每个中继的都是完全相同的一个副本, 使用相同的签名来确保数据不被仿冒和篡改, 这些都是 Nostr 为了保持数据一致性做出的努力. 读者可能会问: 那为什么你还说还存在数据不一致? 此时就需要我们复习一下杰克·多西的三原则其二: 只有(内容)原作者才能删除他们自己制造的内容. 没错, 只有保证传递完整性是不符合原则二的, 这些数据还要能够被删除. Nostr 为此专门设计了一个事件删除协议标准: NIP-09. nips/09.md at master · nostr-protocol/nips 为了删除已经传递到中继中的内容, 需要使用 NIP-09 定义的事件类型 kind:5 向中继发送引用删除标记, 中继在收到后会将已存在的对应内容标记为隐藏, 客户端会对这些被删除事件引用的事件做出对应的动作, 一般是直接不显示. 并且在文档里明确说明: 删除事件无法被再次删除, 即使发出引用 kind:5 的新 kind:5 事件, 也是无效的. Publishing a deletion event against a deletion has no effect. Clients and relays are not obliged to support &quot;undelete&quot; functionality. 现在条件列出完毕, 让我来给你讲一个 Nostr 小故事: 你用客户端向一百个中继都发送了一条 &quot;我是靓仔&quot;, 一年时间过去了, 这期间你了解到在客户端使用这么多中继简直是手机电池克星, 于是逐渐清理掉了一些中继, 最后剩下了十二个速度和覆盖范围都不错的中继. 又过了一年, 你无意间从网络上得知两年前发的那条 &quot;我是靓仔&quot; 在部分方言里居然和 &quot;我是sb&quot; 别无二致, 你还跨越了两年的时间线找到了这条动态, 顿时感觉浑身上下有蚂蚁在爬, 于是决定里利用 kind:5 删除这条动态, 你也做了, 你在时间线里确实看不到这条动态了, 你心满意足地发了一条新动态说明了这件事, 于是你的关注者们立马圣地巡礼, 精准地找到了那条 &quot;我是靓仔&quot;, 但有些网友和你自己一样还是找不到. 有的网友还表示你这条消息会 &quot;闪现&quot;, 时而出现时而消失. 为什么? 为什么你找不到别人却能找到? 因为你只是向当初一百个已经接收到这条消息的中继中的十二个发了删除事件, 删除没有覆盖到全部. 为什么有人找得到有人找不到? 因为找不到的人恰好用了和你相同的十二个中继中的几个或者全部所以他们找不到, 而有的恰好用的是另外的八十八个中继, 所以他们没有收到删除事件自然就找得到. 为什么有人说这条消息时而出现时而消失? 因为他们使用的中继里既有你使用的十二个中继, 也有另外的八十八个中继, 所以完全看客户端先收到的是有删除事件引用的还是没有删除事件引用的, 对应的客户端也会让这条消息时而消失(收到了删除事件), 时而出现(没有收到删除事件). 接下来还有一个故事, 改编自真人真事: 你很喜欢 Nostr, 这一年以来你在上面记录一切, 也积攒了上万粉丝, 这里面也同时有你上千条的动态, 点赞数量超过十万的也不是少数. 今天你的粉丝向你推荐了一个全新的 Nostr 客户端, 说它是专门为你这样的博主设计的, 有很多小惊喜. 你当然很高兴有适合自己的客户端, 于是毫不迟疑地从网友的私信里发给你的网盘链接下载了这个客户端开始试用. 你用私钥登录了客户端, 准备发一条动态试试感觉怎么样, 突然软件弹出一个窗口说你的公钥下消息太多需要优化本地缓存索引, 是否优化? 你觉得听起来很有道理, 毫不犹豫地点了一下 &quot;确认&quot;, 于是弹窗消失了, 什么都没有发生, 你无聊地随便点击客户端里的功能浏览着, 看着自己的粉丝数量每刷新一下就多几个很是开心. 几分钟后, 你突然收到了新粉丝的私信问你为什么你的动态都没有了. 你很疑惑, 因为你刚刚才检查过你的主页并没有什么变化, 于是你用手机打开你最常用的 Nostr 客户端重新进到你的主页, 发现你的所有动态真的全部消失了. 你想要想要恢复数据, 但无力回天. 那位向你发送客户端的那位粉丝早早删除了自己的聊天记录逃之夭夭. 为什么? 到底发生了什么? 那个粉丝发给你的客户端的那个弹窗就是个钓鱼攻击, 看似优化本地索引实则是缓存了你的所有动态并且通过你自己的确认签署了对所有动态的删除事件. 为什么客户端能做到这么大的破坏? 因为整个 Nostr 网络中掌管私钥的客户端拥有所有权限, 只要你愿意, 你就能用私钥对你的公钥账户做任何事情, 只需要轻轻点击一下, 包括删除你的所有动态. 为什么无法恢复数据? 因为 NIP-09 定义的删除事件无法被再次删除. 以上的两个小故事阐述了这在一对公私钥和中继器控制的 Nostr 社交网络会出现的数据不一致问题; 以及拥有私钥后的客户端能够做到的事情. 面对未来针对 Nostr 社交网络的攻击, 在这对最大弱点在人的公私钥系统中要利用 NIP-09 损害账户数据简直效果拔群, 攻击者只需要做到一次就能造成无法挽回的损失, 而用户需要时时刻刻提防自己的私钥泄露. 对于删除事件导致的中继数据不一致问题, 现在社区之中也有类似讨论: Better deletions / Tombstones · Issue #669 · nostr-protocol/nips 然而 NIP-09 导致的这系列问题也只是冰山一角, 如何保护主密钥和控制私钥权限才是真正这座冰山下最大的那一部分. 针对权限控制, 现在实际上也存在解决办法, 那就是 NIP-26: 委托签署事件. nips/26.md at master · nostr-protocol/nips 这个 NIP 定义了一种委托人和被委托人的密钥对关系, 可以做到类似 GPG 那样签署子密钥代表主秘钥进行专门和限时的行动. 减少使用主秘钥就直接降低的主密钥被攻击的概率, 甚至还能完全禁止子密钥签署发布特定类型的事件, 比如可以禁止发布 kind:5 的删除事件防止被失误操作和钓鱼攻击发布无法挽回的删除事件. 但 NIP-26 发布至今三大客户端也没有支持进行这样的子密钥委托签署, 流行的客户端中笔者也只看到了 Gossip 支持发布这样的事件, 但也都要手写 JSON 内容, 这对于没有任何技术经验的用户来说无疑就是一大使用阻碍. 割裂的多媒体内容存储 社交媒体从来不是只有纯文字组成. 图片和视频虽然不足以撼动文字的地位, 但两者的力量任何一个都足以和它相提并论, 如果需要打一架, 那现在就是 1 vs 2 的局势. Nostr 的协议特性让它非常擅长处理纯文本内容, 当然它也几乎没有考虑过多媒体的问题. 现在在 Nostr 上发送一张图片应该怎么做? 假设你使用了三大客户端, 你的体验或许会好很多, 只需要像大多数社交平台上一样选择好图片后就会自动上传, 然后你就会得到一个图片链接自动插入到正文中, 再发送出去就可以了. 读者们可能会发现问题所在: 图片传送到了外部服务器, 使用了外部链接嵌入到正文才实现了发送图片. 这些专门用于储存和外链图片的服务器在中文互联网有一个更熟知的名字: 图床. 类似的, 如果是专门存储视频的, 就是「视频床」, 更加广义一些, 储存文件的, 都可以叫「文件床」. 问题就变成了: 一个去中心化的社交网络要依赖于另一个中心化的关键基础服务 Nostr 的服务端本身就无法存储非结构化的二进制文件, 社区自然也发现了矛盾所在, 于是为了解决这个问题提出了 NIP-94 和 NIP-95, 它们分别是: NIP-94: 文件元数据. 用于记录文件的原始信息, 包括文件链接, MIME 类型, SHA-256 等等, 用于向客户端提供获取文件的方式和类型并且确保文件提供方提供的文件没有发生篡改. 使用 kind:1063 事件类型. NIP-95: 中继文件存储草案(现已废除). 通过将二进制文件编码为 base64 实现直接在关系数据库中存储非结构化数据. 中继器也可以选择通过其他 NO-SQL 数据库如 MongoDB 存储文件. 使用 kind:30064 事件类型. NIP-94 实际上主要定义了另外一个 &quot;客户端&quot; 的运作方式, 也就是负责存储多媒体文件的存储服务器. 多媒体服务器需要自己支持使用 NIP-94 中定义的数据结构存储文件元信息并且使用用户私钥签署生成事件, 然后用户客户端才能使用媒体服务器签署的事件以引用事件的方式在另外一个事件中嵌入多媒体. 至于被废弃的 NIP-95, 它解决文件储存的方式并不算高明, 使用 base64 编码存储二进制文件很有可能使文件体积进一步增大, 徒增中继服务器数据库的存储压力. 还好, 它后来被废弃了. 很显然, Web3 宣扬的「去中心化的存储」到现在都没有真正广泛应用, 而本身基于 Web2 的 Nostr 更不可能跳出这个圈. 「多媒体如何存储?」这个问题在三大客户端两个月的冲刺之中做出了抉择: 默认使用中心化的存储. 客户端它们也会让用户能够选择自己的文件会被传送到哪一个预设文件服务器. 问题并没有被完全解决, NIP-94 只解决了媒体服务器和用户间的信任问题, 没有解决存储问题. 并且这些公共的媒体服务器就算无法篡改你的多媒体文件, 而只需要让你的文件无法访问就足以造成难以估量的损失. 为此 NIP-94 甚至支持使用磁力(magnet)链接作为文件的提供方式, 当然最终还是要看客户端愿不愿把自己变成一个 Torrent 或者 WebTorrent 客户端. 当然了, 这个问题很可能永远无法完美解决, 毕竟人类到现在都没有找到永久记录信息的办法, 而要在人造的互联网上 &quot;永久存储&quot; 就更是不可能的事情. 并且在社交媒体上, &quot;永久存在的信息&quot; 有时候甚至会变成一种麻烦. 相对地, 要更加长久地且足够 &quot;去中心化&quot; 可控地储存媒体文件, 精明的用户当然也能找到解决办法, 那就是建设自己的媒体服务器, 解析为自己的域名, 自己托管自己的媒体, 从而直接解决信任和存储问题. 不过, 客户端使用自己的媒体服务器就不会像预设的那些媒体服务器那样一键式操作, 并且目前三大客户端也没有开放自定义设置媒体服务器的选项. 永远无法到达的角落 多数人喜欢群居, 于是去中心化社交网络总是会有受欢迎的某几个节点或节点网络, 大多数的人都会选择聚集于此, 于是在去中心化网络中又催生出了新的中心化网络. 在文章开头就提到, Nostr 网络中的 &quot;服务端&quot; —— 中继器, 只承担传递和存储的任务. 而客户端则需要承担从中继中取得, 缓存, 对比, 解析, 渲染再到签署和发送的所有计算任务. 好吧, 如果只是与一个中继进行这样的结构化数据交换, 也并不算是 &quot;不公平&quot;. 但是如果是十个, 五十个, 一百个呢? 如果你想到达 Nostr 世界的每一个角落, 就必须与所有已知和未知的中继建立通讯. nostr.watch (Nostr 中继状态监测) 上方截图中的只是已知的公共中继的数量, 并没有包括那些需要付费使用的以及私有的中继. 为了实现 &quot;去中心化&quot; 理想上应该每个人都应该有自己的中继器, 但由于中继器之间互不通讯就只能通过其他方式比如手动添加或通过 NIP-05 检索. 如果你希望自己的发送的动态被大多数人看到, 那么应该选择最热门的几个中继而不是往所有中继上都发一遍. 而相对的, 也有人只是用 Nostr 保存一下自己的琐事, 顺便和其他人分享一下, 那么他们也不会选择把自己的东西往所有中继上都发一遍, 而是选择自己的中继或者朋友们都在用的中继. 中心化在人与人之间的聚集后又再次诞生了. 那么, 这样的由去中心化为前提造就的中心化, 算是去中心化的失败吗? 不广泛支持 RSS 截止 2023 年 11 月, Nostr 中的 RSS/Atom 问题已经得到很大的改善. Nostr 开发者 dtonon 对 njump(一个 Nostr 静态网关) 添加了用户时间线的 Atom 输出功能, 并且阅读体验非常优秀. 以下是本节修订前的原文: 123其实是笔者我的奇怪需求: 用 RSS 阅读器浏览用户主页时间线. 三大客户端都不支持 RSS, 当然 Nostr 的这个问题应该要交给客户端来处理, 目前 web 客户端没有一个支持 RSS/Atom 数据输出的. 仅有的一个 nostr.band 提供的付费 RSS 订阅生成服务生成的 RSS Feed 实际阅读体验很差. 甚至已经有将 RSS Feed 转换成秘钥对账户的, 但就是很多没有反其道而行之的.&gt; 笔者认为 RSS/Atom 等代表着一种通用数据交换格式, 可以和支持通过这种格式交换数据的软件很好地联合使用. 反观 Fediverse 这边, 基本上都支持 RSS 输出.😥 Nostr vs Fediverse 社交是件非常昂贵的事情, 互联网上也是如此. 笔者是个人类, 这样说可能比较诡异, 不过事实确实如此. 因为我喜欢用 &quot;人类是群居动物&quot; 来描述人需要社交的原因和必要性, 但同时我也会用 &quot;人与人之间无法相互理解&quot; 来尝试降级甚至消灭社交这种需求. 社交这种需求自互联网产生就已经转移到了上面, 当时间和空间变得不再是限制社交的阻碍, 最后就只剩下了选择, 就像超市货架上的方便面, 玲琅满目的选择, 多到足以让人打消想吃方便面的的想法. 如今互联网上的社交方式就如同这货架上的方便面一样, 触手可及, 当你只有一个人的时候也许会选择困难甚至放弃选择, 但是当有一群人的时候呢? 你会选择人人都会拿的的那些方便面, 还是选择只有小孩子才会拿的方便面, 还是选择那些角落上无人问津的方便面呢? 而对我来说, Nostr 就像是这货架上刚上架并且还在超市门外宣传过的那批新方便面, 有人买但不会是大多数, 就连超市自己都不知道自己进的这些货到底会不会好卖, 因为他们周围的现有产品 &quot;看起来&quot; 都太好卖了. 这些好卖的有叫 Twitter 和 Instagram 的, 也有叫 Mastodon 和 Misskey 的, 甚至还有独特风味的微信, 微博, VK 等等, 还有曾经也畅销过但现在没有什么新顾客问津的 Facebook. 笔者也曾经都尝试过上面的畅销产品, 最后选择了... 放弃了买方便面! 是的, 一方面上选择实在太多了, 另一方面上自觉没有什么值得发表给其他人观赏的内容, 早年的我认为自知技不如人还表露出来是种愚蠢, 但现在发生了转变, 现在的我认为技不如人还表露出来是种能开口说话的勇气, 现在如果能有人批评指出我的错误或是赞同和理解甚至因此受益我会倍感荣幸, 能给予不断前进的动力. 所以, 现在我社交的目的就变成了认识更多的人, 而不再是表露自己, 不再是希望一味地寻求认同和赞美以及更多人的注视. 在上一个章节说了那么多问题, 那么还选择 Nostr 是到底为什么? 按照 Nostr 的特性将上面的 &quot;畅销产品&quot; 分类, 只存在中心化和去中心化两类产品, 至于中心化产品的优劣好坏笔者已经不再想赘述, 能够找到并且阅读到本篇这里的读者应该明白为什么有了中心化还要有去中心化这种逆向操作. 接下来我会以去中心化类社交产品的明星产品与 Nostr 进行比较, 并给出 Nostr 的优势. 与域名解绑的身份标识 Nostr 网络依赖公钥定位用户, 唯一名称不需要使用域名. 但如果你愿意照样可以把域名映射到自己的公钥上, 使得拥有一个更加可读的用户名. 在 Fediverse 中, 域名是一切实例实现联邦的基础, 如果你没有域名你就无法在 Fediverse 中标记出自己. 作为互联网的基础设施, 域名系统从 1983 年开始到现在 2023 年的 40 年间依旧是人类进入互联网最大的那扇门, 但是随着互联网世界板块的分裂和漂移, 域名系统变得更加不平等, 不自由. 针对互联网的权力和资源争夺已经把域名系统变成了一种筹码, 随意地破坏这类基础设施已经成了夺得互联网权利过程中的一部分. 真正能够让互联网居民们能够接触到的这些 &quot;门&quot; 严格上来说是门上开的无数小门, 域名持有者拥有的实际上都只是顶级域名下二级域名, 真正控制所有门的是 ICANN, 被委托或指定的域名管理局, 被域名管理局认证的域名注册商, 以及提供域名解析的解析服务器所有者, 最后就是掌握十三台根服务器以及镜像服务器的国家. 可以这么说, 域名系统就是互联网最大的中心化基础设施并且极其容易被蓄意破坏. 意在实现去中心化的 ActivityPub 却选择将用户最重要的唯一标识与域名绑定, 这本来就是一个问题. 并且还让用户无法在不同域名标识下自由移动数据, 甚至连镜像都做不到, 如同一个不允许用户转移邮件的电子邮箱. 想象一下你使用的电子邮箱域名由于各种原因无法再提供邮件服务, 并且你无法镜像你的数据到新的电子邮箱, 你只能设置一个堂而皇之的跳转链接 &quot;墓碑&quot; 让来到这里的人看到后还要经过一次跳转才能找到你. 尽管 Nostr 网络中的中继器也依赖于域名系统, 但在这里用户转移数据比重新发贴还要简单: 删掉不再使用的中继, 添加你想要使用的中继, 重新把你事件广播到中继. 并且说回到问题上: 如果你自己有一个中继, 失去对 Nostr 中继域名的控制并不会让你失去身份标识. 协议级别支持货币化 Nostr 可以随意收取代币, 中继托管者可以为服务设置为付费使用, 域名投资人可以出售域名标识认证服务, 甚至通过任何事件都能获得打赏. Web3 融合带来的一大特性就是可以将任何操作都货币化, Nostr 的货币化基于比特币下的闪电网络. 如果想通过 Nostr 营利, 实际实施起来相比 Fediverse 要更加快捷. 如果服务的客户不是用户, 那这个服务很可能就是在用爱发电. 如果用户没办法成为客户, 服务的托管商原则上就不会对用户负责, 反之这类服务商提供服务吸引来的真正客户大部分会是广告主和赞助商, 交易的「货物」就成了用户数据和隐私. 能够逆转这种利益关系的只有非营利服务(公益或自托管). 而 Fediverse 从没考虑过如何让实例营利, 以及提供营利便利的通用协议甚至功能. 人人都知道生存是第一要义, 人也要靠物质支持才能「用爱发电」. 杰克·多西的 14 BTC 成功把 Nostr 带进了互联网, 走到了用户手上, 这是无法否认的事实. Fediverse 同样收到了众多赞助, 不少的实例直到今天依旧靠邀请制或赞助者模式走到了现在. 也许会有人反驳说不想要让 Fediverse 被商业化, 不想被被资本操纵, 那么笔者会说: 让产品流通于市场目的是让产品参与到市场竞争中快速成长, 而不是被所谓 &quot;资本操纵&quot;. 不参与市场竞争的产品永远只能存在于「象牙塔」之中, 这类产品永远只会讨好这些「象牙塔」里的人, 而不是作为用户的我们, 我们只是恰好迎合了他们的价值观以及需求. 想要他们变得更适合用户? 我们能做的只有: 要么试图加入他们, 要么找到新的替代品, 要么成为他们的「象牙塔」. 值得注意的是, Web3 中喜欢将一切操作进行货币化, 这样做的目的除了赚取收益更重要的一点通过记账操作能将一切计记入区块链中. 虽然笔者并不认为 Nostr 与 Web3 结合是件讨好市场上所有用户的行为, 但至少它走出了市场化的第一步. 分布式而不是联邦 如果你有一个小团体那就非常适合 Fediverse, 但如果你是一个人, 那么就轮到需要为成为或者直接为其他小团体付出努力的时候了, 因为你一个人就是一个联邦. Mastodon documentation - What is federation? 中心化等级 示例 中心化 Twitter, Facebook, Instagram 联邦式 电子邮件, XMPP, 电话网络, 邮政服务 分布式 BitTorrent, IPFS, Scuttlebutt 如果要把 Nostr 放在上面三种网络模型中, 那么就是 &quot;分布式&quot;, 如果要找一个与之相似的产品来类比从而便于理解的话, 那么就是 BitTorrent: Nostr 客户端是 Torrent 客户端, Nostr 中继是 DHT 网络或 &quot;Tracker&quot;. 由于 Nostr 客户端与客户端间隔了一个中继传递消息, 所以 Nostr 实际并没有真正 P2P 意义上的 Tracker. Fediverse 被称为「联邦宇宙」, 建立这样一个宇宙的前提是要有群体首先组成联邦然后再互联. 那么只有一个人如何加入 Fediverse 呢? 只能要么加入一个已经建立的联邦, 要么自己一个人成为联邦. 几乎所有的 Fediverse 实例软件都被设计成适用于多人乃至大型社区, 他们 &quot;强大&quot; 到需要应对与无数联邦互联(当然前提是足够的计算资源), 但没有考虑过单人 &quot;联邦&quot; 的感受: 他们只是用 Fediverse 保存一下自己的琐事, 顺便和其他人分享一下. 僵化的联邦制思维导致 Fediverse 的最小单位无法再细分, 每个实例都默认是一个联邦, 它就必须得承受来自与其他实例互联的 &quot;人肉 DDoS&quot;, 更多的计算资源被花在了组成和维护联邦上, 而不是社交本身. 这样的联邦思维再加上贯彻到底的软件设计似乎在揪着你的脑袋说: &quot;嘿! 想要加入 Fediverse 吗? 先把你的 4 核 4 G 服务器和你所有的磁盘空间贡献给联邦宇宙吧! 哦对了, 流量自理哦. 别忘了, 这是去中心化计划的一部分.&quot; 更低的服务端运行成本 在 Nostr 里你不用为刻意的与他人互联提前准备一切, &quot;人肉 DDoS&quot; 来到了 Nostr 身上的时候(如果真的有)你也不需要担心, 你甚至可以关闭你的私人中继器, 你的数据照常会通过其他大型公共中继或你购买的付费中继为你分发数据; 如果你不愿意做转嫁矛盾的事情, 那么你也可以为自己多设置几个专用的中继, 重新将你的事件广播到你的这些中继上分散到来的流量, 如果不够, 那就再加几个. 不过实际 Nostr 网络中面对这种社交媒体大流量的拥塞最该优先考虑保护的目标是托管媒体文件的服务器. Last month, Amethyst's Image proxy processed ~22,000,000 requests with ~4.76 TB of ... Nostr 得益于简单的服务端设计以及便捷的数据转移, 对其进行扩充或迁移都是非常轻松的事情. 维护一个中继就和使用 BitTorrent 软件一样轻松, 没有复杂的服务端依赖和数据结构, 只有一个 Torrent 客户端(中继程序), 一个 BitTorrent 种子(保存你数据的数据库). 流行的中继实现也已经被三大客户端验证过了性能可行性. 运行 Nostr 中继的开销比 Fediverse 服务端开销低得多得多, 中继不进行大规模负载计算, 只需要将数据传递给客户端. 而客户端才是 Nostr 网络中计算密集的主要位置. 以笔者举例, 我运行了一个私人中继以及一直在使用的图片托管, 中继使用的服务器有 1 个动态(没错不是全时的🤣) CPU 核心, 256 MB 内存, 由于使用外部数据库, 所以也只有运行中继程序的 Docker 容器占用, 本机不需要数据持久化, 除此之外还有一个个人持有的域名. 早期测试用的远程数据库使用的是 Supbase 结果也一直用到了现在, 因为免费层的限制对于私人中继来说实在是太慷慨了(只要你不用 NIP-95 存文件). 实际中继程序占用近三个月平均 60 MB, CPU 核心约 1%(可能有统计问题). 当然主要的原因可能是时间线上到删号之前都只有二三十条动态, 数据库存储体积为 77MB(PostgresSQL, v15.1). 如果你不介意使用公共或者付费的中继, 那么这部分的时间成本甚至金钱成本都能降到更低. 当然笔者还是建议自己花钱托管多媒体文件, 并且使用自己的域名, 因为普通的 Nostr 事件发出去之后都是无法修改内容的, 包括事件内容的外链引用. 结语: 去中心化的未来 Nostr 只是去中心化行动中的一个声音, 如果把所有去中心化的产品放在一起看, 会发现他们的命运几乎都是沦为小众爱好或者小众需求者的玩物. 笔者不敢说现在真的人人都会用电子邮件, 人人都会下载 BitTorrent, 人人都会写独立博客, 人人都会正确使用加密货币, 类似的还有 XMPP, IRC, Matrix 等等. 这逃不开的结局究竟是命中注定还是「墙倒众人推」? Nostr 并不应该设计用于取代 Fediverse, 过于超前的概念并不一定适应得了当下的环境, 况且两者都还有自己没有解决的棘手问题, 作为用户应当选择适合自己的, 你可以听从本文的一些建议去选择 Nostr 或者 Fediverse, 但要记住本文的内容实际上对两者都是「不推荐」, 他们对于没有计算机技术经验的用户并不友好, 如果你不了解其背后的设计原理和目的, 甚至没有基本的使用和理解互联网知识的能力, 请不要过早考虑他们 —— 去中心化产品. 笔者认为, 当真正大多数人掌握互联网基础设施工作原理和设计理念后, 去中心化, 在互联网的去中心化才能真正成为主流. 然而, 这需要倾注一个人大量的时间, 金钱和精力去了解那些对现实生活几乎毫无实用价值的基础知识, 最后很大概率走进一个新的「象牙塔」永远止步于此. 现实世界的人是否真的需要去中心化的互联网产品, 这真的还需要「人民群众」的检验. 注释 埃隆·马斯克收购推特案 - 维基百科，自由的百科全书 ↩︎ 推特文件 - 维基百科，自由的百科全书 ↩︎ Algorithmic choice - Bluesky ↩︎","date_published":"2023-07-30T17:47:00.000Z","tags":["博物志","社交媒体","Nostr","Fediverse","ActivityPub","Web3","去中心化","社交媒体"]},{"id":"https://blog.cxplay.org/works/in-circle/","url":"https://blog.cxplay.org/works/in-circle/","title":"圈中人","content_html":"<h2 id=\"序\"><a href=\"#序\"></a>序</h2>\n<p>外面很危险, 于是有人在地上画了一圈并对我说: &quot;这个圈外很危险, 你不要随便出去, 我会帮你对付这些危险, 所以我很忙, 但我也会派人监督你.&quot;;</p>\n<p>监督人来了, 他对我说: &quot;你知道要怎么做了吧？但为了防止你总是不小心碰到边界, 我会给你的粗心大意一些小小的惩罚, 好让你长记性, 毕竟我要监督的人可不止你一个. &quot;, 于是监督人在圈内画了一个更小的圈.</p>\n<p>最后, 我发现睡觉的时候翻个身也总是不小心超出监督人画的圈, 于是我自己给自己画了一个圈, 好让我只能站在原地不得动弹就再也不能睡觉, 也就不会出现无意识地超出圈外了;</p>\n<p>但我好像忘记了我本来是可以出去的, 但是由于缺少保护自己的经验, 我也渐渐不敢出去了. 因为相比之下, 监督人的惩罚显得比圈外的危险来得更具体更具有危险性, 我也更有经验去应付.</p>\n<h2 id=\"正文\"><a href=\"#正文\"></a>正文</h2>\n<p>这里的人常说, 如今这个地方变成这个样子完全是由于那座灯塔.</p>\n<p>在那个我还未曾触及到的时代, 这里的格局并不是这个样子, 这里和外面的世界都是一样的一马平川无所遮拦. 没有如今形同棋盘一样的森严布局, 更没有那座灯塔. 但是不知道什么时候, 似乎是从巨墙开始, 有一部分人开始为这个地方建起了障碍.</p>\n<p>起初只是一道篱笆, 一个土沟, 再后来不约而同地全都变成了深红色质地的石块, 这种材料从来没有听说过是从什么地方开采出来, 但就现在的状态来看, 这墙就好像是从地底自己长出来一般: 整齐划一, 密不透风, 坚不可摧. 不过现在也有人觉察到, 曾经光滑细密的红墙上出现了裂缝, 但这裂缝实质上与这墙并没有多大关系, 因为裂缝时而出现时而消失, 没有人知道红墙是如何做到这种程度的自愈的, 就好像它是活的一样.</p>\n<p>再说起那个灯塔, 它的材质和红墙并不一样, 但据说两者建立起来的时间都是相同的. 灯塔没有密不透风的样子, 它的表面就像积木拼接一般有很明显的缝隙, 甚至有的地方还有空缺, 不过这些缺陷也和墙上的缝隙一样时而消失时而出现. 但就我认知之中看来, 人人都说这座灯塔是这里的每个人亲手筑起的, 却没有人知道这墙的成因, 可能是因为人人心里都明白但都心照不宣, 也可能是因为它本来就是自己从这块大地上 &quot;生长&quot; 出来的, 因为它的材质从今天看始终都不像是这个大陆上应该存在的东西.</p>\n<hr>\n<p>那片生长在红墙上的蓝色平原, 听说最开始的时候和墙的颜色一样, 是红色的, 后来平原的住民们逐渐发现所有的植物开始变成了蓝色, 土壤也变成了深蓝色. 由此原来单调的墙上多出来一片蓝色, 也吸引了更多的斗篷们过来在平原上定居. 一位从红色时期就开始在这里居住的斗篷曾经和我说, 目前来说这里变成蓝色和红色的时候并没有什么差别, 但其他斗篷们却都不约而同的住进了这里, 问起这些新来者理由却一个二个都含糊其词甚至回答不上来到底是为什么来到这里, 听得最多的一个理由就是: &quot;因为大家都来这里了.&quot; 事实真是这样的话, 那这个平原上早就已经人满为患了, 有相当部分人踏足这里之后就离去了, 还有部分人暂住过一段时间后也悄然离去, 他们留下的痕迹会很快地消失, 绵密的蓝色植被会再次长满被斗篷践踏的区域, 平原变得就好像从来没有人来过的一样.</p>\n<p>当我问起这位斗篷他自己来这里的理由的时候, 它也沉默了, 但没有很久, 它问我: &quot;你喜欢听故事吗?&quot;, 我说: &quot;是真相吗?&quot;, &quot;我也... 不知道.&quot; 他回答我.</p>\n<p>我接着说: &quot;好吧, 虽然我不是来听故事的, 但如果是你愿意说给我听的故事, 我也愿闻其详.&quot;</p>\n<p>斗篷没有办法呈现出背后人的表情, 所以我在这面具上也办法没找到什么破绽, 它拿起了篝火边的一根细木柴在灰烬边画了起来.</p>\n<p>它画了一个圆圈: &quot;我们现在的位置应该是这里, 应该是.&quot; 它在圆圈边上加了一个小圆圈, 指着它说.</p>\n<p>&quot;但如果我再把里面的「棋盘」画出来.&quot; 它在大圆中画了几个小圆.</p>\n<p>&quot;如果我没猜错, 你我都是这棋盘里面的人, 你应该会明白.&quot; 它接着在大圆和几个小圆之间用另外一个内圆隔开了, 出现了一个同心圆套一堆小圆圈的图画. 最后它在同心圆里面套了更多的圆, 为了套下更多的圆还把几个小圆擦去了, 几个小圆最后被数个大圆套在里面, 整个画面就好像一个靶子一样.</p>\n<p>它放下了木柴, 对我说 &quot;曾经我也是这样一个个小圆中的人, 总以为外面有什么好东西, 想要出去看看.&quot;</p>\n<p>&quot;后来, 我真的出去了, 这个小圆不再是束缚, 但我发现外面的世界有更多的圆圈.&quot; 它指着那些同心圆一路向外, 最后到了代表我们位置的那个大圆边缘上的小圆.</p>\n<p>&quot;最后我费尽千辛万苦, 终于从这些圆里出来了, 到了这里, 现在被叫做「蓝色平原」的地方.&quot; 它又拿起细木柴握在手心.</p>\n<p>&quot;还是红色的那个时候, 我心里就想, 这里也许就是这世界的最后一个圆了吧?&quot;</p>\n<p>&quot;说实话, 现在我们这个圆外面的景色从来就没变过, 我最后本来打算继续往外去亲眼看看那些五彩斑斓的风景的.&quot;</p>\n<p>它沉默下来, 用木柴继续在最外围的圆上加了一个更大的圆, 不过这次画布的面积不太够用了, 只画了一部分.</p>\n<p>它指着新画的那个圆对我说: &quot;有一天我在平原上看到一个蓝色的斗篷, 我一如既往地和它打着招呼, 但是它说着一口不是我们的语言, 它好像也听不懂我在说什么.&quot;</p>\n<p>&quot;蓝色的斗篷好像也明白了我们两人语言不通, 就在脚下开始用手指在雪地上画画.&quot;</p>\n<p>&quot;一个圈, 两个圈, 三个圈. 我已经记不清他当时画了多少个圈了, 最后同心圆的外围上也多出了一个代表着当时位置的小圆.&quot;</p>\n<p>&quot;看起来就和现在这幅画几乎一模一样, 我现在都不知道它当时到底是在往圈外走还是进到一个新的圈里. 「蓝色平原」这个小圈又在什么位置呢?&quot; 它又放下了木柴.</p>\n<p>&quot;蓝斗篷画完之后我明白了它的意思, 它好像要往我曾经来过的路上走, 我也没有阻拦它.&quot;</p>\n<p>&quot;因为我知道, 它以为它正在向圈外走, 就和我当时一样, 也许是以为圈外有什么好东西.&quot;</p>\n<p>我说: &quot;那你觉得你有找到什么 '好东西' 吗?&quot;</p>\n<p>它说: &quot;没什么好东西, 但比起最里面令人窒息的小圈, 这里确实呼吸以及行动上更加自由, 不过...&quot;</p>\n<p>&quot;不过?&quot; 我接他的话道.</p>\n<p>&quot;圈里来的人总是以为这里就是真正的圈外, 喜欢在这些相对自由的地方胡作非为, 大声喧哗. 不过最后它们留下的痕迹总是会被自然而然地抹除, 也无所谓了.&quot; 它的语气变得轻松了起来.</p>\n<p>我问: &quot;你觉得这些 '圈', 或者这些墙是从什么地方来的? 我从里面出来的时候好像没有遇到很多的这些 '圈' ?&quot;</p>\n<p>它说: &quot;我不知道, 我也和后来的人谈过几次这类话题, 他们的回答和你一样, 他们从里面 '出来' 的时候遇到的墙确实和我曾经遇到的数量或者规模上都有很大差别.&quot;</p>\n<p>最后我和它又随便聊了一些有关墙边逐渐侵入的黑藤的事情, 不过由于它很久没有出过这个平原, 所以连黑藤是什么它都不清楚, 它也表示出一种漠不关心的态度, 所以我也没有继续问下去. 离开的时候, 它叮嘱我注意一下平原上的人, 因为它察觉到最近平原上的人越来越稀少了, 如果有什么发现, 希望能够和它分享一下.</p>\n<hr>\n<p>我把这类驻守在「蓝色平原」或者墙上任何地方上的人成为 &quot;守墙人&quot;, 他们虽然身处大多数的圈之外, 但却一般不愿意接触新的事物, 极端的守墙人甚至不愿意和斗篷乃至其他守墙人交流, 还好我遇到的是一个尚且能和其他人交流的斗篷, 不然这个平原上的很多事情我也无从得知.</p>\n<p>之于这个 &quot;圈&quot; 的问题, 在「棋盘」中的时候我就曾经发现, 大部分的小圈中人都不关心圈外的事情, 甚至都不知道有圈存在, 更不用说去突破这圈了, 和这类永远不会走出圈的人打交道总是要先自己去适应它自己的圈, 否则他们也会和守墙人一样把你拒之 &quot;圈&quot; 外.</p>\n<p>但相对的越往外, 这些 &quot;圈&quot; 的意味逐渐变得模糊不清, 没有人知道是谁建起了它, 也从来没有人宣布过自己有关于它的事迹.</p>\n","content_text":"序 外面很危险, 于是有人在地上画了一圈并对我说: &quot;这个圈外很危险, 你不要随便出去, 我会帮你对付这些危险, 所以我很忙, 但我也会派人监督你.&quot;; 监督人来了, 他对我说: &quot;你知道要怎么做了吧？但为了防止你总是不小心碰到边界, 我会给你的粗心大意一些小小的惩罚, 好让你长记性, 毕竟我要监督的人可不止你一个. &quot;, 于是监督人在圈内画了一个更小的圈. 最后, 我发现睡觉的时候翻个身也总是不小心超出监督人画的圈, 于是我自己给自己画了一个圈, 好让我只能站在原地不得动弹就再也不能睡觉, 也就不会出现无意识地超出圈外了; 但我好像忘记了我本来是可以出去的, 但是由于缺少保护自己的经验, 我也渐渐不敢出去了. 因为相比之下, 监督人的惩罚显得比圈外的危险来得更具体更具有危险性, 我也更有经验去应付. 正文 这里的人常说, 如今这个地方变成这个样子完全是由于那座灯塔. 在那个我还未曾触及到的时代, 这里的格局并不是这个样子, 这里和外面的世界都是一样的一马平川无所遮拦. 没有如今形同棋盘一样的森严布局, 更没有那座灯塔. 但是不知道什么时候, 似乎是从巨墙开始, 有一部分人开始为这个地方建起了障碍. 起初只是一道篱笆, 一个土沟, 再后来不约而同地全都变成了深红色质地的石块, 这种材料从来没有听说过是从什么地方开采出来, 但就现在的状态来看, 这墙就好像是从地底自己长出来一般: 整齐划一, 密不透风, 坚不可摧. 不过现在也有人觉察到, 曾经光滑细密的红墙上出现了裂缝, 但这裂缝实质上与这墙并没有多大关系, 因为裂缝时而出现时而消失, 没有人知道红墙是如何做到这种程度的自愈的, 就好像它是活的一样. 再说起那个灯塔, 它的材质和红墙并不一样, 但据说两者建立起来的时间都是相同的. 灯塔没有密不透风的样子, 它的表面就像积木拼接一般有很明显的缝隙, 甚至有的地方还有空缺, 不过这些缺陷也和墙上的缝隙一样时而消失时而出现. 但就我认知之中看来, 人人都说这座灯塔是这里的每个人亲手筑起的, 却没有人知道这墙的成因, 可能是因为人人心里都明白但都心照不宣, 也可能是因为它本来就是自己从这块大地上 &quot;生长&quot; 出来的, 因为它的材质从今天看始终都不像是这个大陆上应该存在的东西. 那片生长在红墙上的蓝色平原, 听说最开始的时候和墙的颜色一样, 是红色的, 后来平原的住民们逐渐发现所有的植物开始变成了蓝色, 土壤也变成了深蓝色. 由此原来单调的墙上多出来一片蓝色, 也吸引了更多的斗篷们过来在平原上定居. 一位从红色时期就开始在这里居住的斗篷曾经和我说, 目前来说这里变成蓝色和红色的时候并没有什么差别, 但其他斗篷们却都不约而同的住进了这里, 问起这些新来者理由却一个二个都含糊其词甚至回答不上来到底是为什么来到这里, 听得最多的一个理由就是: &quot;因为大家都来这里了.&quot; 事实真是这样的话, 那这个平原上早就已经人满为患了, 有相当部分人踏足这里之后就离去了, 还有部分人暂住过一段时间后也悄然离去, 他们留下的痕迹会很快地消失, 绵密的蓝色植被会再次长满被斗篷践踏的区域, 平原变得就好像从来没有人来过的一样. 当我问起这位斗篷他自己来这里的理由的时候, 它也沉默了, 但没有很久, 它问我: &quot;你喜欢听故事吗?&quot;, 我说: &quot;是真相吗?&quot;, &quot;我也... 不知道.&quot; 他回答我. 我接着说: &quot;好吧, 虽然我不是来听故事的, 但如果是你愿意说给我听的故事, 我也愿闻其详.&quot; 斗篷没有办法呈现出背后人的表情, 所以我在这面具上也办法没找到什么破绽, 它拿起了篝火边的一根细木柴在灰烬边画了起来. 它画了一个圆圈: &quot;我们现在的位置应该是这里, 应该是.&quot; 它在圆圈边上加了一个小圆圈, 指着它说. &quot;但如果我再把里面的「棋盘」画出来.&quot; 它在大圆中画了几个小圆. &quot;如果我没猜错, 你我都是这棋盘里面的人, 你应该会明白.&quot; 它接着在大圆和几个小圆之间用另外一个内圆隔开了, 出现了一个同心圆套一堆小圆圈的图画. 最后它在同心圆里面套了更多的圆, 为了套下更多的圆还把几个小圆擦去了, 几个小圆最后被数个大圆套在里面, 整个画面就好像一个靶子一样. 它放下了木柴, 对我说 &quot;曾经我也是这样一个个小圆中的人, 总以为外面有什么好东西, 想要出去看看.&quot; &quot;后来, 我真的出去了, 这个小圆不再是束缚, 但我发现外面的世界有更多的圆圈.&quot; 它指着那些同心圆一路向外, 最后到了代表我们位置的那个大圆边缘上的小圆. &quot;最后我费尽千辛万苦, 终于从这些圆里出来了, 到了这里, 现在被叫做「蓝色平原」的地方.&quot; 它又拿起细木柴握在手心. &quot;还是红色的那个时候, 我心里就想, 这里也许就是这世界的最后一个圆了吧?&quot; &quot;说实话, 现在我们这个圆外面的景色从来就没变过, 我最后本来打算继续往外去亲眼看看那些五彩斑斓的风景的.&quot; 它沉默下来, 用木柴继续在最外围的圆上加了一个更大的圆, 不过这次画布的面积不太够用了, 只画了一部分. 它指着新画的那个圆对我说: &quot;有一天我在平原上看到一个蓝色的斗篷, 我一如既往地和它打着招呼, 但是它说着一口不是我们的语言, 它好像也听不懂我在说什么.&quot; &quot;蓝色的斗篷好像也明白了我们两人语言不通, 就在脚下开始用手指在雪地上画画.&quot; &quot;一个圈, 两个圈, 三个圈. 我已经记不清他当时画了多少个圈了, 最后同心圆的外围上也多出了一个代表着当时位置的小圆.&quot; &quot;看起来就和现在这幅画几乎一模一样, 我现在都不知道它当时到底是在往圈外走还是进到一个新的圈里. 「蓝色平原」这个小圈又在什么位置呢?&quot; 它又放下了木柴. &quot;蓝斗篷画完之后我明白了它的意思, 它好像要往我曾经来过的路上走, 我也没有阻拦它.&quot; &quot;因为我知道, 它以为它正在向圈外走, 就和我当时一样, 也许是以为圈外有什么好东西.&quot; 我说: &quot;那你觉得你有找到什么 '好东西' 吗?&quot; 它说: &quot;没什么好东西, 但比起最里面令人窒息的小圈, 这里确实呼吸以及行动上更加自由, 不过...&quot; &quot;不过?&quot; 我接他的话道. &quot;圈里来的人总是以为这里就是真正的圈外, 喜欢在这些相对自由的地方胡作非为, 大声喧哗. 不过最后它们留下的痕迹总是会被自然而然地抹除, 也无所谓了.&quot; 它的语气变得轻松了起来. 我问: &quot;你觉得这些 '圈', 或者这些墙是从什么地方来的? 我从里面出来的时候好像没有遇到很多的这些 '圈' ?&quot; 它说: &quot;我不知道, 我也和后来的人谈过几次这类话题, 他们的回答和你一样, 他们从里面 '出来' 的时候遇到的墙确实和我曾经遇到的数量或者规模上都有很大差别.&quot; 最后我和它又随便聊了一些有关墙边逐渐侵入的黑藤的事情, 不过由于它很久没有出过这个平原, 所以连黑藤是什么它都不清楚, 它也表示出一种漠不关心的态度, 所以我也没有继续问下去. 离开的时候, 它叮嘱我注意一下平原上的人, 因为它察觉到最近平原上的人越来越稀少了, 如果有什么发现, 希望能够和它分享一下. 我把这类驻守在「蓝色平原」或者墙上任何地方上的人成为 &quot;守墙人&quot;, 他们虽然身处大多数的圈之外, 但却一般不愿意接触新的事物, 极端的守墙人甚至不愿意和斗篷乃至其他守墙人交流, 还好我遇到的是一个尚且能和其他人交流的斗篷, 不然这个平原上的很多事情我也无从得知. 之于这个 &quot;圈&quot; 的问题, 在「棋盘」中的时候我就曾经发现, 大部分的小圈中人都不关心圈外的事情, 甚至都不知道有圈存在, 更不用说去突破这圈了, 和这类永远不会走出圈的人打交道总是要先自己去适应它自己的圈, 否则他们也会和守墙人一样把你拒之 &quot;圈&quot; 外. 但相对的越往外, 这些 &quot;圈&quot; 的意味逐渐变得模糊不清, 没有人知道是谁建起了它, 也从来没有人宣布过自己有关于它的事迹.","summary":"序 外面很危险, 于是有人在地上画了一圈并对我说: &quot;这个圈外很危险, 你不要随便出去, 我会帮你对付这些危险, 所以我很忙, 但我也会派人监督你.&quot;; 监督人来了, 他对我说: &quot;你知道要怎么做了吧？但为了防止你总是不小心碰到边界, 我会给你的粗心大意一些小小的惩罚, 好让你长记性, 毕竟我要监督的人可不止你一个. &quot;, 于是监督人在圈内画了一个更小的圈. 最后, 我发现睡觉的时候翻个身也总是不小心超出监督人画的圈, 于是我自己给自己画了一个圈, 好让我只能站在原地不得动弹就再也不能睡觉, 也就不会出现无意识地超出圈外了; 但我好像忘记了我本来是可以出去的, 但是由于缺少保护自己的经验, 我也渐渐不敢出去了. 因为相比之下, 监督人的惩罚显得比圈外的危险来得更具体更具有危险性, 我也更有经验去应付. 正文 这里的人常说, 如今这个地方变成这个样子完全是由于那座灯塔. 在那个我还未曾触及到的时代, 这里的格局并不是这个样子, 这里和外面的世界都是一样的一马平川无所遮拦. 没有如今形同棋盘一样的森严布局, 更没有那座灯塔. 但是不知道什么时候, 似乎是从巨墙开始, 有一部分人开始为这个地方建起了障碍. 起初只是一道篱笆, 一个土沟, 再后来不约而同地全都变成了深红色质地的石块, 这种材料从来没有听说过是从什么地方开采出来, 但就现在的状态来看, 这墙就好像是从地底自己长出来一般: 整齐划一, 密不透风, 坚不可摧. 不过现在也有人觉察到, 曾经光滑细密的红墙上出现了裂缝, 但这裂缝实质上与这墙并没有多大关系, 因为裂缝时而出现时而消失, 没有人知道红墙是如何做到这种程度的自愈的, 就好像它是活的一样. 再说起那个灯塔, 它的材质和红墙并不一样, 但据说两者建立起来的时间都是相同的. 灯塔没有密不透风的样子, 它的表面就像积木拼接一般有很明显的缝隙, 甚至有的地方还有空缺, 不过这些缺陷也和墙上的缝隙一样时而消失时而出现. 但就我认知之中看来, 人人都说这座灯塔是这里的每个人亲手筑起的, 却没有人知道这墙的成因, 可能是因为人人心里都明白但都心照不宣, 也可能是因为它本来就是自己从这块大地上 &quot;生长&quot; 出来的, 因为它的材质从今天看始终都不像是这个大陆上应该存在的东西. 那片生长在红墙上的蓝色平原, 听说最开始的时候和墙的颜色一样, 是红色的, 后来平原的住民们逐渐发现所有的植物开始变成了蓝色, 土壤也变成了深蓝色. 由此原来单调的墙上多出来一片蓝色, 也吸引了更多的斗篷们过来在平原上定居. 一位从红色时期就开始在这里居住的斗篷曾经和我说, 目前来说这里变成蓝色和红色的时候并没有什么差别, 但其他斗篷们却都不约而同的住进了这里, 问起这些新来者理由却一个二个都含糊其词甚至回答不上来到底是为什么来到这里, 听得最多的一个理由就是: &quot;因为大家都来这里了.&quot; 事实真是这样的话, 那这个平原上早就已经人满为患了, 有相当部分人踏足这里之后就离去了, 还有部分人暂住过一段时间后也悄然离去, 他们留下的痕迹会很快地消失, 绵密的蓝色植被会再次长满被斗篷践踏的区域, 平原变得就好像从来没有人来过的一样. 当我问起这位斗篷他自己来这里的理由的时候, 它也沉默了, 但没有很久, 它问我: &quot;你喜欢听故事吗?&quot;, 我说: &quot;是真相吗?&quot;, &quot;我也... 不知道.&quot; 他回答我. 我接着说: &quot;好吧, 虽然我不是来听故事的, 但如果是你愿意说给我听的故事, 我也愿闻其详.&quot; 斗篷没有办法呈现出背后人的表情, 所以我在这面具上也办法没找到什么破绽, 它拿起了篝火边的一根细木柴在灰烬边画了起来. 它画了一个圆圈: &quot;我们现在的位置应该是这里, 应该是.&quot; 它在圆圈边上加了一个小圆圈, 指着它说. &quot;但如果我再把里面的「棋盘」画出来.&quot; 它在大圆中画了几个小圆. &quot;如果我没猜错, 你我都是这棋盘里面的人, 你应该会明白.&quot; 它接着在大圆和几个小圆之间用另外一个内圆隔开了, 出现了一个同心圆套一堆小圆圈的图画. 最后它在同心圆里面套了更多的圆, 为了套下更多的圆还把几个小圆擦去了, 几个小圆最后被数个大圆套在里面, 整个画面就好像一个靶子一样. 它放下了木柴, 对我说 &quot;曾经我也是这样一个个小圆中的人, 总以为外面有什么好东西, 想要出去看看.&quot; &quot;后来, 我真的出去了, 这个小圆不再是束缚, 但我发现外面的世界有更多的圆圈.&quot; 它指着那些同心圆一路向外, 最后到了代表我们位置的那个大圆边缘上的小圆. &quot;最后我费尽千辛万苦, 终于从这些圆里出来了, 到了这里, 现在被叫做「蓝色平原」的地方.&quot; 它又拿起细木柴握在手心. &quot;还是红色的那个时候, 我心里就想, 这里也许就是这世界的最后一个圆了吧?&quot; &quot;说实话, 现在我们这个圆外面的景色从来就没变过, 我最后本来打算继续往外去亲眼看看那些五彩斑斓的风景的.&quot; 它沉默下来, 用木柴继续在最外围的圆上加了一个更大的圆, 不过这次画布的面积不太够用了, 只画了一部分. 它指着新画的那个圆对我说: &quot;有一天我在平原上看到一个蓝色的斗篷, 我一如既往地和它打着招呼, 但是它说着一口不是我们的语言, 它好像也听不懂我在说什么.&quot; &quot;蓝色的斗篷好像也明白了我们两人语言不通, 就在脚下开始用手指在雪地上画画.&quot; &quot;一个圈, 两个圈, 三个圈. 我已经记不清他当时画了多少个圈了, 最后同心圆的外围上也多出了一个代表着当时位置的小圆.&quot; &quot;看起来就和现在这幅画几乎一模一样, 我现在都不知道它当时到底是在往圈外走还是进到一个新的圈里. 「蓝色平原」这个小圈又在什么位置呢?&quot; 它又放下了木柴. &quot;蓝斗篷画完之后我明白了它的意思, 它好像要往我曾经来过的路上走, 我也没有阻拦它.&quot; &quot;因为我知道, 它以为它正在向圈外走, 就和我当时一样, 也许是以为圈外有什么好东西.&quot; 我说: &quot;那你觉得你有找到什么 '好东西' 吗?&quot; 它说: &quot;没什么好东西, 但比起最里面令人窒息的小圈, 这里确实呼吸以及行动上更加自由, 不过...&quot; &quot;不过?&quot; 我接他的话道. &quot;圈里来的人总是以为这里就是真正的圈外, 喜欢在这些相对自由的地方胡作非为, 大声喧哗. 不过最后它们留下的痕迹总是会被自然而然地抹除, 也无所谓了.&quot; 它的语气变得轻松了起来. 我问: &quot;你觉得这些 '圈', 或者这些墙是从什么地方来的? 我从里面出来的时候好像没有遇到很多的这些 '圈' ?&quot; 它说: &quot;我不知道, 我也和后来的人谈过几次这类话题, 他们的回答和你一样, 他们从里面 '出来' 的时候遇到的墙确实和我曾经遇到的数量或者规模上都有很大差别.&quot; 最后我和它又随便聊了一些有关墙边逐渐侵入的黑藤的事情, 不过由于它很久没有出过这个平原, 所以连黑藤是什么它都不清楚, 它也表示出一种漠不关心的态度, 所以我也没有继续问下去. 离开的时候, 它叮嘱我注意一下平原上的人, 因为它察觉到最近平原上的人越来越稀少了, 如果有什么发现, 希望能够和它分享一下. 我把这类驻守在「蓝色平原」或者墙上任何地方上的人成为 &quot;守墙人&quot;, 他们虽然身处大多数的圈之外, 但却一般不愿意接触新的事物, 极端的守墙人甚至不愿意和斗篷乃至其他守墙人交流, 还好我遇到的是一个尚且能和其他人交流的斗篷, 不然这个平原上的很多事情我也无从得知. 之于这个 &quot;圈&quot; 的问题, 在「棋盘」中的时候我就曾经发现, 大部分的小圈中人都不关心圈外的事情, 甚至都不知道有圈存在, 更不用说去突破这圈了, 和这类永远不会走出圈的人打交道总是要先自己去适应它自己的圈, 否则他们也会和守墙人一样把你拒之 &quot;圈&quot; 外. 但相对的越往外, 这些 &quot;圈&quot; 的意味逐渐变得模糊不清, 没有人知道是谁建起了它, 也从来没有人宣布过自己有关于它的事迹.","date_published":"2023-07-16T10:52:12.000Z","tags":["密语瓶","密语瓶","绝界行"]},{"id":"https://blog.cxplay.org/works/microbin-204-review/","url":"https://blog.cxplay.org/works/microbin-204-review/","title":"MicroBin v2.0.4 更新: 权限控制仍然需要改进","content_html":"<h2 id=\"前言\"><a href=\"#前言\"></a>前言</h2>\n<p>很久之前就在找 Pastebin 的自托管替代品, 当时左找右找也只有找到一个 PrivateBin, 但是看到 PHP 我就开始有点害怕了, 试用过后感到这个程序反应也有些迟钝(但实际上这个程序的功能, 特别是权限控制上比较完善).</p>\n<p>最后找到的是 MicroBin, 数个月前发布的 1.x 版本使用过后喜欢上了它的轻量, 但可惜当时的 MicroBin 功能缺失, 权限管理也非常不成熟, 虽然通过 &quot;一些方法&quot; 可以做到基本上的权限控制让它勉强能用了, 但始终是无法适应大部分的使用场景.</p>\n<blockquote>\n<p>&quot;一些方法&quot;: 1.0 时期有很多改进功能的合并请求, 但是项目负责人就是不通过合并. 于是只好手动去 fork 了源代码合并了 129 号请求, 才得以避免任何人都能删除条目的问题, 做到了最基本的权限控制. (为什么会被设计成任何人都能删除条目啊? 啊?)</p>\n<blockquote>\n<p><a href=\"https://github.com/szabodanika/microbin/pull/129\">added option for configuring pasta removal by mrcodekiddie · Pull Request #129 · szabodanika/microbin</a></p>\n</blockquote>\n</blockquote>\n<p>勉强运行一段, 不, 一大段时间后, 最近几天他终于开始发 2.0 beta 了. 一连被好几个 beta 版本轰炸邮箱之后, 好在是前天终于憋出来 2.0 正式版 v2.0.4 了.</p>\n<p>高兴之余那就是赶紧试试新版的 MicroBin 到底怎么样, 当然结论就如标题所述: 权限控制依旧是最大的问题.</p>\n<h2 id=\"更新了什么\"><a href=\"#更新了什么\"></a>更新了什么</h2>\n<blockquote>\n<p>官方实例: <a href=\"https://pub.microbin.eu/\">https://pub.microbin.eu/</a></p>\n</blockquote>\n<h3 id=\"ui-改进\"><a href=\"#ui-改进\"></a>UI 改进</h3>\n<p>这应该是最直观的感受了, MicroBin 有了全新设计的 Logo... 额, 好像也只有这点了...🙂</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/microbin-204-review/IMG_20230713-194046784.png\" alt=\"v2.0.4 版本的 UI\" loading='lazy'></p>\n<p>实际上 UI 和 1.0 版本时期几乎一摸一样, 下面是我 1.0 时期部署后自定义选项的版本:</p>\n<blockquote>\n<p>选择去掉了页眉和 Logo, 所以实际上原来的 1.0 和现在 2.0 实际上一模一样, 当然除了新画的 Logo.</p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/microbin-204-review/IMG_20230713-194259891.png\" alt=\"v1.0 版本的 UI\" loading='lazy'></p>\n<h3 id=\"管理员后台\"><a href=\"#管理员后台\"></a>管理员后台</h3>\n<p>2.0 版本的更新加入了一个路径在 <code>/admin</code> 中的管理员后台, 登录后可以看到一些全局信息, 比如:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/microbin-204-review/IMG_20230713-194808171.png\" alt=\"程序状态, 相关链接, 条目数量.\" loading='lazy'></p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/microbin-204-review/IMG_20230713-194843494.png\" alt=\"环境变量\" loading='lazy'></p>\n<blockquote>\n<p>有读者可能好奇: &quot;啊? 为什么你的 MicroBin 是中文的界面语言啊?&quot;</p>\n<blockquote>\n<p>是因为 CX 我自己在 v2.0.4 版本更新后部署前自己改源代码汉化成了中文... 肯定不是我英语水平的问题, 不管怎么样看着自己母语的界面语言就是比较好一点(虽然用习惯了都一样).</p>\n</blockquote>\n</blockquote>\n<h3 id=\"双端加密\"><a href=\"#双端加密\"></a>双端加密</h3>\n<p>2.0 加入了客户端和服务端加密, 于是对应的选择条目的隐私选项的时候就多了几个选项:</p>\n<p><img src=\"https://i.cxplay.org/u/1/blog/microbin-204-review/IMG_20230713-195507895.png\" alt=\"v2.0.4 版本的条目隐私选项\" loading='lazy'></p>\n<p>使用到加密的选项是 Private 和 Secret 模式, 前者是仅服务端加密, 后者是端对端加密. 端对端加密应该不用再赘述, MicroBin 在浏览器端通过 JavaScript 进行一次客户端加密条目内容后再传输至服务器, 服务器再经过一次加密后最后存进数据库里.</p>\n<blockquote>\n<p>据称附件也会经过加密, 没试过, 谁知道呢.</p>\n</blockquote>\n<h3 id=\"链接重定向\"><a href=\"#链接重定向\"></a>链接重定向</h3>\n<p>简单来说就是短链接的功能, 用 302 跳转实现. 具体操作参照指南中的描述:</p>\n<blockquote>\n<p>Did you know that MicroBin can act as a URL shortener?</p>\n<p>Simply paste the url into the Content field and save it.</p>\n<p>You will be redirected to your upload's page, where you can see &gt; the destination URL and will see a &quot;Copy Redirect&quot; button. Press &gt; that to copy MicroBin's redirection link, which will look &gt; something like this:</p>\n<p><a href=\"https://pub.microbin.eu/url/bear-mouse-turkey\">https://pub.microbin.eu/url/bear-mouse-turkey</a></p>\n<p>Opening this link will simply redirect you to your original URL.</p>\n</blockquote>\n<p>简单来说就是:</p>\n<blockquote>\n<p>在粘贴框内输入一个链接后保存条目, 即可通过左上角 &quot;复制重定向&quot; 按钮获得一个 302 重定向到条目内容链接的链接.</p>\n</blockquote>\n<p>这个功能实际上有点背离粘贴板用途, 但就论新功能而言, 也值得单独拿出来说一下.</p>\n<h3 id=\"强化权限控制\"><a href=\"#强化权限控制\"></a>强化权限控制</h3>\n<p>权限控制是我一直以来很在意的问题, 1.0 版本存在的最严重的问题就是缺失的权限控制, 现在 2.0 版本终于得到了一些改善.</p>\n<p>除了 1.0 版本就存在, 现在也没有什么变化的 Public 和 Unlisted 模式, 新版增加了 Read-only 模式. 在这个模式下需要为条目设置密码, 对条目查看的时候不需要验证密码, 但是对条目进行修改和删除的时候就需要验证密码了. 这一点做得很不错, 基本上满足了我对基本权限控制的要求, 但是给访客放下这种权限的时候, 却没有给网站管理员放下更高的用于压制这种权限的权限.</p>\n<p>正所谓 &quot;魔高一尺道高一丈&quot;, 当 &quot;魔&quot; 真的有一尺的时候, 你作为网站管理员的 &quot;道&quot; 最好能真的有给我 &quot;高一丈&quot; 的能力, 可惜这个 2.0 版本没有考虑到这一点: <strong>当条目创建者使用 Read-only 模式创建条目后, 其使用的密码是会在服务数据库中加密存储的, 因此管理员没办法知道访客创建的条目密码是什么, 所以作为管理员即使能够登录后台, 能够看到没有分享过的条目标识, 但管理员要删除这个条目的时候却依旧要使用访客设置的密码才能删除.</strong></p>\n<blockquote>\n<p>可能会有读者怀疑我的动机有问题: &quot;啊? 你为什么要随便看还想随便删别人的东西?&quot;</p>\n<blockquote>\n<p>对于这种问题, 我只能说: &quot;作为网站管理员我有权限也有义务治理服务滥用, 如果不喜欢我这个网站管理员的做派或者是想体验一下作为清道夫的感觉, 那建议换一个网站或者自己去托管一个网站试试.&quot;</p>\n</blockquote>\n</blockquote>\n<p>这个问题依旧是权限控制上的问题, 我也在项目上提了 issue, 希望在某个版本能够被改进一下, 不然这管理员光有能看的眼睛可不太行, 还的要有能干活的手.</p>\n<blockquote>\n<p><a href=\"https://github.com/szabodanika/microbin/issues/187\">Use the administrator password to delete and edit parts of pastas. · Issue #187 · szabodanika/microbin</a></p>\n</blockquote>\n<p>就以我个人托管的实例来说, 如果没办法治理滥用, 那在考虑维护数据持久化之后限制上传是有必要的, 毕竟即使是有效期最长不过 1 周, 垃圾回收时间也只有 90 天的实例, 切换到 SQLite 数据库之后我也不想要通过操纵数据库才能删垃圾内容, 即使是从来没有发生过, 但这的确是会发生且难以解决的事情.</p>\n<h3 id=\"其他质量更新\"><a href=\"#其他质量更新\"></a>其他质量更新</h3>\n<ul>\n<li>支持且默认启用 SQLite 数据库.</li>\n<li>客户端实现的语法高亮.</li>\n<li>多媒体附件嵌入式预览.</li>\n<li>可限制附件上传尺寸.</li>\n<li>新增了指南(Guide)页面.</li>\n</ul>\n<h2 id=\"后话\"><a href=\"#后话\"></a>后话</h2>\n<p>v2.0.4 作为 MicroBin 2.0 的第一个正式版还是能看出这个项目有在稳步前进的, 并且项目的官网也放上了 MicroBin 商业版的 waitlist:</p>\n<blockquote>\n<p><a href=\"https://microbin.eu/waitlist/\">MicroBin Waitlist Signup</a></p>\n</blockquote>\n<p><img src=\"https://i.cxplay.org/u/1/blog/microbin-204-review/IMG_20230713-203718851.png\" alt=\"MicroBin for Business\" loading='lazy'><br>\n<img src=\"https://i.cxplay.org/u/1/blog/microbin-204-review/IMG_20230713-203804965.png\" alt=\"MicroBin for Business\nWaitlist Signup\" loading='lazy'></p>\n<p>这是好的, 说明项目负责人有计划把这个项目发展到足以应付商业需求.</p>\n<p>但对于现阶段的 MicroBin 来说, 目前的功能连我这种业余用户的需求都难以满足, 那么对于那些对信息安全和信息治理及预防有更高要求的商业客户, 估计是现在这个完善程度的 MicroBin 难以满足的.</p>\n<p>不过 MicroBin 已经可以看到在前进了, 期待它会有更好的表现.😀</p>\n<h2 id=\"相关链接\"><a href=\"#相关链接\"></a>相关链接</h2>\n<ul>\n<li>MicroBin 源代码仓库: <a href=\"https://github.com/szabodanika/microbin\">szabodanika/microbin: A tiny, self-contained, configurable paste bin and URL shortener written in Rust.</a> (GitHub)</li>\n<li>MicroBin 项目官方网站: <a href=\"https://microbin.eu/\">MicroBin</a></li>\n<li>MicroBin 官方托管实例: <a href=\"https://pub.microbin.eu/\">https://pub.microbin.eu/</a></li>\n</ul>\n<hr>\n","content_text":"前言 很久之前就在找 Pastebin 的自托管替代品, 当时左找右找也只有找到一个 PrivateBin, 但是看到 PHP 我就开始有点害怕了, 试用过后感到这个程序反应也有些迟钝(但实际上这个程序的功能, 特别是权限控制上比较完善). 最后找到的是 MicroBin, 数个月前发布的 1.x 版本使用过后喜欢上了它的轻量, 但可惜当时的 MicroBin 功能缺失, 权限管理也非常不成熟, 虽然通过 &quot;一些方法&quot; 可以做到基本上的权限控制让它勉强能用了, 但始终是无法适应大部分的使用场景. &quot;一些方法&quot;: 1.0 时期有很多改进功能的合并请求, 但是项目负责人就是不通过合并. 于是只好手动去 fork 了源代码合并了 129 号请求, 才得以避免任何人都能删除条目的问题, 做到了最基本的权限控制. (为什么会被设计成任何人都能删除条目啊? 啊?) added option for configuring pasta removal by mrcodekiddie · Pull Request #129 · szabodanika/microbin 勉强运行一段, 不, 一大段时间后, 最近几天他终于开始发 2.0 beta 了. 一连被好几个 beta 版本轰炸邮箱之后, 好在是前天终于憋出来 2.0 正式版 v2.0.4 了. 高兴之余那就是赶紧试试新版的 MicroBin 到底怎么样, 当然结论就如标题所述: 权限控制依旧是最大的问题. 更新了什么 官方实例: https://pub.microbin.eu/ UI 改进 这应该是最直观的感受了, MicroBin 有了全新设计的 Logo... 额, 好像也只有这点了...🙂 实际上 UI 和 1.0 版本时期几乎一摸一样, 下面是我 1.0 时期部署后自定义选项的版本: 选择去掉了页眉和 Logo, 所以实际上原来的 1.0 和现在 2.0 实际上一模一样, 当然除了新画的 Logo. 管理员后台 2.0 版本的更新加入了一个路径在 /admin 中的管理员后台, 登录后可以看到一些全局信息, 比如: 有读者可能好奇: &quot;啊? 为什么你的 MicroBin 是中文的界面语言啊?&quot; 是因为 CX 我自己在 v2.0.4 版本更新后部署前自己改源代码汉化成了中文... 肯定不是我英语水平的问题, 不管怎么样看着自己母语的界面语言就是比较好一点(虽然用习惯了都一样). 双端加密 2.0 加入了客户端和服务端加密, 于是对应的选择条目的隐私选项的时候就多了几个选项: 使用到加密的选项是 Private 和 Secret 模式, 前者是仅服务端加密, 后者是端对端加密. 端对端加密应该不用再赘述, MicroBin 在浏览器端通过 JavaScript 进行一次客户端加密条目内容后再传输至服务器, 服务器再经过一次加密后最后存进数据库里. 据称附件也会经过加密, 没试过, 谁知道呢. 链接重定向 简单来说就是短链接的功能, 用 302 跳转实现. 具体操作参照指南中的描述: Did you know that MicroBin can act as a URL shortener? Simply paste the url into the Content field and save it. You will be redirected to your upload's page, where you can see &gt; the destination URL and will see a &quot;Copy Redirect&quot; button. Press &gt; that to copy MicroBin's redirection link, which will look &gt; something like this: https://pub.microbin.eu/url/bear-mouse-turkey Opening this link will simply redirect you to your original URL. 简单来说就是: 在粘贴框内输入一个链接后保存条目, 即可通过左上角 &quot;复制重定向&quot; 按钮获得一个 302 重定向到条目内容链接的链接. 这个功能实际上有点背离粘贴板用途, 但就论新功能而言, 也值得单独拿出来说一下. 强化权限控制 权限控制是我一直以来很在意的问题, 1.0 版本存在的最严重的问题就是缺失的权限控制, 现在 2.0 版本终于得到了一些改善. 除了 1.0 版本就存在, 现在也没有什么变化的 Public 和 Unlisted 模式, 新版增加了 Read-only 模式. 在这个模式下需要为条目设置密码, 对条目查看的时候不需要验证密码, 但是对条目进行修改和删除的时候就需要验证密码了. 这一点做得很不错, 基本上满足了我对基本权限控制的要求, 但是给访客放下这种权限的时候, 却没有给网站管理员放下更高的用于压制这种权限的权限. 正所谓 &quot;魔高一尺道高一丈&quot;, 当 &quot;魔&quot; 真的有一尺的时候, 你作为网站管理员的 &quot;道&quot; 最好能真的有给我 &quot;高一丈&quot; 的能力, 可惜这个 2.0 版本没有考虑到这一点: 当条目创建者使用 Read-only 模式创建条目后, 其使用的密码是会在服务数据库中加密存储的, 因此管理员没办法知道访客创建的条目密码是什么, 所以作为管理员即使能够登录后台, 能够看到没有分享过的条目标识, 但管理员要删除这个条目的时候却依旧要使用访客设置的密码才能删除. 可能会有读者怀疑我的动机有问题: &quot;啊? 你为什么要随便看还想随便删别人的东西?&quot; 对于这种问题, 我只能说: &quot;作为网站管理员我有权限也有义务治理服务滥用, 如果不喜欢我这个网站管理员的做派或者是想体验一下作为清道夫的感觉, 那建议换一个网站或者自己去托管一个网站试试.&quot; 这个问题依旧是权限控制上的问题, 我也在项目上提了 issue, 希望在某个版本能够被改进一下, 不然这管理员光有能看的眼睛可不太行, 还的要有能干活的手. Use the administrator password to delete and edit parts of pastas. · Issue #187 · szabodanika/microbin 就以我个人托管的实例来说, 如果没办法治理滥用, 那在考虑维护数据持久化之后限制上传是有必要的, 毕竟即使是有效期最长不过 1 周, 垃圾回收时间也只有 90 天的实例, 切换到 SQLite 数据库之后我也不想要通过操纵数据库才能删垃圾内容, 即使是从来没有发生过, 但这的确是会发生且难以解决的事情. 其他质量更新 支持且默认启用 SQLite 数据库. 客户端实现的语法高亮. 多媒体附件嵌入式预览. 可限制附件上传尺寸. 新增了指南(Guide)页面. 后话 v2.0.4 作为 MicroBin 2.0 的第一个正式版还是能看出这个项目有在稳步前进的, 并且项目的官网也放上了 MicroBin 商业版的 waitlist: MicroBin Waitlist Signup 这是好的, 说明项目负责人有计划把这个项目发展到足以应付商业需求. 但对于现阶段的 MicroBin 来说, 目前的功能连我这种业余用户的需求都难以满足, 那么对于那些对信息安全和信息治理及预防有更高要求的商业客户, 估计是现在这个完善程度的 MicroBin 难以满足的. 不过 MicroBin 已经可以看到在前进了, 期待它会有更好的表现.😀 相关链接 MicroBin 源代码仓库: szabodanika/microbin: A tiny, self-contained, configurable paste bin and URL shortener written in Rust. (GitHub) MicroBin 项目官方网站: MicroBin MicroBin 官方托管实例: https://pub.microbin.eu/","summary":"前言 很久之前就在找 Pastebin 的自托管替代品, 当时左找右找也只有找到一个 PrivateBin, 但是看到 PHP 我就开始有点害怕了, 试用过后感到这个程序反应也有些迟钝(但实际上这个程序的功能, 特别是权限控制上比较完善). 最后找到的是 MicroBin, 数个月前发布的 1.x 版本使用过后喜欢上了它的轻量, 但可惜当时的 MicroBin 功能缺失, 权限管理也非常不成熟, 虽然通过 &quot;一些方法&quot; 可以做到基本上的权限控制让它勉强能用了, 但始终是无法适应大部分的使用场景. &quot;一些方法&quot;: 1.0 时期有很多改进功能的合并请求, 但是项目负责人就是不通过合并. 于是只好手动去 fork 了源代码合并了 129 号请求, 才得以避免任何人都能删除条目的问题, 做到了最基本的权限控制. (为什么会被设计成任何人都能删除条目啊? 啊?) added option for configuring pasta removal by mrcodekiddie · Pull Request #129 · szabodanika/microbin 勉强运行一段, 不, 一大段时间后, 最近几天他终于开始发 2.0 beta 了. 一连被好几个 beta 版本轰炸邮箱之后, 好在是前天终于憋出来 2.0 正式版 v2.0.4 了. 高兴之余那就是赶紧试试新版的 MicroBin 到底怎么样, 当然结论就如标题所述: 权限控制依旧是最大的问题. 更新了什么 官方实例: https://pub.microbin.eu/ UI 改进 这应该是最直观的感受了, MicroBin 有了全新设计的 Logo... 额, 好像也只有这点了...🙂 实际上 UI 和 1.0 版本时期几乎一摸一样, 下面是我 1.0 时期部署后自定义选项的版本: 选择去掉了页眉和 Logo, 所以实际上原来的 1.0 和现在 2.0 实际上一模一样, 当然除了新画的 Logo. 管理员后台 2.0 版本的更新加入了一个路径在 /admin 中的管理员后台, 登录后可以看到一些全局信息, 比如: 有读者可能好奇: &quot;啊? 为什么你的 MicroBin 是中文的界面语言啊?&quot; 是因为 CX 我自己在 v2.0.4 版本更新后部署前自己改源代码汉化成了中文... 肯定不是我英语水平的问题, 不管怎么样看着自己母语的界面语言就是比较好一点(虽然用习惯了都一样). 双端加密 2.0 加入了客户端和服务端加密, 于是对应的选择条目的隐私选项的时候就多了几个选项: 使用到加密的选项是 Private 和 Secret 模式, 前者是仅服务端加密, 后者是端对端加密. 端对端加密应该不用再赘述, MicroBin 在浏览器端通过 JavaScript 进行一次客户端加密条目内容后再传输至服务器, 服务器再经过一次加密后最后存进数据库里. 据称附件也会经过加密, 没试过, 谁知道呢. 链接重定向 简单来说就是短链接的功能, 用 302 跳转实现. 具体操作参照指南中的描述: Did you know that MicroBin can act as a URL shortener? Simply paste the url into the Content field and save it. You will be redirected to your upload's page, where you can see &gt; the destination URL and will see a &quot;Copy Redirect&quot; button. Press &gt; that to copy MicroBin's redirection link, which will look &gt; something like this: https://pub.microbin.eu/url/bear-mouse-turkey Opening this link will simply redirect you to your original URL. 简单来说就是: 在粘贴框内输入一个链接后保存条目, 即可通过左上角 &quot;复制重定向&quot; 按钮获得一个 302 重定向到条目内容链接的链接. 这个功能实际上有点背离粘贴板用途, 但就论新功能而言, 也值得单独拿出来说一下. 强化权限控制 权限控制是我一直以来很在意的问题, 1.0 版本存在的最严重的问题就是缺失的权限控制, 现在 2.0 版本终于得到了一些改善. 除了 1.0 版本就存在, 现在也没有什么变化的 Public 和 Unlisted 模式, 新版增加了 Read-only 模式. 在这个模式下需要为条目设置密码, 对条目查看的时候不需要验证密码, 但是对条目进行修改和删除的时候就需要验证密码了. 这一点做得很不错, 基本上满足了我对基本权限控制的要求, 但是给访客放下这种权限的时候, 却没有给网站管理员放下更高的用于压制这种权限的权限. 正所谓 &quot;魔高一尺道高一丈&quot;, 当 &quot;魔&quot; 真的有一尺的时候, 你作为网站管理员的 &quot;道&quot; 最好能真的有给我 &quot;高一丈&quot; 的能力, 可惜这个 2.0 版本没有考虑到这一点: 当条目创建者使用 Read-only 模式创建条目后, 其使用的密码是会在服务数据库中加密存储的, 因此管理员没办法知道访客创建的条目密码是什么, 所以作为管理员即使能够登录后台, 能够看到没有分享过的条目标识, 但管理员要删除这个条目的时候却依旧要使用访客设置的密码才能删除. 可能会有读者怀疑我的动机有问题: &quot;啊? 你为什么要随便看还想随便删别人的东西?&quot; 对于这种问题, 我只能说: &quot;作为网站管理员我有权限也有义务治理服务滥用, 如果不喜欢我这个网站管理员的做派或者是想体验一下作为清道夫的感觉, 那建议换一个网站或者自己去托管一个网站试试.&quot; 这个问题依旧是权限控制上的问题, 我也在项目上提了 issue, 希望在某个版本能够被改进一下, 不然这管理员光有能看的眼睛可不太行, 还的要有能干活的手. Use the administrator password to delete and edit parts of pastas. · Issue #187 · szabodanika/microbin 就以我个人托管的实例来说, 如果没办法治理滥用, 那在考虑维护数据持久化之后限制上传是有必要的, 毕竟即使是有效期最长不过 1 周, 垃圾回收时间也只有 90 天的实例, 切换到 SQLite 数据库之后我也不想要通过操纵数据库才能删垃圾内容, 即使是从来没有发生过, 但这的确是会发生且难以解决的事情. 其他质量更新 支持且默认启用 SQLite 数据库. 客户端实现的语法高亮. 多媒体附件嵌入式预览. 可限制附件上传尺寸. 新增了指南(Guide)页面. 后话 v2.0.4 作为 MicroBin 2.0 的第一个正式版还是能看出这个项目有在稳步前进的, 并且项目的官网也放上了 MicroBin 商业版的 waitlist: MicroBin Waitlist Signup 这是好的, 说明项目负责人有计划把这个项目发展到足以应付商业需求. 但对于现阶段的 MicroBin 来说, 目前的功能连我这种业余用户的需求都难以满足, 那么对于那些对信息安全和信息治理及预防有更高要求的商业客户, 估计是现在这个完善程度的 MicroBin 难以满足的. 不过 MicroBin 已经可以看到在前进了, 期待它会有更好的表现.😀 相关链接 MicroBin 源代码仓库: szabodanika/microbin: A tiny, self-contained, configurable paste bin and URL shortener written in Rust. (GitHub) MicroBin 项目官方网站: MicroBin MicroBin 官方托管实例: https://pub.microbin.eu/","date_published":"2023-07-13T11:05:43.000Z","tags":["博物志","软件","MicroBin","Pastebin","PrivateBin"]},{"id":"https://blog.cxplay.org/works/autodiscovery-in-email-system_autodiscover-autoconfiguration-and-rfc6186/","url":"https://blog.cxplay.org/works/autodiscovery-in-email-system_autodiscover-autoconfiguration-and-rfc6186/","title":"电子邮件系统的自动发现:  Autodiscover, Autoconfiguration 和 RFC 6186.","content_html":"<p>本文已合并至邮件人百科中, 后续更新请前往: <a href=\"https://www.mailer.su/wiki/configure/autodiscovery/\">https://www.mailer.su/wiki/configure/autodiscovery/</a></p>\n\n<h2 id=\"前言\"><a href=\"#前言\"></a>前言</h2>\n<p>电子邮件属于一种去中心化的通讯协议, 核心的电子邮件标准只有出入站协议的定义, 并没有定义一个电子邮件服务器如何为电子邮件客户端下发服务器配置, 所以在 Autodiscover 和 Autoconfiguration 之前, 大部分的电子邮件客户端都需要用户手动填写服务器传入和传出配置, 而获取这些配置往往需要用户自己到邮件服务器后台, Webmail 或者提供托管服务的服务商文档中搜寻, 这样的做法等于用户配置与服务器配置并不是自动同步, 在邮件服务器出现更改(迁移某部分的域名, 加密协议或端口)并不会及时通知客户端中跟随做出更改, 从而造成服务端改动后客户端的收发问题.</p>\n<p>除去邮件服务器和用户之间的不同步问题, 没有那么多经验用户自己在首次配置客户端的时候可能会对那些参数感到莫名其妙: 为什么我的 SMTP 服务器和 IMAP 服务器域名不是我的邮件地址域名? 为什么要使用 STARTTLS 而不是 TLS? 为什么我的密码不是我的 Webmail 密码?</p>\n<p>然后就是: 为什么我每次都要自己手动填这些乱七八糟的参数才能用? 为什么 Webmail 就可以一键登录? (🤦‍♂️)</p>\n<p>本文主要叙述基于 DNS 查询, HTTP 请求和静态配置文件实现的 Autodiscover 和 Autoconfiguration. 不包含基于本地的如: 环境变量, 注册表, 网络部署和用户目录等; 以及基于远程网络服务器的: 动态配置文件, 身份验证和多配置文件等.</p>\n\n<h2 id=\"区别与实现\"><a href=\"#区别与实现\"></a>区别与实现</h2>\n<p>所以, 为了解决以上的 &quot;电子邮件客户端自动配置邮件服务器参数&quot; 的问题, 引出了 &quot;Autodiscover&quot; 和 &quot;Autoconfiguration&quot;, 顾名思义 Autodiscover 即为 &quot;自动发现&quot;, Autoconfiguration 即为 &quot;自动配置&quot;. 两者大同小异, 区别在于前者 Autodiscover 主要指的是存在于 Microsoft 开发的电子邮件系统 Exchange 中使用到的另一个也是 Microsoft 开发的同步协议 Exchange ActiveSync  中附带的为电子邮件客户端下发邮件服务器配置的功能; 后者主要是由 Thunderbrid 定义的一种为了实现前者类似功能的规范.</p>\n<h3 id=\"autodiscover\"><a href=\"#autodiscover\"></a>Autodiscover</h3>\n<p>Autodiscover 目前存在于 Exchange Server 和 Exchange Online 中, 由于 Microsoft 逐渐推广禁止使用基本身份验证, 所以这部分功能可能未来只会在 Exchange Server 中见到了, 目前 Exchange Online 主要从 Active Directory 用户目录中获取配置, 之后才会尝试使用传统的 Autodiscover 去发现配置.</p>\n<p>发起 Autodiscover 的基本步骤:</p>\n<ul>\n<li>从 Exchange Activesync 中请求配置:\n<ol>\n<li>尝试从 URL 中请求配置: <code>https://example.com/Autodiscover/Autodiscover.xml</code></li>\n<li>尝试从 URL 中请求配置: <code>https://autodiscover.example.com/Autodiscover/Autodiscover.xml</code></li>\n<li>尝试从主机名 Autodiscover DNS 中寻找重定向 URL: <code>autodiscover.example.com</code> &gt;&gt; CNAME &gt;&gt; <code>autodiscover.exchangeserver.com</code> &gt;&gt; HTTP 301/302 &gt;&gt; <code>autodiscover.mailhoster.com</code>\n<ol>\n<li>尝试从重定向 URL 中请求配置: <code>https://autodiscover.mailhoster.com/Autodiscover/Autodiscover.xml</code>\n<ol>\n<li>尝试从主机名 Autodiscover DNS 中寻找重定向 URL: <code>autodiscover.mailhoster.com</code></li>\n</ol>\n<ul>\n<li>...</li>\n</ul>\n</li>\n</ol>\n</li>\n</ol>\n</li>\n</ul>\n<p>Autodiscover 就是不断从主机名和 URI 中寻找配置, 一层一层从上到下尝试寻找, 经过多层重定向之后最终找到文件名为 <code>Autodiscover.xml</code> 配置文件, 如果多次重定向之后还是没找到, 那客户端读取不到配置文件就还是需要手动配置了.</p>\n<h3 id=\"autoconfiguration\"><a href=\"#autoconfiguration\"></a>Autoconfiguration</h3>\n<p>Autoconfiguration 来自 Thunderbrid, 使用了 Autodiscover 类似的实现方法, 但步骤和规范上有些区别.</p>\n<p>发起 Autoconfiguration 的基本步骤:</p>\n<ol>\n<li>从 Thunderbrid 在本地磁盘上的安装目录中读取配置: <code>C:/Program Files/Mozilla/Thunderbird/isp/example.com.xml</code></li>\n<li>从电子邮件服务商获取配置:\n<ol>\n<li>尝试请求 <code>http://autoconfig.emailaddressdomain/mail/config-v1.1.xml?emailaddress=username@example.com</code> 或 <code>http://example.com/.well-known/autoconfig/mail/config-v1.1.xml</code> 文件路径.</li>\n<li>尝试从 DNS 记录中寻找配置:\n<ol>\n<li>尝试查询用户名域名 <code>example.com</code> 中存在内容为 <code>https://www.example.com/mozilla.xml</code> 的 TXT 记录依据记录进行请求配置文件<mark class=\"hl-label default\">未应用</mark>.</li>\n<li>另一种<em><strong>提议</strong></em>, 也是基于 DNS<mark class=\"hl-label default\">未应用</mark>:\n<ol>\n<li>尝试解析用户名域名中的 MX 记录, 若存在多个 MX 记录则取最高优先级的记录; 如果不存在 MX 记录, 则查询并使用用户名域名中符合 SMTP 协议(RFC 2821<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">[1]</a></sup>)的 A 记录.</li>\n<li>依据上方的查询结果, 从结果主机名中查询存在 <code>mailconf=https://...</code> 形式的 TXT 记录, 依据记录寻找配置文件, 如果 URL 错误则继续向下寻找.</li>\n<li>如果查询不到有效的 TXT 记录则选择第一条有效的 TXT 记录进行查找配置文件.\n<ol>\n<li>在对配置文件进行 GET 请求时中附带初始请求的用户邮件地址(<code>mailto:username@example.com</code>)为 referrer, 用于实现可能存在的动态配置文件下发.</li>\n<li>解析配置文件并让用户选择需要的协议和其他建议配置.</li>\n<li>继续进行登录验证.</li>\n</ol>\n</li>\n</ol>\n</li>\n<li>尝试解析用户名域名 DNS 从中寻找 SRV 记录获取配置(RFC 6186<sup class=\"footnote-ref\"><a href=\"#fn2\" id=\"fnref2\">[2]</a></sup>)<mark class=\"hl-label default\">未应用</mark>.</li>\n</ol>\n</li>\n</ol>\n</li>\n<li>尝试从 Mozilla 服务器检索配置:\n<ol>\n<li>从 <code>autoconfig.thunderbird.net</code><sup class=\"footnote-ref\"><a href=\"#fn3\" id=\"fnref3\">[3]</a></sup> 请求用户名域名中的服务商配置文件, 如: <code>https://autoconfig.thunderbird.net/v1.1/google.com</code>.</li>\n<li>如果找不到公共配置文件, 则尝试使用一些通配字符和常用端口进行猜测, 如 <code>imap.example.com</code>, <code>pop.example.com</code>, <code>pop3.example.com</code>, <code>smtp.example.com</code> 或 <code>mail.example.com</code>.</li>\n<li>继续失败, 则直接让用户进行手动配置.</li>\n</ol>\n</li>\n</ol>\n<p>以上标记有 <mark class=\"hl-label default\">未应用</mark> 的都代表存在这些<em><strong>提议</strong></em>, 但没有实际应用到 Thunderbird 邮件客户端和服务器系统中.</p>\n\n<h3 id=\"rfc-6186\"><a href=\"#rfc-6186\"></a>RFC 6186</h3>\n<blockquote>\n<p>请直接阅读下一节中有关 RFC 6186 的响应规范.</p>\n</blockquote>\n<h2 id=\"协议响应规范\"><a href=\"#协议响应规范\"></a>协议响应规范</h2>\n<p>从上面小节对两种自动寻找配置的方式可以看出, 它们都依赖于一个扩展名为 <code>.xml</code> 及使用 XML 标记语言的编写的二进制文件.</p>\n<p>两种实现方式虽然在过程上大同小异, 但它们要使用到的最终配置文件是存在差异的, 并不能通用. Microsoft 在 Exchange Server 多个版本也修改了对于这部分静态文件的定义和规范, 直到现在的 Exchange Online 加入身份验证之后基本上就无法用于静态文件的实现, 所以需要向前使用还能单纯依靠静态文件就能被客户端解析的规范.</p>\n<p>如果要一同在服务器实现, 那么需要分别针对两种方式编写对应的 XML 源文件.</p>\n<p>此外上面还稍微提到过, 还存在一个拟议中的规范 RFC 6186<sup class=\"footnote-ref\"><a href=\"#fn2\" id=\"fnref2:1\">[2:1]</a></sup>, 并不依赖于特定域名下的配置文件, 而是使用完全基于 DNS 的 SRV 记录查询实现配置发现.</p>\n<h3 id=\"autodiscover-2\"><a href=\"#autodiscover-2\"></a>Autodiscover</h3>\n<p>以下是 Microsoft 提供的适用 Outlook 2010 邮件客户端的服务端配置文件模板:</p>\n<blockquote>\n<p><a href=\"https://learn.microsoft.com/zh-cn/previous-versions/office/office-2010/cc511507%28v=office.14%29#xml-%E5%93%8D%E5%BA%94%E6%9E%B6%E6%9E%84\">计划自动在 Outlook 2010 中配置用户帐户 | Microsoft Learn</a></p>\n</blockquote>\n<p>以下配置文件注释经过本人翻译.</p>\n\n<pre class=\"language-xml\"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt;\n    &lt;!-- Response: 必须\n本标签用于指示该 XML 是一个 Autodiscover 响应.\n--&gt;\n    &lt;Response\n        xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt;\n        &lt;!-- User: 可选\n本标签用于提供用户信息, Autodiscover 必须使用 UTF-8 编码.\n--&gt;\n        &lt;User&gt;\n            &lt;!-- DisplayName: 可选\n服务端使用这个标签提供一个默认显示名称. 客户端可以选择接受或自定义, 这可以免除用户所需要的输入从而节省时间.\n--&gt;\n            &lt;DisplayName&gt;John Doe&lt;/DisplayName&gt;\n        &lt;/User&gt;\n\n        &lt;!-- Account: 必须\n本标签用于指定账户类型, 如电子邮件(Email), 新闻组(Newsgroup)和 SIP 服务器等.\n--&gt;\n        &lt;Account&gt;\n            &lt;!-- AccountType: 必须\n本标签中的值用于指定账户类型.\n可用值:\nemail: 表示该标签内用于定义电子邮件服务器的配置.\nnntp: 表示该标签内用于定义 NNTP 服务器的配置(Outlook 2007 没有使用).\n--&gt;\n            &lt;AccountType&gt;email | nntp&lt;/AccountType&gt;\n\n            &lt;!-- Action: 必须\n本标签中的值用于定义直接使用本文件的配置或重定向至另一个可提供 Autodiscover 配置的网络服务器.\n可用值:\nredirectUrl: 如果指定使用这个值, 那么在 RedirectUrl 标签中必须要包含要定向到的 Autodiscover 配置路径(http/https).\n            为了防止服务端的重定向将客户端置入请求循环中, 客户端应在 10 次重定向后停止继续重定向.\nredirectAddr: 如果指定使用这个值, 表示客户端应当使用 RedirectAddr 标签中的电子邮件地址进行 Autodiscover 配置查找, 而不是使用当前的用户电子邮箱地址.\nsettings: 如果指定使用了这个值, 表示本 XML 文件中包含进行账户配置所需要的信息. 客户端将使用 Protocol 标签下的服务器配置进行配置客户端.\n--&gt;\n            &lt;Action&gt;redirectUrl | redirectAddr | settings&lt;/Action&gt;\n\n            &lt;!-- RedirectUrl: 如果 Action 标签值指定为 &quot;redirectUrl&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.\n本标签的值应是一个 http 或 https 协议的 URL, 用于客户端使用该值进一步获取配置.\n--&gt;\n            &lt;RedirectUrl&gt;redirect.URL&lt;/RedirectUrl&gt;\n\n            &lt;!-- RedirectAddr: 如果 Action 标签值指定为 &quot;redirectAddr&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.\n本标签的值应是一个电子邮件地址, 用于客户端使用该地址重新查找 Autodiscover 配置.\n--&gt;\n            &lt;RedirectAddr&gt;email@address&lt;/RedirectAddr&gt;\n\n            &lt;!-- Image: 可选\n本标签的值是一张网络 JPG 图片, 用作配置电子邮件服务提供商的品牌标志(Outlook 2007 没有使用).\n--&gt;\n            &lt;Image&gt;http://path.to.image.com/image.jpg&lt;/Image&gt;\n\n            &lt;!-- ServiceHome: 可选\n本标签的值是一个指向电子邮件服务提供商主页的 URL, 客户端可以选择是否将这个值显示给用户(Outlook 2007 没有使用).\n--&gt;\n            &lt;ServiceHome&gt;http://web.page.com&lt;/ServiceHome&gt;\n\n            &lt;!-- Protocol: 如果 Action 标签值指定为 &quot;settings&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.\n本标签只能包含一个账户类型的配置信息, 多个 Protocol 标签可按照服务端的偏好进行配置, 但客户端可以自行选择偏好协议.\n--&gt;\n            &lt;Protocol&gt;\n                &lt;!-- Type: 必须.\n本标签的值指定使用何种账户类型.\n可选值:\nPOP3: 指定连接到服务器的协议使用 POP3. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.\nSMTP: 指定连接到服务器的协议使用 SMTP. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.\nIMAP: 指定连接到服务器的协议使用 IMAP. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.\nDAV: 指定连接到服务器的协议使用 DAV. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.\nWEB: 指定客户端使用 Server 标签中的 URL 从浏览器访问电子邮件. 仅适用于 AccountType 标签值为 &quot;email&quot; 时(Outlook 2007 没有使用).\nNNTP: 指定连接到服务器的协议使用 NNTP. 仅适用于 AccountType 标签值为 &quot;nntp&quot; 时(Outlook 2007 没有使用).\n--&gt;\n                &lt;Type&gt;POP3 | SMTP | IMAP | DAV | WEB | NNTP&lt;/Type&gt;\n\n                &lt;!-- ExpirationDate: 可选.\n本标签的值指定一个该配置文件最后有效的日期, 在该日期过后客户端应该自动重新查找 Autodiscover 配置. 如果未指定则默认为不过期.\n--&gt;\n                &lt;ExpirationDate&gt;YYYYMMDD&lt;/ExpirationDate&gt;\n\n                &lt;!-- TTL: 可选.\n本标签的值指定该配置文件的有效时间, 单位为小时. 自首次查找该配置文件起算, 经过本标签值的时间后客户端将再次查找 Autodiscover 配置. 如果本标签未指定值, 则默认使用 1 小时的\n                TTL.\n--&gt;\n                &lt;TTL&gt;168&lt;/TTL&gt;\n\n                &lt;!-- Server: 必须.\n本标签的值指定了与上方 Type 标签值中账户类型对应的服务器地址.\n对于 Type 标签值为 POP3, SMTP, IMAP 或 NNTP 的, 该值应是一个主机名或 IP 地址.\n对于 Type 标签值为 DAV 和 WEB 的, 该值应是一个 URL.\n--&gt;\n                &lt;Server&gt;mail.contoso.com&lt;/Server&gt;                &lt;!--服务器的\n                IP 地址或域名--&gt;\n\n                &lt;!-- Port: 可选.\n本标签的值指定对 Server 标签值使用的端口号. 如果未指定则根据 Type 标签值使用默认设置. 如果 Server 标签值是一个 URL, 则不使用本标签值.\n--&gt;\n                &lt;Port&gt;110&lt;/Port&gt;\n\n                &lt;!-- LoginName: 可选.\n本标签值指定用户的登录名. 如果未指定, 则默认使用用户设置的电子邮件地址 &quot;@&quot; 前的字串符. 如果本标签值指定为一个域名, 则使用 [用户名]@[域名] 的格式进行配置, 如\n                &quot;Alice@example.com&quot;.\n--&gt;\n                &lt;LoginName&gt;Alice&lt;/LoginName&gt;\n\n                &lt;!-- DomainRequired: 可选.  默认为 off.\n本标签指定身份验证过程中是否需要域名, 如果设置为 on, 则在身份验证过程中需要域名, off 则为不需要. 如果未在 LoginName 标签中指定一个域名或未指定 LoginName\n                标签的值, 那么需要用户手动输入域名才能进行身份验证.\n--&gt;\n                &lt;DomainRequired&gt;on | off&lt;/DomainRequired&gt;\n\n                &lt;!-- DomainName: 可选.\n本标签值指定用户域名. 如果未指定值, 则默认将用户电子邮件地址作为 UPN 的格式([用户名]@[域名])来使用. 如 Alic@example.com.\n--&gt;\n                &lt;DomainName&gt;&lt;/DomainName&gt;\n\n                &lt;!-- SPA: (Secure Password Authentication 安全密码验证) 可选.\n本标签值指定是否需要安全密码验证. 如果未指定, 则默认为 on.\n--&gt;\n                &lt;SPA&gt;on | off&lt;/SPA&gt;\n\n                &lt;!-- SSL: 可选.\n本标签值指定是否需要进行安全登录. 如果未指定, 则默认为 on.\n--&gt;\n                &lt;SSL&gt;on | off&lt;/SSL&gt;\n\n                &lt;!-- AuthRequired: 可选.\n本标签值指定是否需要身份验证(基于密码). 如果未指定, 则默认为 on.\n--&gt;\n                &lt;AuthRequired&gt;on | off&lt;/AuthRequired&gt;                &lt;!-- 可选: 是否需要身份验证. --&gt;\n\n                &lt;!-- UsePOPAuth: 可选.\n本标签值仅在 Type 标签值为 &quot;SMTP&quot; 时可用. 用于指定 POP3 类型的账户使用的身份验证信息是否也将用于 SMTP.\n--&gt;\n                &lt;UsePOPAuth&gt;on | off&lt;/UsePOPAuth&gt;\n\n                &lt;!-- SMTPLast: 可选.  默认为 off.\n本标签值指定在通过 SMTP 发件前是否需要先下载电子邮件. 通常 SMTP 下载邮件时都会检查身份验证是否成功, 所以一般为需要.\n--&gt;\n                &lt;SMTPLast&gt;on | off&lt;/SMTPLast&gt;\n            &lt;/Protocol&gt;\n        &lt;/Account&gt;\n    &lt;/Response&gt;\n&lt;/Autodiscover&gt;\n</code></pre>\n<p>使用这个规范编写的一个典型的 IMAP &amp; POP3 &amp; SMTP 服务器配置应该像这样:</p>\n<pre class=\"language-xml\"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;\n&lt;Autodiscover xmlns=&quot;http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt;\n    &lt;Response xmlns=&quot;http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt;\n        &lt;Account&gt;\n            &lt;AccountType&gt;email&lt;/AccountType&gt;\n            &lt;Action&gt;settings&lt;/Action&gt;\n            &lt;Protocol&gt;\n                &lt;Type&gt;IMAP&lt;/Type&gt;\n                &lt;Server&gt;imap.example.com&lt;/Server&gt;\n                &lt;Port&gt;993&lt;/Port&gt;\n                &lt;DomainRequired&gt;off&lt;/DomainRequired&gt;\n                &lt;LoginName&gt;&lt;/LoginName&gt;\n                &lt;SPA&gt;off&lt;/SPA&gt;\n                &lt;SSL&gt;on&lt;/SSL&gt;\n                &lt;AuthRequired&gt;on&lt;/AuthRequired&gt;\n            &lt;/Protocol&gt;\n            &lt;Protocol&gt;\n                &lt;Type&gt;POP3&lt;/Type&gt;\n                &lt;Server&gt;pop3.example.com&lt;/Server&gt;\n                &lt;Port&gt;995&lt;/Port&gt;\n                &lt;DomainRequired&gt;off&lt;/DomainRequired&gt;\n                &lt;LoginName&gt;&lt;/LoginName&gt;\n                &lt;SPA&gt;off&lt;/SPA&gt;\n                &lt;SSL&gt;on&lt;/SSL&gt;\n                &lt;AuthRequired&gt;on&lt;/AuthRequired&gt;\n            &lt;/Protocol&gt;\n            &lt;Protocol&gt;\n                &lt;Type&gt;SMTP&lt;/Type&gt;\n                &lt;Server&gt;smtp.example.com&lt;/Server&gt;\n                &lt;Port&gt;465&lt;/Port&gt;\n                &lt;DomainRequired&gt;off&lt;/DomainRequired&gt;\n                &lt;LoginName&gt;&lt;/LoginName&gt;\n                &lt;SPA&gt;off&lt;/SPA&gt;\n                &lt;Encryption&gt;SSL&lt;/Encryption&gt;\n                &lt;AuthRequired&gt;on&lt;/AuthRequired&gt;\n                &lt;UsePOPAuth&gt;off&lt;/UsePOPAuth&gt;\n                &lt;SMTPLast&gt;off&lt;/SMTPLast&gt;\n            &lt;/Protocol&gt;\n        &lt;/Account&gt;\n    &lt;/Response&gt;\n&lt;/Autodiscover&gt;\n</code></pre>\n<p>由于是使用标准的电子邮件协议, 而 Microsoft 提供的给 Exchange 服务器中的协议模板中有部分配置项目并不是必须的, 比如 UPN 就只是 Microsoft 专门用来指定一个账户个体的简称, 实际上和电子邮箱地址并没有区别, 两者几乎是一模一样的定义.</p>\n<h3 id=\"autoconfiguration-2\"><a href=\"#autoconfiguration-2\"></a>Autoconfiguration</h3>\n<p>从 MozillaWiki 中可以得到配置格式:</p>\n<blockquote>\n<p><a href=\"https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat\">Thunderbird:Autoconfiguration:ConfigFileFormat - MozillaWiki</a></p>\n</blockquote>\n<p>以下配置文件注释经过本人翻译.</p>\n\n<pre class=\"language-xml\"><code>&lt;?xml version=&quot;1.0&quot;?&gt;\n&lt;clientConfig version=&quot;1.1&quot;&gt;\n    &lt;!-- 电子邮件服务提供商 --&gt;\n    &lt;emailProvider id=&quot;example.com&quot;&gt;\n        &lt;!-- domain: 域名 --&gt;\n        &lt;domain&gt;example.com&lt;/domain&gt;\n        &lt;domain&gt;example.net&lt;/domain&gt;\n        &lt;!-- displayName: 显示在客户端上的名称. --&gt;\n        &lt;displayName&gt;Google Mail&lt;/displayName&gt;\n        &lt;!-- displayShortName: 显示的简称. --&gt;\n        &lt;displayShortName&gt;GMail&lt;/displayShortName&gt;\n\n        &lt;!-- incomingServer type: 传入服务器类型\n                &quot;imap&quot;: IMAP\n                &quot;pop3&quot;: POP3\n           --&gt;\n        &lt;incomingServer type=&quot;pop3&quot;&gt;\n            &lt;!-- hostname: 选定类型所使用的服务器主机名. --&gt;\n            &lt;hostname&gt;\n                pop.example.com&lt;/hostname&gt;\n            &lt;!-- port: 选定类型所使用的服务器端口号. --&gt;\n            &lt;port&gt;995&lt;/port&gt;\n            &lt;!-- socketType: 连接类型\n                    &quot;plain&quot;: 不加密.\n                    &quot;SSL&quot;: SSL 加密.\n                    &quot;STARTTLS&quot;: 使用 STARTTLS 进行机会性加密\n                --&gt;\n            &lt;socketType&gt;SSL&lt;/socketType&gt;\n            &lt;!-- username: 指定用户名. --&gt;\n            &lt;username&gt;%EMAILLOCALPART%&lt;/username&gt;\n            &lt;!-- authentication: 身份验证类型\n                    &quot;password-cleartext&quot;:\n                       直接发送明文密码进行验证. 如果没有加密连接协议还要使用明文密码则非常不安全.\n                    &quot;password-encrypted&quot;:\n                       对于密码进行加密, 可以是 CRAM-MD5, DIGEST-MD5. 不包含 NTLM.\n                    &quot;NTLM&quot;:\n                       使用 NTLM 或 HTLMv2 及后续版本. Windows 所使用的登录方法.\n                    &quot;GSSAPI&quot;:\n                       使用 Kerberos 或 GSSAPI, 一种适用于大规模验证需要的单点登录方式.\n                    &quot;client-IP-address&quot;:\n                       根据 IP 地址识别用户, 无需用户名和密码, 也不需要进行身份验证.\n                    &quot;TLS-client-cert&quot;:\n                       使用客户端证书进行认证, 要求客户端提供 SSL 证书(如果需要, 可让用户自行选择证书后发送). Thunderbird 尚未支持.\n                    &quot;OAuth2&quot;:\n                        使用 OAuth2, 仅在部分硬编码服务器上可用. 只能作为备用方法使用.\n                        由于 OAuth2 规范的缺陷, 客户端一般需要发送客户端的凭证密钥到服务商,\n                        这样做往往需要客户端要先在服务商注册客户端并获得服务商的同意.\n                        这就不仅导致电子邮件服务商可以禁止用户使用任意电子邮件客户端, 还导致并不能支持所有的 OAuth2 服务器(这与开源的理念相悖).\n                        最后 Thunderbird 只能将支持 OAuth2 的服务器和密钥硬编码进客户端里.\n                        这也意味着并不能使用任意的 OAuth2 服务器. 只有在客户端中已经被定义好服务器才能使用.\n                        在Thunderbird 中受支持的 OAuth2 服务器参见源代码:\n            &quot;https://searchfox.org/comm-central/source/mailnews/base/src/OAuth2Providers.jsm&quot;\n                    &quot;none&quot;:\n                        不进行验证.\n                 --&gt;\n            &lt;authentication&gt;\n                password-cleartext&lt;/authentication&gt;\n            &lt;pop3&gt;\n                &lt;!-- 移除以下内容则让客户端或用户自行选择以下参数配置. --&gt;\n                &lt;!-- leaveMessagesOnServer: 是否将邮件保存在服务器上(true/false). --&gt;\n                &lt;leaveMessagesOnServer&gt;true&lt;/leaveMessagesOnServer&gt;\n                &lt;!-- downloadOnBiff: 下载时发送 Biff 通知(?)(true/false).  --&gt;\n                &lt;downloadOnBiff&gt;true&lt;/downloadOnBiff&gt;\n                &lt;!-- daysToLeaveMessagesOnServer: 服务器中邮件的保留天数. --&gt;\n                &lt;daysToLeaveMessagesOnServer&gt;\n                    14&lt;/daysToLeaveMessagesOnServer&gt;\n                &lt;!-- checkInterval: 从服务器检查的间隔(分钟数), 仅适用于不允许进行频繁请求检查的服务器. Thunderbird 暂不支持. --&gt;\n                &lt;checkInterval minutes=&quot;15&quot; /&gt;\n            &lt;/pop3&gt;\n            &lt;!-- password: 值可选, 用户的密码. --&gt;\n            &lt;password&gt;&lt;/password&gt;\n        &lt;/incomingServer&gt;\n\n        &lt;!-- outgoingServer type: 传出服务器类型 --&gt;\n        &lt;outgoingServer type=&quot;smtp&quot;&gt;\n            &lt;!-- hostname: 选定类型所使用的服务器主机名. --&gt;\n            &lt;hostname&gt;\n                smtp.googlemail.com&lt;/hostname&gt;\n            &lt;!-- port: 选定类型所使用的服务器端口号. --&gt;\n            &lt;port&gt;587&lt;/port&gt;\n            &lt;!-- socketType: 连接类型\n                    &quot;plain&quot;: 不加密.\n                    &quot;SSL&quot;: SSL 加密.\n                    &quot;STARTTLS&quot;: 使用 STARTTLS 进行机会性加密\n                --&gt;\n            &lt;socketType&gt;STARTTLS&lt;/socketType&gt;\n            &lt;!-- username: 指定用户名. --&gt;\n            &lt;username&gt;%EMAILLOCALPART%&lt;/username&gt;\n            &lt;!-- authentication: 身份验证类型\n                符合 RFC 2554, RFC 4954 的或其他的身份验证方法, 可用选项参考 incomingServer 标签中的 authentication;\n                除此之外还可用使用以下的方法:\n                (关于 Thunderbird 对此的兼容性: Thunderbird 3.0 只支持 &quot;plain&quot;, &quot;secure&quot;, &quot;none&quot; 和 &quot;smtp-after-pop&quot;.)\n                 &quot;SMTP-after-POP&quot;:\n                    在对 SMTP 进行验证前先对传入服务器进行验证.\n            --&gt;\n            &lt;authentication&gt;\n                password-cleartext&lt;/authentication&gt;\n            &lt;!-- restriction: 限定服务器在客户端上进行连接的条件(如果服务器在 authentication 之外还要求其他的验证选项).\n                    &quot;client-IP-address&quot;: 只有用户在特定的网络中才能连接到服务器且服务器才能工作. 如特定 ISP 网络或公司网络.\n                        注意: &lt;authentication&gt;client-IP-address&lt;/&gt; 表示可以在没有任何身份验证的情况下连接并使用服务器.\n                        使用 &lt;authentication&gt;password-cleartext&lt;/&gt; 和 &lt;restriction&gt;client-IP-address&lt;/&gt; 时表示用户需要处于正确的 IP\n            网络环境中并且应该进行身份验证.\n                        建议不要使用实行这种验证措施的服务器, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=556267&quot;.\n                    尚未实现. 规范(标签元素?)可能会改动.\n            --&gt;\n            &lt;restriction&gt;client-IP-address&lt;/restriction&gt;\n            &lt;!-- 移除以下内容则让客户端或用户自行选择以下参数配置. --&gt;\n            &lt;!-- 是否选择添加此服务器(true/false). --&gt;\n            &lt;addThisServer&gt;true&lt;/addThisServer&gt;\n            &lt;!-- 是否是配置文件中的全局首选服务器(true/false) --&gt;\n            &lt;useGlobalPreferredServer&gt;\n                true&lt;/useGlobalPreferredServer&gt;\n            &lt;!-- password: 值可选, 用户的密码. --&gt;\n            &lt;password&gt;&lt;/password&gt;\n        &lt;/outgoingServer&gt;\n\n        &lt;!--\n            在连接到服务器之前需要用户手动进行配置的选项(比如, 部分邮件服务提供商情况默认关闭 POP3, IMAP 或 SMTP, 需要用户登录 Webamil 手动打开账户设置中的这些选项).\n            注意: 根据 XML 规范, 部分特殊符号需要进行转义, 如:\n                &quot;&amp;&quot; &gt;&gt; &quot;&amp;amp;&quot;\n                &quot;&lt;&quot; &gt;&gt; &quot;&amp;lt;&quot;\n                &quot;&gt;&quot; &gt;&gt; &quot;&amp;gt;&quot;\n                &quot;&#x27;&quot; &gt;&gt; &quot;&amp;apos;&quot;\n                &quot;&quot;&quot; &gt;&gt; &quot;&amp;quot;&quot;\n            尚未实现, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=586364&quot;.\n        --&gt;\n        &lt;!-- visiturl 值为显示给用户的 URL. --&gt;\n        &lt;enable visiturl=&quot;https://mail.google.com/mail/?ampshva=1#settings/fwdandpop&quot;&gt;\n            &lt;!-- instruction: 说明文字 --&gt;\n            &lt;instruction&gt;Check &#x27;Enable\n                IMAP and POP&#x27; in Google settings page&lt;/instruction&gt;\n            &lt;!-- instruction lang: 另一种语言的说明文字 --&gt;\n            &lt;instruction lang=&quot;de&quot;&gt;Schalten Sie &#x27;IMAP und POP\n                aktivieren&#x27; auf der Google\n                Einstellungs-Seite an&lt;/instruction&gt;\n        &lt;/enable&gt;\n\n        &lt;!--\n            一个电子邮件服务商用于描述配置文件的网页.\n            主要用于配置文件维护, 客户端并不使用这里的提供的信息.\n            并不一定需要完全使用服务商提供的配置, 比如服务商在配置文件中不使用 SSL, 但 SLL 实际可用, 此时就可以选择性配置 SLL.\n            descr 应该包含来自服务商对客户的(多语言)说明.\n        --&gt;\n        &lt;documentation url=&quot;http://www.example.com/help/mail/thunderbird&quot;&gt;\n            &lt;descr lang=&quot;en&quot;&gt;Configure Thunderbird 2.0 for\n                IMAP&lt;/descr&gt;\n            &lt;descr lang=&quot;de&quot;&gt;Thunderbird 2.0 mit IMAP konfigurieren&lt;/descr&gt;\n        &lt;/documentation&gt;\n\n    &lt;/emailProvider&gt;\n\n    &lt;!-- 用于同步用户通讯录或联系人方式的配置. 尚未实现. Thunderbird 使用 RFC 6764 实现这类功能的自动发现. --&gt;\n    &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt;\n    &lt;addressBook type=&quot;carddav&quot;&gt;\n        &lt;!-- 用户名 --&gt;\n        &lt;username&gt;\n            %EMAILADDRESS%&lt;/username&gt;\n        &lt;!-- authentication: 身份验证方式, 参见 incomingServer 标签配置.\n              &quot;http-basic&quot;:\n                    使用 HTTP 服务器进行 WWW-Authenticate: Basic 身份验证.\n              &quot;http-digest&quot;:\n                    使用 HTTP 服务器进行 WWW-Authenticate: Digest 身份验证.\n              &quot;OAuth2&quot;:\n                    OAuth2 身份验证. 使用和 Email 相同的口令.\n        --&gt;\n        &lt;authentication&gt;http-basic&lt;/authentication&gt;\n        &lt;!-- 指定服务器 URL. --&gt;\n        &lt;serverURL&gt;https://contacts.example.com/remote.php/dav&lt;/serverURL&gt;\n    &lt;/addressBook&gt;\n    &lt;!-- 用于同步用户日历方式的配置. 尚未实现. Thunderbird 使用 RFC 6764 实现这类功能的自动发现. --&gt;\n    &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt;\n    &lt;calendar type=&quot;caldav&quot;&gt;\n        &lt;!-- 用户名 --&gt;\n        &lt;username&gt;%EMAILADDRESS%&lt;/username&gt;\n        &lt;!-- 身份验证方式, 参见 addressBook 中的身份验证方式. --&gt;\n        &lt;authentication&gt;\n            http-basic&lt;/authentication&gt;\n        &lt;!-- 指定服务器 URL. --&gt;\n        &lt;serverURL&gt;https://calendar.example.com/remote.php/dav&lt;/serverURL&gt;\n    &lt;/calendar&gt;\n\n    &lt;!-- 用于用户进行文件共享方式的配置. 尚未实现. 可用于 Thunderbird 中的 FileLink 或者在用户桌面创建一个同步文件夹. --&gt;\n    &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt;\n    &lt;fileShare type=&quot;webdav&quot;&gt;\n        &lt;!-- 用户名 --&gt;\n        &lt;username&gt;%EMAILADDRESS%&lt;/username&gt;\n        &lt;!-- 身份验证方式, 参见 addressBook 中的身份验证方式. --&gt;\n        &lt;authentication&gt;http-basic&lt;/authentication&gt;\n        &lt;!-- 指定服务器 URL. --&gt;\n        &lt;serverURL&gt;https://share.example.com/remote.php/dav&lt;/serverURL&gt;\n    &lt;/fileShare&gt;\n\n    &lt;!-- 可选, 用于访问 Webmail 的配置. 其中的 URL 值会在标准的网络浏览器中打开. --&gt;\n    &lt;webMail&gt;\n        &lt;!-- Webmail 的登录页面 URL, 用户必须手动输入用户名和密码进行登录. URL 需要使用 HTTPS. --&gt;\n        &lt;loginPage url=&quot;https://mail.example.com/login/&quot; /&gt;\n\n        &lt;!--\n            与 loginAutomaticDOM 相同, 但网站会检查用户是否来自登录页.\n            会在浏览器中打开登录页面, 获取页面 DOM, 然后自动填写用户名和密码然后触发登录按钮.\n            登录按钮可能不是 HTML 而是一个 div, 所以触发这类按钮需要发送一个点击事件.\n            URL 需要使用 HTTPS.\n        --&gt;\n        &lt;loginPageInfo url=&quot;https://mail.example.com/login/&quot;&gt;\n            &lt;!-- username: 用户名\n                填写入 usernameField 中的内容.\n                格式(包括占位符)与 incomingServer 中的 username 相同.\n            --&gt;\n            &lt;username&gt;%EMAILADDRESS%&lt;/username&gt;\n            &lt;!--\n                允许找到页面中的文本字段并填充它们.\n                id 属性输出给 DOM name 属性. id 和 name 属性必须同时或存在其中一个. 如果存在, 则优先使用 id(例如使用 &quot;getElementByid()&quot;), 如果不存在,\n            则会尝试按名称查找元素.\n                不要把这个示例 XML 中的这些 id 当成是通用的, 在使用之前应该要验证格式(例如某些仅使用字符和数字的 id).\n                如果你还使用 jQuery 这样强大的函数, 还在 XML 的用户名 id 中返回了没有经过检查的代码, 那么它有可能会被执行.\n            --&gt;\n            &lt;usernameField id=&quot;email_field&quot; name=&quot;email&quot; /&gt;\n            &lt;passwordField name=&quot;password&quot; /&gt;\n            &lt;!-- 定义提交按钮, 用于填充完成后触发自动提交. id 和 name 属性参见 username.--&gt;\n            &lt;loginButton id=&quot;submit_button&quot; name=&quot;login&quot; /&gt;\n        &lt;/loginPageInfo&gt;\n    &lt;/webMail&gt;\n\n    &lt;!-- 尚未实现, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=564043&quot;. --&gt;\n    &lt;!--\n        在某些电子邮件服务提供商中, 用户名和密码并不与 IMAP, POP3 或 SMTP 的用户名和密码对应.\n        比如用户邮件地址是 &quot;Alice@example.com&quot;, 但用于登录电子邮件服务器的用户名是给定的用户 ID 或随机的用户名, 密码也可能同样不与电子邮件服务器的登录密码相同.\n        此时可以提供一个 inputField 标签, 这可以让客户端向用户显示一个输入框, 从而实现自定义的的用户名或密码.\n        输入框的提示由标签中的 label 属性定义, 其填充示例是 inputField 标签的值.\n        用户在对应 inputField 标签中输入的内容会写入到标签属性的 key 之中, 这个用于 key 属性变量值必须是大写字母.\n        同时被定义的变量可以在 XML 配置中的其他位置使用, 但并不建议用这种办法来让用户定义其他的配置属性(因为这样做的话, 提供该 XML 配置的意义就变得不再那么大, 并且用户体验比在 UI\n    上手动配置还要差).\n    --&gt;\n    &lt;inputField key=&quot;USERNAME&quot; label=&quot;Username&quot;&gt;\n        123456&lt;/inputField&gt;\n    &lt;inputField key=&quot;NICKNAME&quot; label=&quot;Nickname&quot;&gt;Alice&lt;/inputField&gt;\n    &lt;!--\n        以上配置将生成如下的 UI:\n        Username: [          ] example: 123456\n        Nickname: [          ] example: Alice\n        同时, 在配置文件的其他位置就能够使用 &quot;%USERNAME%&quot; 和 &quot;%NICKNAME%&quot; 来引用这两个用户输入的值.\n    --&gt;\n\n    &lt;!-- clientConfigUpdate: 客户端用于配置文件更新的 URL. --&gt;\n    &lt;clientConfigUpdate url=&quot;https://www.example.com/config/mozilla.xml&quot; /&gt;\n\n&lt;/clientConfig&gt;\n</code></pre>\n<p>对于配置文件中的 <code>incomingServer</code> (传入服务器) 和 <code>outgoingServer</code>(传出服务器) 可能出现多次. 但应该按照优先使用顺序来排序, 通常会使用第一个出现的服务器配置, 只有当实际服务器表现不满足配置中要求的选项时(比如配置指定使用 SSL 但实际无法通过 SSL 连接到服务器), 才尝试选择第二个服务器配置. 如果 IMAP 和 POP3 服务器同时存在, 客户端应该同时列出(依旧遵循配置中的顺序显示来表示服务商的偏好), 让用户自行选择需要的服务器协议.</p>\n<p>除上面配置文件中提到的, 依据用户在客户端的输入内容, 还存在以下通用占位符(变量):</p>\n<ul>\n<li><code>%EMAILADDRESS%</code>: 用户的完整电子邮件地址.</li>\n<li><code>%EMAILLOCALPART%</code>: 用户电子邮件地址中 &quot;@&quot; 前的部分.</li>\n<li><code>%EMAILDOMAIN%</code>: 用户电子邮件地址中 &quot;@&quot; 后的部分.</li>\n<li><code>%REALNAME%</code>: 真实名字(?)</li>\n</ul>\n<hr>\n<p>依据这个规范编写的一个典型的 IMAP &amp; POP3 &amp; SMTP 服务器配置应该像这样:</p>\n<pre class=\"language-xml\"><code>&lt;?xml version=&quot;1.0&quot;?&gt;\n&lt;clientConfig version=&quot;1.1&quot;&gt;\n    &lt;emailProvider id=&quot;example.com&quot;&gt;\n        &lt;domain&gt;example.com&lt;/domain&gt;\n        &lt;displayName&gt;EXAMPLE Inc&lt;/displayName&gt;\n        &lt;displayShortName&gt;CORG&lt;/displayShortName&gt;\n        &lt;incomingServer type=&quot;imap&quot;&gt;\n            &lt;hostname&gt;imap.example.com&lt;/hostname&gt;\n            &lt;port&gt;994&lt;/port&gt;\n            &lt;socketType&gt;SSL&lt;/socketType&gt;\n            &lt;username&gt;%EMAILADDRESS%&lt;/username&gt;\n            &lt;authentication&gt;password-cleartext&lt;/authentication&gt;\n        &lt;/incomingServer&gt;\n        &lt;incomingServer type=&quot;pop3&quot;&gt;\n            &lt;hostname&gt;pop3.example.com&lt;/hostname&gt;\n            &lt;port&gt;995&lt;/port&gt;\n            &lt;socketType&gt;SSL&lt;/socketType&gt;\n            &lt;username&gt;%EMAILADDRESS%&lt;/username&gt;\n            &lt;authentication&gt;password-cleartext&lt;/authentication&gt;\n            &lt;pop3&gt;\n                &lt;leaveMessagesOnServer&gt;true&lt;/leaveMessagesOnServer&gt;\n            &lt;/pop3&gt;\n        &lt;/incomingServer&gt;\n        &lt;outgoingServer type=&quot;smtp&quot;&gt;\n            &lt;hostname&gt;smtp.example.com&lt;/hostname&gt;\n            &lt;port&gt;465&lt;/port&gt;\n            &lt;socketType&gt;SSL&lt;/socketType&gt;\n            &lt;username&gt;%EMAILADDRESS%&lt;/username&gt;\n            &lt;authentication&gt;password-cleartext&lt;/authentication&gt;\n        &lt;/outgoingServer&gt;\n        &lt;clientConfigUpdate url=&quot;https://autoconfig.example.com/mail/config-v1.1.xml&quot; /&gt;\n    &lt;/clientConfig&gt;\n</code></pre>\n<p>对于不需要的配置如果不是需要保留为空值, 那么应该在配置文件中移除.</p>\n\n<h3 id=\"rfc-6186-2\"><a href=\"#rfc-6186-2\"></a>RFC 6186</h3>\n<p>RFC 6186 的响应是基于 DNS 的 SRV 记录, 所以实现响应的标准就是 SRV 记录(RFC 2782<sup class=\"footnote-ref\"><a href=\"#fn4\" id=\"fnref4\">[4]</a></sup>).</p>\n<p>SRV 记录标准:</p>\n<pre class=\"language-zone\"><code>_Service._Proto.Name TTL Class SRV Priority Weight Port Target\n</code></pre>\n<p>使用 ZONE 进行命名就是:</p>\n<pre class=\"language-zone\"><code>_[服务类型]._[协议].[域名名称] [生存时间] IN SRV [优先级] [权重] [端口] [目标服务器]\n</code></pre>\n<p>比如:</p>\n<pre class=\"language-zone\"><code>_minecraft._tcp.minecraft.example.com 3600 IN SRV 0 1 19132 minecraft.hoster.com.\n</code></pre>\n<h2 id=\"部署与应用\"><a href=\"#部署与应用\"></a>部署与应用</h2>\n<p>依据在<a href=\"#%E5%8D%8F%E8%AE%AE%E5%93%8D%E5%BA%94%E8%A7%84%E8%8C%83\">协议响应规范</a>中提到的各自的发现行为, 除了 RFC 6186, 实现只需要在对应域名设置 DNS 即可. Autodiscover 和 Autoconfiguration 按照对应需要解析 DNS 并放置配置文件至对应域名的固定路径即可实现部署.</p>\n<h3 id=\"autodiscover-3\"><a href=\"#autodiscover-3\"></a>Autodiscover</h3>\n<h4 id=\"标准实现\"><a href=\"#标准实现\"></a>标准实现</h4>\n<ol>\n<li>\n<p>编写正确规范的 XML 配置文件.</p>\n</li>\n<li>\n<p>将电子邮件地址使用的域名解析到一个用于托管配置文件的网络服务器. 如:</p>\n<pre class=\"language-zone\"><code>example.com 3600 IN A 1.0.0.1\n</code></pre>\n</li>\n<li>\n<p>将配置文件放入网络服务器, 使其路径为: <code>/Autodiscover/Autodiscover.xml</code>.</p>\n</li>\n<li>\n<p>再次解析邮件地址域名的子域名 <code>autodiscover</code> 到配置文件服务器:</p>\n<pre class=\"language-zone\"><code>autodiscover.example.com 3600 IN A 1.0.0.1\n</code></pre>\n</li>\n<li>\n<p>将配置文件放入网络服务器, 使其路径为: <code>/Autodiscover/Autodiscover.xml</code></p>\n</li>\n</ol>\n<p>多个配置文件理论并不会相互覆盖, 因为只会使用最先被发现的的那一个配置文件, 其目的是为了冗余. 但是不同客户端可能存在不同的行为, 所以建议同时设置多个方法且保证配置文件内容相同. 如果当前 DNS 解析状况导致解析已存在或不允许再次占用, 那么应该向后选择不冲突的那一种实现方式.</p>\n\n<p>当然使用 CNAME 也是可以的, 因为会自动到 DNS 中的 CNAME 域名寻找.</p>\n<h4 id=\"跳转实现\"><a href=\"#跳转实现\"></a>跳转实现</h4>\n<p>从 Autodiscover 响应规范小节可以看出, 配置文件中存在一个 <code>RedirectUrl</code> 的标签, 可以让电子邮件客户端解析到时跳转到下一个网络配置文件路径.</p>\n<p>因此可以将当前电子邮件域名的两个用于 Autodiscover 的 DNS 记录解析后在网络服务器对应的路径放入一个只用于重定向跳转的 XML:</p>\n<p>XML 格式应该为:</p>\n<pre class=\"language-xml\"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt;\n    &lt;Response\n        xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt;\n        &lt;Account&gt;\n            &lt;AccountType&gt;email&lt;/AccountType&gt;\n            &lt;Action&gt;redirectUrl&lt;/Action&gt;\n            &lt;RedirectUrl&gt;https://filesystem.example.com/autodiscover/example.com/Autodiscover.xml&lt;/RedirectUrl&gt;\n        &lt;/Account&gt;\n    &lt;/Response&gt;\n&lt;/Autodiscover&gt;\n</code></pre>\n<p>这样就能将当前电子邮件域名的配置文件重定向到另一个网络文件路径</p>\n<p>这样做的好处是能够将大量的域名电子邮件配置集中在一个网络文件系统中管理, 这个文件系统可以是你的服务商管理也可以是企业的 IT 管理员管理, 作为客户及最终用户可以免去维护 XML 配置的步骤. 如果多个域名都使用了相同的电子邮件服务器或电子邮件服务提供商, 那么这些电子邮件服务器的提供的客户端配置一般都是相同的.</p>\n<p>但相比直接使用 CNAME 的跳转发现, 还需要配置一个用于跳转的 XML, 所以其实并不太理想. 这样的跳转实现大多用在 Outlook 本地安装的情况, 在本地设备上一次性使用注册表或者组策略将 Outlook 的 Autodiscover 指向这个跳转文件.</p>\n<p>除了使用 HTTP 跳转, 还能跳转到另一个邮箱地址进行发现, XML 格式为:</p>\n<pre class=\"language-xml\"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt;\n    &lt;Response\n        xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt;\n        &lt;Account&gt;\n            &lt;AccountType&gt;email&lt;/AccountType&gt;\n            &lt;Action&gt;redirectAddr&lt;/Action&gt;\n            &lt;RedirectAddr&gt;others@example.net&lt;/RedirectAddr&gt;\n        &lt;/Account&gt;\n    &lt;/Response&gt;\n&lt;/Autodiscover&gt;\n</code></pre>\n<p>同样的, 这样会让客户端解析到这个 XML 文件后直接跳转到 <code>others@example.net</code> 对这个邮箱地址的 <code>example.net</code> 的域名进行配置文件发现. 相较于直接配置一个 XML 的好处是: 避免跳转 URL 的修改可能无法及时同步到用于本地配置. 所以使用了跳转到另一个邮箱域名进行重新发现.</p>\n<p>多种配置模式适用于不同的电子邮件系统架构和组织部署环境, 需要按照需求选择合适自身的方法.</p>\n<h3 id=\"autoconfiguration-3\"><a href=\"#autoconfiguration-3\"></a>Autoconfiguration</h3>\n<h4 id=\"标准实现-2\"><a href=\"#标准实现-2\"></a>标准实现</h4>\n<ol>\n<li>编写正确规范的 XML 配置文件.\n<ol>\n<li>\n<p>将电子邮件地址使用的域名的子域名 <code>autoconfig</code> 解析到一个用于托管配置文件的网络服务器, 如:</p>\n<pre class=\"language-zone\"><code>autoconfig.example.com 3600 IN A 1.0.0.1\n</code></pre>\n</li>\n<li>\n<p>将配置文件放入网络服务器, 使其路径为: <code>/mail/config-v1.1.xml</code>.</p>\n</li>\n</ol>\n</li>\n<li>编写正确规范的 XML 配置文件.\n<ol>\n<li>\n<p>将电子邮件域名直接解析至配置文件服务器:</p>\n<pre class=\"language-zone\"><code>example.com 3600 IN A 1.0.0.1\n</code></pre>\n</li>\n<li>\n<p>将配置文件放入网络服务器, 使其路径为: <code>/.well-known/autoconfig/mail/config-v1.1.xml</code></p>\n</li>\n</ol>\n</li>\n<li>编写正确规范的 XML 配置文件.\n<ol>\n<li>\n<p>将其电子邮件域名的子域名 <code>www</code> 解析至网络服务器, 并将 XML 文件放入该网络文件服务器, 使其为任意便于管理的路径, 如 <code>/autoconfig/example.com/config-v1.1.xml</code>.</p>\n</li>\n<li>\n<p>为电子邮箱域名添加一个 TXT 记录, 值为上一步放置的 XML 文件完整的 HTTP/HTTPS 网络路径:</p>\n<pre class=\"language-zone\"><code>example.com 3600 IN TXT &quot;https://www.example.com/autoconfig/example.com/config-v1.1.xml&quot;\n</code></pre>\n</li>\n</ol>\n</li>\n</ol>\n<p>多个配置文件理论并不会相互覆盖, 因为只会使用最先被发现的的那一个配置文件, 其目的是为了冗余. 但是不同客户端可能存在不同的行为, 所以建议同时设置多个方法且保证配置文件内容相同. 如果当前 DNS 解析状况导致解析已存在或不允许再次占用, 那么应该向后选择不冲突的那一种实现方式.</p>\n\n<h4 id=\"中心化实现\"><a href=\"#中心化实现\"></a>中心化实现</h4>\n<p>本文将 Autoconfiguration 实现方法中, 需要依赖第三方中心化服务实现自动发现的方式称为 &quot;中心化实现&quot;. 比如这里要提到的 &quot;尝试从 Mozilla 服务器检索配置&quot;.</p>\n<p>由 Thunderbird 维护并托管的电子邮件服务商配置文件数据库称为 &quot;ISPDB&quot;, 可免费供给任何用户(客户)使用. 它包含了最多的电子邮件服务商配置, 大多市场份额超过 0.1% 的服务商都包含在内, 几乎囊括了全球 50% 的电子邮件账户.</p>\n\n<p>但要注意, 并不是随便一个服务商就能进入这个数据库, 比如自托管的个人电子邮件服务器就不符合要求.  ISPDB 要求:</p>\n<blockquote>\n<p><em>If you are a big ISP (&gt; 100,000 users) providing email addresses solely under a few domains like &quot;<code>example.com</code>&quot; and &quot;<code>example.de</code>&quot;, you may either submit the configuration to the ISPDB or set up a configuration server.</em><sup class=\"footnote-ref\"><a href=\"#fn5\" id=\"fnref5\">[5]</a></sup></p>\n<blockquote>\n<p><em>&quot;如果您是大型 ISP(&gt; 100,000 个用户), 仅在 &quot;<code>example.com</code>&quot; 和 &quot;<code>example.de</code>&quot; 等几个域下提供电子邮件地址, 您可以将配置提交到 ISPDB 或设置配置服务器.&quot;</em><sup class=\"footnote-ref\"><a href=\"#fn5\" id=\"fnref5:1\">[5:1]</a></sup></p>\n</blockquote>\n<p><em>If you are a small company installing Thunderbird on your employees' desktops, you can place a configuration file in the Thunderbird installation folder.</em><sup class=\"footnote-ref\"><a href=\"#fn5\" id=\"fnref5:2\">[5:2]</a></sup></p>\n<blockquote>\n<p><em>如果您是在员工桌面上安装 Thunderbird 的小公司, 您可以将配置文件放在 Thunderbird 安装文件夹中.</em><sup class=\"footnote-ref\"><a href=\"#fn5\" id=\"fnref5:3\">[5:3]</a></sup></p>\n</blockquote>\n</blockquote>\n<p>如果想要为自己的电子邮件服务能够通过 Mozilla 服务器查找到服务器配置, 那么除去需要在客户端支持从 ISPDB  中检索, 还要为该服务的源 Git 仓库提交你的 Autoconfiguration 配置文件:</p>\n<blockquote>\n<p><a href=\"https://github.com/thundernest/autoconfig\">thundernest/autoconfig: The ISPDB, Thunderbird's database of mail configuration files.</a></p>\n</blockquote>\n<p>源仓库存在两分支的配置文件索引, 一个是 <code>prod</code> 分支, 直接用于 Thunderbird(<code>https://autoconfig.thunderbird.net/autoconfig/v1.1/</code>); 另一个是 <code>master</code> 分支, 用于暂存服务器(<code>https://autoconfig-stage.thunderbird.net/autoconfig/v1.1/</code>).</p>\n<h3 id=\"rfc-6186-3\"><a href=\"#rfc-6186-3\"></a>RFC 6186</h3>\n<p>RFC 6186<sup class=\"footnote-ref\"><a href=\"#fn2\" id=\"fnref2:2\">[2:2]</a></sup> 制定的目的就是为了解决从域名中发现电子邮件服务器配置的问题, SMTP<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1:1\">[1:1]</a></sup> 本身存在对 MX 记录进行发现进而查找到对应的服务器主机, 但是现代邮件系统中的 MSA 不使用 MX 记录以及工作端口是 587. 于是这个规范设计来与 RFC 4409 联合使用.</p>\n<blockquote>\n<p>在由 RFC 4409<sup class=\"footnote-ref\"><a href=\"#fn6\" id=\"fnref6\">[6]</a></sup> 确立的现代的电子邮件提交系统里, 在用户发送电子邮件时, 需要通过 MSA(Message submission agent, 消息提交代理)进行处理才能传输到邮件服务器或中继服务器. 通常来说, 最终用户使用的传出服务器基本都是 MSA 系统, 这样避免了恶意提交对邮件服务器的侵害, 也能保护用户身份不被盗用.</p>\n</blockquote>\n<p>这个标准中定义了对  MSA, IMAP 和 POP3 进行配置的新 SRV 记录类型, 并且还可以定义优先使用哪一种传入协议.</p>\n<p>值得一提的是, RFC 6186 是由 Apple 起草发起的, RFC 4409 主要作者之一是 Qualcomm(高通).</p>\n<h4 id=\"实现标准\"><a href=\"#实现标准\"></a>实现标准</h4>\n<ol>\n<li>\n<p>MSA</p>\n<p>使用如下的 SRV 记录指向 MSA 服务:</p>\n<pre class=\"language-zone\"><code>_submission._tcp 3600 IN SRV 0 1 587 mail.example.com.\n</code></pre>\n</li>\n<li>\n<p>IMAP</p>\n<blockquote>\n<ul>\n<li>使用 <code>imap</code> 标识一个普通 IMAP 服务类型.</li>\n</ul>\n<blockquote>\n<p>对于 <code>STARTTLS</code> 应该是客户端和 IMAP 服务器必须实现的, 但有的服务商可能并不需要使用.</p>\n</blockquote>\n<ul>\n<li>使用 <code>imaps</code> 表示一个使用 TLS 连接的 IMAP 服务器.</li>\n</ul>\n</blockquote>\n<p>使用如下的 SRV 记录指向 IMAP 服务:</p>\n<pre class=\"language-zone\"><code>_imap._tcp 3600 IN SRV 0 1 143 imap.example.com.\n</code></pre>\n<pre class=\"language-zone\"><code>_imaps._tcp 3600 IN SRV 0 1 993 imap.example.com.\n</code></pre>\n</li>\n<li>\n<p>POP3</p>\n<blockquote>\n<ul>\n<li>使用 <code>pop3</code> 标识一个可能会需要客户端使用 STLS<sup class=\"footnote-ref\"><a href=\"#fn7\" id=\"fnref7\">[7]</a></sup> 进行连接的普通 POP3 服务器.</li>\n<li>使用 <code>pop3s</code> 标识一个使用 TLS 连接的 POP3 服务器.</li>\n</ul>\n</blockquote>\n<p>使用如下的 SRV 记录指向 POP3 服务:</p>\n<pre class=\"language-zone\"><code>_pop3._tcp 3600 IN SRV 0 1 110 pop3.example.com.\n</code></pre>\n<pre class=\"language-zone\"><code>_pop3s._tcp 3600 IN SRV 0 1 995 pop3.example.com.\n</code></pre>\n</li>\n</ol>\n<h4 id=\"优先级指定\"><a href=\"#优先级指定\"></a>优先级指定</h4>\n<p>由于 SRV 记录允许为服务指定优先级, 所以如果同时在 DNS 中解析指向了多种服务类型, 那么管理员可以通过指定 SRV 记录优先级表示服务端对服务类型的偏好.</p>\n<blockquote>\n<p>SRV 记录与 MX 记录指定优先级的概念相同, 是以优先级数字编号越小代表越优先(权重恰恰相反, 数字越大表示权重越高, 但在 SRV 里优先使用优先级, 如果优先级相同再使用权重判定).</p>\n</blockquote>\n<p>通过设置优先级, 将希望优先被采用的传入服务器配置记录使其小于其他的服务记录实现服务端偏好设置.</p>\n<p>优先使用 IMAP 而不是 POP3:</p>\n<pre class=\"language-zone\"><code>_imap._tcp 3600 IN SRV 0 1 143 imap.example.com.\n</code></pre>\n<pre class=\"language-zone\"><code>_pop3._tcp 3600 IN SRV 10 1 110 pop3.example.com.\n</code></pre>\n<p>通过将 SRV 记录中的目标服务器值设置为 <code>.</code> 来表示该域完全不支持此服务. 如果存在这种记录值, 则电子邮件客户端应该认为这种服务器不可用, 并继续使用 SRV 记录进行确定.</p>\n<p>比如, 通过以下的记录值</p>\n<pre class=\"language-zone\"><code>_imap._tcp 3600 IN SRV 0 0 0 .\n</code></pre>\n<pre class=\"language-zone\"><code>_imaps._tcp 3600 IN SRV 0 1 993 imap.example.com.\n</code></pre>\n<pre class=\"language-zone\"><code>_pop3._tcp 3600 IN SRV 0 0 0 .\n</code></pre>\n<pre class=\"language-zone\"><code>_pop3s._tcp 3600 IN SRV 10 1 995 pop3.example.com.\n</code></pre>\n<p>上面的记录值指定本域中存在使用 TLS 和不使用 TLS 的 IMAP 服务和 POP3 服务(带 s 和不带 s), 但是不使用 TLS 的 IMAP 和 POP3 服务不可用. 且使用 IMAPs 服务的优先级高于使用 POP3s, 因为 IMAPs 的优先级 0 要比 POP3s 的优先级 10 要小.</p>\n<h2 id=\"现状与未来\"><a href=\"#现状与未来\"></a>现状与未来</h2>\n<p>到此为止, 本文就介绍完目前所有<strong>去中心化</strong>且<strong>主流的</strong>实现自动发现方式了.</p>\n<p>那么现在在电子邮件中的自动发现是一个什么样的情况? 未来呢?</p>\n<h3 id=\"现状\"><a href=\"#现状\"></a>现状</h3>\n<p>目前文中提到的自动发现, 对于 Autodiscover, 在 Exchange Online 所有的 Exchange Online 服务都需要组织管理员单独为域名设置一个 <code>autodiscover</code> 别名记录指向微软的 Outlook 服务器 <code>autodiscover.outlook.com</code> 并且由于关闭了基础身份验证, 还要用户先通过身份验证才能检索到 Microsoft 服务器中的配置; Exchange Server 目前依旧能够通过本地的 Active Directory 和 IIS 服务器发现到配置文件. 就目前来说, Autodiscover 还在是 Microsoft 自己设计能尚且还在运行的标准, 其他的非 Exchange 电子邮件服务器也能通过它的标准的部分来兼容实现自动发现.</p>\n<p>对于 Autoconfiguration, 这是 Mozilla 的 Thunderbird 采用的一种发现标准, 比起 Autodiscover 为了兼容更多服务商的情况给出了很多 Autodiscover 中没有的选项. 但有的选项 Thunderbird 自己都没有实现支持, 或者还是存在疑问的地方. 最后衍生出了 Mozilla 自己的中心化 ISPDB 服务器, 主要的目的也是为了兼容.</p>\n<p>而 RFC 6186 目前还是处于 &quot;拟议标准(Proposed Standard)&quot; 状态, 也就是说这个标准刚通过起草阶段被 IETF 审阅后公布, 而如果已经做到被互联网广泛采用, 它应该已经被标记上 &quot;标准追踪(Standards Track)&quot; 状态. 它首次发表于 2011 年, 最后一次修改是 2015 年.</p>\n<hr>\n<p>对于这些针对服务端份规范, 尚且处于不成熟不统一的状态. 那么客户端的规范呢?</p>\n<p>对于目前的电子邮件客户端, 大部分由电子邮件服务商直接提供的本地客户端都优先使用的是内置的配置数据库, 对于不存在于内置数据库中的还是会要求用户自己输入配置, 并不会尝试去通过以上的标准进行自动发现. 并且对于这些专有客户端, 用户也无从得知它的自动发现支持状态和实现标准.</p>\n<p>除了专有客户端, 大部分开源的或其他开放的电子邮件客户端几乎都支持其中一种或多种自动发现, 也内置预设配置数据. 实在不济的也还能通过账号系统保存上一次得到的配置数据, 或者能够导入导出账号列表的数据.</p>\n<hr>\n<p>对于电子邮件服务提供商, 大部分都不会提供配置自动发现的指引, 甚至不会在 DNS 记录中提供官方的配置. 就算一万个用户使用十万个域名, 他们使用的服务端配置都是相同, 但服务商依旧不会提供自动发现, 甚至不提供指引, 当然这里也不是指所有的代托管服务商, 有少部分的服务商具有完整的自动发现指引, 有的还提供官方的自动发现服务器, 能够让客户(用户)通过自己的域名 CNAME 实现支持.</p>\n<hr>\n<p>对于用户, 本该并不需要知道什么是自动发现, 这应该是电子邮件管理员和电子邮件服务商提前配置好的. 但是由于客户端的问题, 或是管理员和服务商根本没有考虑自动发现, 导致每次登录新的客户端都需要手动配置至少两个(传入与传出)服务器的参数才能使用, 而且如果不是专业用户, 他们甚至会面对这些参数手无足措, 转而把问题转移到服务商和管理员身上(大部分不支持自动发现的客户端反而却能幸免于难). 那么, 又到了给不懂计算机的朋友修电脑的时候了, 但要面对的可能是你的用户.</p>\n<p>但是, 即使是精明的用户, 也并不是都想或都了解过自动发现. 而是更愿意使用更成熟的, 内建客户端账号系统的电子邮件客户端, 将这些繁杂的服务器配置数据保存在第三方手中(包括密码).</p>\n<h3 id=\"未来\"><a href=\"#未来\"></a>未来</h3>\n<p>笔者期望的未来是用 RFC 实现的互联网标准, 但不会是目前的 RFC 6186, 因为它并不能解决除了下发配置之外的问题, 至少也要如 Mozilla Thunderbird 的 Autoconfiguration 中的一样, 为身份验证和个性化给出支持, 对于现代的 MSA 系统大部分都已经开始采用 API 与服务器交互, 而不是还在使用服务器通讯协议, MSA 的 587 端口同样难以抵御泛滥的网络攻击, 部分的服务商已经开始使用更进一步的身份验证保护 MSA 系统甚至禁止基本身份验证, 这其中包括现在多方强调的多因素身份验证以及零信任模型.</p>\n<p>现代的电子邮件系统已经将用户和攻击者隔离在服务器之外很远的地方, 用户能通过身份验证表示攻击者也同样能, 只要还存在暴露的门, 那就一定会有乘虚而入的家伙. 所以, 如果电子邮件系统在未来依旧不会消亡, 那么对于自动发现应该实现的, 应该包括服务器安全策略和客户端策略, 而不是简单的指向一个端口和服务器.</p>\n<p>又或者, 在 MSA 系统和用户之间再叠一层专用于身份验证和威胁防御的专用系统? 也许会叫 &quot;用户认证代理(User Verification Agent, UVA)&quot;?🥱</p>\n<h2 id=\"参考资料\"><a href=\"#参考资料\"></a>参考资料</h2>\n<ul>\n<li><a href=\"https://learn.microsoft.com/zh-cn/exchange/architecture/client-access/autodiscover\">Exchange Server中的自动发现服务 | Microsoft Learn</a></li>\n<li><a href=\"https://learn.microsoft.com/zh-cn/exchange/client-developer/exchange-web-services/autodiscover-for-exchange\">Exchange 自动发现 | Microsoft Learn</a></li>\n<li><a href=\"https://learn.microsoft.com/zh-cn/previous-versions/office/office-2010/cc511507(v=office.14)\">计划自动在 Outlook 2010 中配置用户帐户 | Microsoft Learn</a></li>\n<li><a href=\"https://testconnectivity.microsoft.com/\">Microsoft Remote Connectivity Analyzer</a></li>\n<li><a href=\"https://wiki.mozilla.org/Thunderbird:Autoconfiguration\">Thunderbird:Autoconfiguration - MozillaWiki</a></li>\n<li><a href=\"https://wiki.mozilla.org/Thunderbird:Autoconfiguration:DNSBasedLookup\">Thunderbird:Autoconfiguration:DNSBasedLookup - MozillaWiki</a></li>\n<li><a href=\"https://www.bucksch.org/1/projects/thunderbird/autoconfiguration/config-file-format.html\">Thunderbird Autoconfiguration - Config file format</a></li>\n<li><a href=\"https://www.zoho.com/mail/help/adminconsole/autodiscovery-settings.html#alink1\">IMAP and ActiveSync Configuration for Outlook using Autodiscovery - Zoho Mail</a></li>\n<li><a href=\"https://www.howto-outlook.com/howto/autodiscoverconfiguration.htm#xmlredirect\">Autodiscover: Some quick methods to get it working - HowTo-Outlook</a></li>\n<li><a href=\"https://knowledge.validity.com/hc/en-us/sections/360005967372-Email-Glossary\">Email Glossary – Validity Help Center</a></li>\n<li><a href=\"https://www.cloudflare.com/zh-cn/learning/dns/dns-records/dns-srv-record/\">什么是 DNS SRV 记录？ | Cloudflare</a></li>\n<li><a href=\"https://en.wikipedia.org/wiki/Zone_file\">Zone file - Wikipedia</a></li>\n</ul>\n<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p><a href=\"https://datatracker.ietf.org/doc/html/rfc2821\">RFC 2821 - Simple Mail Transfer Protocol</a> (IETF) <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a> <a href=\"#fnref1:1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn2\" class=\"footnote-item\"><p><a href=\"https://datatracker.ietf.org/doc/rfc6186/\">RFC 6186 - Use of SRV Records for Locating Email Submission/Access Services</a> (IETF) <a href=\"#fnref2\" class=\"footnote-backref\">↩︎</a> <a href=\"#fnref2:1\" class=\"footnote-backref\">↩︎</a> <a href=\"#fnref2:2\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn3\" class=\"footnote-item\"><p><a href=\"https://github.com/thundernest/autoconfig\">thundernest/autoconfig: The ISPDB, Thunderbird's database of mail configuration files.</a> (GitHub) <a href=\"#fnref3\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn4\" class=\"footnote-item\"><p><a href=\"https://datatracker.ietf.org/doc/rfc2782/\">RFC 2782 - A DNS RR for specifying the location of services (DNS SRV)</a> (IETF) <a href=\"#fnref4\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn5\" class=\"footnote-item\"><p><a href=\"https://www.bucksch.org/1/projects/thunderbird/autoconfiguration/\">Thunderbird Autoconfiguration</a> (Ben Bucksch) <a href=\"#fnref5\" class=\"footnote-backref\">↩︎</a> <a href=\"#fnref5:1\" class=\"footnote-backref\">↩︎</a> <a href=\"#fnref5:2\" class=\"footnote-backref\">↩︎</a> <a href=\"#fnref5:3\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn6\" class=\"footnote-item\"><p><a href=\"https://datatracker.ietf.org/doc/rfc4409/\">RFC 4409 - Message Submission for Mail</a> (IETF) <a href=\"#fnref6\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n<li id=\"fn7\" class=\"footnote-item\"><p><a href=\"https://datatracker.ietf.org/doc/rfc2595/\">RFC 2595 - Using TLS with IMAP, POP3 and ACAP</a> (IETF) <a href=\"#fnref7\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n","content_text":"本文已合并至邮件人百科中, 后续更新请前往: https://www.mailer.su/wiki/configure/autodiscovery/ 前言 电子邮件属于一种去中心化的通讯协议, 核心的电子邮件标准只有出入站协议的定义, 并没有定义一个电子邮件服务器如何为电子邮件客户端下发服务器配置, 所以在 Autodiscover 和 Autoconfiguration 之前, 大部分的电子邮件客户端都需要用户手动填写服务器传入和传出配置, 而获取这些配置往往需要用户自己到邮件服务器后台, Webmail 或者提供托管服务的服务商文档中搜寻, 这样的做法等于用户配置与服务器配置并不是自动同步, 在邮件服务器出现更改(迁移某部分的域名, 加密协议或端口)并不会及时通知客户端中跟随做出更改, 从而造成服务端改动后客户端的收发问题. 除去邮件服务器和用户之间的不同步问题, 没有那么多经验用户自己在首次配置客户端的时候可能会对那些参数感到莫名其妙: 为什么我的 SMTP 服务器和 IMAP 服务器域名不是我的邮件地址域名? 为什么要使用 STARTTLS 而不是 TLS? 为什么我的密码不是我的 Webmail 密码? 然后就是: 为什么我每次都要自己手动填这些乱七八糟的参数才能用? 为什么 Webmail 就可以一键登录? (🤦‍♂️) 本文主要叙述基于 DNS 查询, HTTP 请求和静态配置文件实现的 Autodiscover 和 Autoconfiguration. 不包含基于本地的如: 环境变量, 注册表, 网络部署和用户目录等; 以及基于远程网络服务器的: 动态配置文件, 身份验证和多配置文件等. 区别与实现 所以, 为了解决以上的 &quot;电子邮件客户端自动配置邮件服务器参数&quot; 的问题, 引出了 &quot;Autodiscover&quot; 和 &quot;Autoconfiguration&quot;, 顾名思义 Autodiscover 即为 &quot;自动发现&quot;, Autoconfiguration 即为 &quot;自动配置&quot;. 两者大同小异, 区别在于前者 Autodiscover 主要指的是存在于 Microsoft 开发的电子邮件系统 Exchange 中使用到的另一个也是 Microsoft 开发的同步协议 Exchange ActiveSync 中附带的为电子邮件客户端下发邮件服务器配置的功能; 后者主要是由 Thunderbrid 定义的一种为了实现前者类似功能的规范. Autodiscover Autodiscover 目前存在于 Exchange Server 和 Exchange Online 中, 由于 Microsoft 逐渐推广禁止使用基本身份验证, 所以这部分功能可能未来只会在 Exchange Server 中见到了, 目前 Exchange Online 主要从 Active Directory 用户目录中获取配置, 之后才会尝试使用传统的 Autodiscover 去发现配置. 发起 Autodiscover 的基本步骤: 从 Exchange Activesync 中请求配置: 尝试从 URL 中请求配置: https://example.com/Autodiscover/Autodiscover.xml 尝试从 URL 中请求配置: https://autodiscover.example.com/Autodiscover/Autodiscover.xml 尝试从主机名 Autodiscover DNS 中寻找重定向 URL: autodiscover.example.com &gt;&gt; CNAME &gt;&gt; autodiscover.exchangeserver.com &gt;&gt; HTTP 301/302 &gt;&gt; autodiscover.mailhoster.com 尝试从重定向 URL 中请求配置: https://autodiscover.mailhoster.com/Autodiscover/Autodiscover.xml 尝试从主机名 Autodiscover DNS 中寻找重定向 URL: autodiscover.mailhoster.com ... Autodiscover 就是不断从主机名和 URI 中寻找配置, 一层一层从上到下尝试寻找, 经过多层重定向之后最终找到文件名为 Autodiscover.xml 配置文件, 如果多次重定向之后还是没找到, 那客户端读取不到配置文件就还是需要手动配置了. Autoconfiguration Autoconfiguration 来自 Thunderbrid, 使用了 Autodiscover 类似的实现方法, 但步骤和规范上有些区别. 发起 Autoconfiguration 的基本步骤: 从 Thunderbrid 在本地磁盘上的安装目录中读取配置: C:/Program Files/Mozilla/Thunderbird/isp/example.com.xml 从电子邮件服务商获取配置: 尝试请求 http://autoconfig.emailaddressdomain/mail/config-v1.1.xml?emailaddress=username@example.com 或 http://example.com/.well-known/autoconfig/mail/config-v1.1.xml 文件路径. 尝试从 DNS 记录中寻找配置: 尝试查询用户名域名 example.com 中存在内容为 https://www.example.com/mozilla.xml 的 TXT 记录依据记录进行请求配置文件未应用. 另一种提议, 也是基于 DNS未应用: 尝试解析用户名域名中的 MX 记录, 若存在多个 MX 记录则取最高优先级的记录; 如果不存在 MX 记录, 则查询并使用用户名域名中符合 SMTP 协议(RFC 2821[1])的 A 记录. 依据上方的查询结果, 从结果主机名中查询存在 mailconf=https://... 形式的 TXT 记录, 依据记录寻找配置文件, 如果 URL 错误则继续向下寻找. 如果查询不到有效的 TXT 记录则选择第一条有效的 TXT 记录进行查找配置文件. 在对配置文件进行 GET 请求时中附带初始请求的用户邮件地址(mailto:username@example.com)为 referrer, 用于实现可能存在的动态配置文件下发. 解析配置文件并让用户选择需要的协议和其他建议配置. 继续进行登录验证. 尝试解析用户名域名 DNS 从中寻找 SRV 记录获取配置(RFC 6186[2])未应用. 尝试从 Mozilla 服务器检索配置: 从 autoconfig.thunderbird.net[3] 请求用户名域名中的服务商配置文件, 如: https://autoconfig.thunderbird.net/v1.1/google.com. 如果找不到公共配置文件, 则尝试使用一些通配字符和常用端口进行猜测, 如 imap.example.com, pop.example.com, pop3.example.com, smtp.example.com 或 mail.example.com. 继续失败, 则直接让用户进行手动配置. 以上标记有 未应用 的都代表存在这些提议, 但没有实际应用到 Thunderbird 邮件客户端和服务器系统中. RFC 6186 请直接阅读下一节中有关 RFC 6186 的响应规范. 协议响应规范 从上面小节对两种自动寻找配置的方式可以看出, 它们都依赖于一个扩展名为 .xml 及使用 XML 标记语言的编写的二进制文件. 两种实现方式虽然在过程上大同小异, 但它们要使用到的最终配置文件是存在差异的, 并不能通用. Microsoft 在 Exchange Server 多个版本也修改了对于这部分静态文件的定义和规范, 直到现在的 Exchange Online 加入身份验证之后基本上就无法用于静态文件的实现, 所以需要向前使用还能单纯依靠静态文件就能被客户端解析的规范. 如果要一同在服务器实现, 那么需要分别针对两种方式编写对应的 XML 源文件. 此外上面还稍微提到过, 还存在一个拟议中的规范 RFC 6186[2:1], 并不依赖于特定域名下的配置文件, 而是使用完全基于 DNS 的 SRV 记录查询实现配置发现. Autodiscover 以下是 Microsoft 提供的适用 Outlook 2010 邮件客户端的服务端配置文件模板: 计划自动在 Outlook 2010 中配置用户帐户 | Microsoft Learn 以下配置文件注释经过本人翻译. Autodiscover.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt; &lt;!-- Response: 必须本标签用于指示该 XML 是一个 Autodiscover 响应.--&gt; &lt;Response xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt; &lt;!-- User: 可选本标签用于提供用户信息, Autodiscover 必须使用 UTF-8 编码.--&gt; &lt;User&gt; &lt;!-- DisplayName: 可选服务端使用这个标签提供一个默认显示名称. 客户端可以选择接受或自定义, 这可以免除用户所需要的输入从而节省时间.--&gt; &lt;DisplayName&gt;John Doe&lt;/DisplayName&gt; &lt;/User&gt; &lt;!-- Account: 必须本标签用于指定账户类型, 如电子邮件(Email), 新闻组(Newsgroup)和 SIP 服务器等.--&gt; &lt;Account&gt; &lt;!-- AccountType: 必须本标签中的值用于指定账户类型.可用值:email: 表示该标签内用于定义电子邮件服务器的配置.nntp: 表示该标签内用于定义 NNTP 服务器的配置(Outlook 2007 没有使用).--&gt; &lt;AccountType&gt;email | nntp&lt;/AccountType&gt; &lt;!-- Action: 必须本标签中的值用于定义直接使用本文件的配置或重定向至另一个可提供 Autodiscover 配置的网络服务器.可用值:redirectUrl: 如果指定使用这个值, 那么在 RedirectUrl 标签中必须要包含要定向到的 Autodiscover 配置路径(http/https). 为了防止服务端的重定向将客户端置入请求循环中, 客户端应在 10 次重定向后停止继续重定向.redirectAddr: 如果指定使用这个值, 表示客户端应当使用 RedirectAddr 标签中的电子邮件地址进行 Autodiscover 配置查找, 而不是使用当前的用户电子邮箱地址.settings: 如果指定使用了这个值, 表示本 XML 文件中包含进行账户配置所需要的信息. 客户端将使用 Protocol 标签下的服务器配置进行配置客户端.--&gt; &lt;Action&gt;redirectUrl | redirectAddr | settings&lt;/Action&gt; &lt;!-- RedirectUrl: 如果 Action 标签值指定为 &quot;redirectUrl&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.本标签的值应是一个 http 或 https 协议的 URL, 用于客户端使用该值进一步获取配置.--&gt; &lt;RedirectUrl&gt;redirect.URL&lt;/RedirectUrl&gt; &lt;!-- RedirectAddr: 如果 Action 标签值指定为 &quot;redirectAddr&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.本标签的值应是一个电子邮件地址, 用于客户端使用该地址重新查找 Autodiscover 配置.--&gt; &lt;RedirectAddr&gt;email@address&lt;/RedirectAddr&gt; &lt;!-- Image: 可选本标签的值是一张网络 JPG 图片, 用作配置电子邮件服务提供商的品牌标志(Outlook 2007 没有使用).--&gt; &lt;Image&gt;http://path.to.image.com/image.jpg&lt;/Image&gt; &lt;!-- ServiceHome: 可选本标签的值是一个指向电子邮件服务提供商主页的 URL, 客户端可以选择是否将这个值显示给用户(Outlook 2007 没有使用).--&gt; &lt;ServiceHome&gt;http://web.page.com&lt;/ServiceHome&gt; &lt;!-- Protocol: 如果 Action 标签值指定为 &quot;settings&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.本标签只能包含一个账户类型的配置信息, 多个 Protocol 标签可按照服务端的偏好进行配置, 但客户端可以自行选择偏好协议.--&gt; &lt;Protocol&gt; &lt;!-- Type: 必须.本标签的值指定使用何种账户类型.可选值:POP3: 指定连接到服务器的协议使用 POP3. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.SMTP: 指定连接到服务器的协议使用 SMTP. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.IMAP: 指定连接到服务器的协议使用 IMAP. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.DAV: 指定连接到服务器的协议使用 DAV. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.WEB: 指定客户端使用 Server 标签中的 URL 从浏览器访问电子邮件. 仅适用于 AccountType 标签值为 &quot;email&quot; 时(Outlook 2007 没有使用).NNTP: 指定连接到服务器的协议使用 NNTP. 仅适用于 AccountType 标签值为 &quot;nntp&quot; 时(Outlook 2007 没有使用).--&gt; &lt;Type&gt;POP3 | SMTP | IMAP | DAV | WEB | NNTP&lt;/Type&gt; &lt;!-- ExpirationDate: 可选.本标签的值指定一个该配置文件最后有效的日期, 在该日期过后客户端应该自动重新查找 Autodiscover 配置. 如果未指定则默认为不过期.--&gt; &lt;ExpirationDate&gt;YYYYMMDD&lt;/ExpirationDate&gt; &lt;!-- TTL: 可选.本标签的值指定该配置文件的有效时间, 单位为小时. 自首次查找该配置文件起算, 经过本标签值的时间后客户端将再次查找 Autodiscover 配置. 如果本标签未指定值, 则默认使用 1 小时的 TTL.--&gt; &lt;TTL&gt;168&lt;/TTL&gt; &lt;!-- Server: 必须.本标签的值指定了与上方 Type 标签值中账户类型对应的服务器地址.对于 Type 标签值为 POP3, SMTP, IMAP 或 NNTP 的, 该值应是一个主机名或 IP 地址.对于 Type 标签值为 DAV 和 WEB 的, 该值应是一个 URL.--&gt; &lt;Server&gt;mail.contoso.com&lt;/Server&gt; &lt;!--服务器的 IP 地址或域名--&gt; &lt;!-- Port: 可选.本标签的值指定对 Server 标签值使用的端口号. 如果未指定则根据 Type 标签值使用默认设置. 如果 Server 标签值是一个 URL, 则不使用本标签值.--&gt; &lt;Port&gt;110&lt;/Port&gt; &lt;!-- LoginName: 可选.本标签值指定用户的登录名. 如果未指定, 则默认使用用户设置的电子邮件地址 &quot;@&quot; 前的字串符. 如果本标签值指定为一个域名, 则使用 [用户名]@[域名] 的格式进行配置, 如 &quot;Alice@example.com&quot;.--&gt; &lt;LoginName&gt;Alice&lt;/LoginName&gt; &lt;!-- DomainRequired: 可选. 默认为 off.本标签指定身份验证过程中是否需要域名, 如果设置为 on, 则在身份验证过程中需要域名, off 则为不需要. 如果未在 LoginName 标签中指定一个域名或未指定 LoginName 标签的值, 那么需要用户手动输入域名才能进行身份验证.--&gt; &lt;DomainRequired&gt;on | off&lt;/DomainRequired&gt; &lt;!-- DomainName: 可选.本标签值指定用户域名. 如果未指定值, 则默认将用户电子邮件地址作为 UPN 的格式([用户名]@[域名])来使用. 如 Alic@example.com.--&gt; &lt;DomainName&gt;&lt;/DomainName&gt; &lt;!-- SPA: (Secure Password Authentication 安全密码验证) 可选.本标签值指定是否需要安全密码验证. 如果未指定, 则默认为 on.--&gt; &lt;SPA&gt;on | off&lt;/SPA&gt; &lt;!-- SSL: 可选.本标签值指定是否需要进行安全登录. 如果未指定, 则默认为 on.--&gt; &lt;SSL&gt;on | off&lt;/SSL&gt; &lt;!-- AuthRequired: 可选.本标签值指定是否需要身份验证(基于密码). 如果未指定, 则默认为 on.--&gt; &lt;AuthRequired&gt;on | off&lt;/AuthRequired&gt; &lt;!-- 可选: 是否需要身份验证. --&gt; &lt;!-- UsePOPAuth: 可选.本标签值仅在 Type 标签值为 &quot;SMTP&quot; 时可用. 用于指定 POP3 类型的账户使用的身份验证信息是否也将用于 SMTP.--&gt; &lt;UsePOPAuth&gt;on | off&lt;/UsePOPAuth&gt; &lt;!-- SMTPLast: 可选. 默认为 off.本标签值指定在通过 SMTP 发件前是否需要先下载电子邮件. 通常 SMTP 下载邮件时都会检查身份验证是否成功, 所以一般为需要.--&gt; &lt;SMTPLast&gt;on | off&lt;/SMTPLast&gt; &lt;/Protocol&gt; &lt;/Account&gt; &lt;/Response&gt;&lt;/Autodiscover&gt; 使用这个规范编写的一个典型的 IMAP &amp; POP3 &amp; SMTP 服务器配置应该像这样: Autodiscover.xml1234567891011121314151617181920212223242526272829303132333435363738394041&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;Autodiscover xmlns=&quot;http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt; &lt;Response xmlns=&quot;http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt; &lt;Account&gt; &lt;AccountType&gt;email&lt;/AccountType&gt; &lt;Action&gt;settings&lt;/Action&gt; &lt;Protocol&gt; &lt;Type&gt;IMAP&lt;/Type&gt; &lt;Server&gt;imap.example.com&lt;/Server&gt; &lt;Port&gt;993&lt;/Port&gt; &lt;DomainRequired&gt;off&lt;/DomainRequired&gt; &lt;LoginName&gt;&lt;/LoginName&gt; &lt;SPA&gt;off&lt;/SPA&gt; &lt;SSL&gt;on&lt;/SSL&gt; &lt;AuthRequired&gt;on&lt;/AuthRequired&gt; &lt;/Protocol&gt; &lt;Protocol&gt; &lt;Type&gt;POP3&lt;/Type&gt; &lt;Server&gt;pop3.example.com&lt;/Server&gt; &lt;Port&gt;995&lt;/Port&gt; &lt;DomainRequired&gt;off&lt;/DomainRequired&gt; &lt;LoginName&gt;&lt;/LoginName&gt; &lt;SPA&gt;off&lt;/SPA&gt; &lt;SSL&gt;on&lt;/SSL&gt; &lt;AuthRequired&gt;on&lt;/AuthRequired&gt; &lt;/Protocol&gt; &lt;Protocol&gt; &lt;Type&gt;SMTP&lt;/Type&gt; &lt;Server&gt;smtp.example.com&lt;/Server&gt; &lt;Port&gt;465&lt;/Port&gt; &lt;DomainRequired&gt;off&lt;/DomainRequired&gt; &lt;LoginName&gt;&lt;/LoginName&gt; &lt;SPA&gt;off&lt;/SPA&gt; &lt;Encryption&gt;SSL&lt;/Encryption&gt; &lt;AuthRequired&gt;on&lt;/AuthRequired&gt; &lt;UsePOPAuth&gt;off&lt;/UsePOPAuth&gt; &lt;SMTPLast&gt;off&lt;/SMTPLast&gt; &lt;/Protocol&gt; &lt;/Account&gt; &lt;/Response&gt;&lt;/Autodiscover&gt; 由于是使用标准的电子邮件协议, 而 Microsoft 提供的给 Exchange 服务器中的协议模板中有部分配置项目并不是必须的, 比如 UPN 就只是 Microsoft 专门用来指定一个账户个体的简称, 实际上和电子邮箱地址并没有区别, 两者几乎是一模一样的定义. Autoconfiguration 从 MozillaWiki 中可以得到配置格式: Thunderbird:Autoconfiguration:ConfigFileFormat - MozillaWiki 以下配置文件注释经过本人翻译. config-v1.1.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247&lt;?xml version=&quot;1.0&quot;?&gt;&lt;clientConfig version=&quot;1.1&quot;&gt; &lt;!-- 电子邮件服务提供商 --&gt; &lt;emailProvider id=&quot;example.com&quot;&gt; &lt;!-- domain: 域名 --&gt; &lt;domain&gt;example.com&lt;/domain&gt; &lt;domain&gt;example.net&lt;/domain&gt; &lt;!-- displayName: 显示在客户端上的名称. --&gt; &lt;displayName&gt;Google Mail&lt;/displayName&gt; &lt;!-- displayShortName: 显示的简称. --&gt; &lt;displayShortName&gt;GMail&lt;/displayShortName&gt; &lt;!-- incomingServer type: 传入服务器类型 &quot;imap&quot;: IMAP &quot;pop3&quot;: POP3 --&gt; &lt;incomingServer type=&quot;pop3&quot;&gt; &lt;!-- hostname: 选定类型所使用的服务器主机名. --&gt; &lt;hostname&gt; pop.example.com&lt;/hostname&gt; &lt;!-- port: 选定类型所使用的服务器端口号. --&gt; &lt;port&gt;995&lt;/port&gt; &lt;!-- socketType: 连接类型 &quot;plain&quot;: 不加密. &quot;SSL&quot;: SSL 加密. &quot;STARTTLS&quot;: 使用 STARTTLS 进行机会性加密 --&gt; &lt;socketType&gt;SSL&lt;/socketType&gt; &lt;!-- username: 指定用户名. --&gt; &lt;username&gt;%EMAILLOCALPART%&lt;/username&gt; &lt;!-- authentication: 身份验证类型 &quot;password-cleartext&quot;: 直接发送明文密码进行验证. 如果没有加密连接协议还要使用明文密码则非常不安全. &quot;password-encrypted&quot;: 对于密码进行加密, 可以是 CRAM-MD5, DIGEST-MD5. 不包含 NTLM. &quot;NTLM&quot;: 使用 NTLM 或 HTLMv2 及后续版本. Windows 所使用的登录方法. &quot;GSSAPI&quot;: 使用 Kerberos 或 GSSAPI, 一种适用于大规模验证需要的单点登录方式. &quot;client-IP-address&quot;: 根据 IP 地址识别用户, 无需用户名和密码, 也不需要进行身份验证. &quot;TLS-client-cert&quot;: 使用客户端证书进行认证, 要求客户端提供 SSL 证书(如果需要, 可让用户自行选择证书后发送). Thunderbird 尚未支持. &quot;OAuth2&quot;: 使用 OAuth2, 仅在部分硬编码服务器上可用. 只能作为备用方法使用. 由于 OAuth2 规范的缺陷, 客户端一般需要发送客户端的凭证密钥到服务商, 这样做往往需要客户端要先在服务商注册客户端并获得服务商的同意. 这就不仅导致电子邮件服务商可以禁止用户使用任意电子邮件客户端, 还导致并不能支持所有的 OAuth2 服务器(这与开源的理念相悖). 最后 Thunderbird 只能将支持 OAuth2 的服务器和密钥硬编码进客户端里. 这也意味着并不能使用任意的 OAuth2 服务器. 只有在客户端中已经被定义好服务器才能使用. 在Thunderbird 中受支持的 OAuth2 服务器参见源代码: &quot;https://searchfox.org/comm-central/source/mailnews/base/src/OAuth2Providers.jsm&quot; &quot;none&quot;: 不进行验证. --&gt; &lt;authentication&gt; password-cleartext&lt;/authentication&gt; &lt;pop3&gt; &lt;!-- 移除以下内容则让客户端或用户自行选择以下参数配置. --&gt; &lt;!-- leaveMessagesOnServer: 是否将邮件保存在服务器上(true/false). --&gt; &lt;leaveMessagesOnServer&gt;true&lt;/leaveMessagesOnServer&gt; &lt;!-- downloadOnBiff: 下载时发送 Biff 通知(?)(true/false). --&gt; &lt;downloadOnBiff&gt;true&lt;/downloadOnBiff&gt; &lt;!-- daysToLeaveMessagesOnServer: 服务器中邮件的保留天数. --&gt; &lt;daysToLeaveMessagesOnServer&gt; 14&lt;/daysToLeaveMessagesOnServer&gt; &lt;!-- checkInterval: 从服务器检查的间隔(分钟数), 仅适用于不允许进行频繁请求检查的服务器. Thunderbird 暂不支持. --&gt; &lt;checkInterval minutes=&quot;15&quot; /&gt; &lt;/pop3&gt; &lt;!-- password: 值可选, 用户的密码. --&gt; &lt;password&gt;&lt;/password&gt; &lt;/incomingServer&gt; &lt;!-- outgoingServer type: 传出服务器类型 --&gt; &lt;outgoingServer type=&quot;smtp&quot;&gt; &lt;!-- hostname: 选定类型所使用的服务器主机名. --&gt; &lt;hostname&gt; smtp.googlemail.com&lt;/hostname&gt; &lt;!-- port: 选定类型所使用的服务器端口号. --&gt; &lt;port&gt;587&lt;/port&gt; &lt;!-- socketType: 连接类型 &quot;plain&quot;: 不加密. &quot;SSL&quot;: SSL 加密. &quot;STARTTLS&quot;: 使用 STARTTLS 进行机会性加密 --&gt; &lt;socketType&gt;STARTTLS&lt;/socketType&gt; &lt;!-- username: 指定用户名. --&gt; &lt;username&gt;%EMAILLOCALPART%&lt;/username&gt; &lt;!-- authentication: 身份验证类型 符合 RFC 2554, RFC 4954 的或其他的身份验证方法, 可用选项参考 incomingServer 标签中的 authentication; 除此之外还可用使用以下的方法: (关于 Thunderbird 对此的兼容性: Thunderbird 3.0 只支持 &quot;plain&quot;, &quot;secure&quot;, &quot;none&quot; 和 &quot;smtp-after-pop&quot;.) &quot;SMTP-after-POP&quot;: 在对 SMTP 进行验证前先对传入服务器进行验证. --&gt; &lt;authentication&gt; password-cleartext&lt;/authentication&gt; &lt;!-- restriction: 限定服务器在客户端上进行连接的条件(如果服务器在 authentication 之外还要求其他的验证选项). &quot;client-IP-address&quot;: 只有用户在特定的网络中才能连接到服务器且服务器才能工作. 如特定 ISP 网络或公司网络. 注意: &lt;authentication&gt;client-IP-address&lt;/&gt; 表示可以在没有任何身份验证的情况下连接并使用服务器. 使用 &lt;authentication&gt;password-cleartext&lt;/&gt; 和 &lt;restriction&gt;client-IP-address&lt;/&gt; 时表示用户需要处于正确的 IP 网络环境中并且应该进行身份验证. 建议不要使用实行这种验证措施的服务器, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=556267&quot;. 尚未实现. 规范(标签元素?)可能会改动. --&gt; &lt;restriction&gt;client-IP-address&lt;/restriction&gt; &lt;!-- 移除以下内容则让客户端或用户自行选择以下参数配置. --&gt; &lt;!-- 是否选择添加此服务器(true/false). --&gt; &lt;addThisServer&gt;true&lt;/addThisServer&gt; &lt;!-- 是否是配置文件中的全局首选服务器(true/false) --&gt; &lt;useGlobalPreferredServer&gt; true&lt;/useGlobalPreferredServer&gt; &lt;!-- password: 值可选, 用户的密码. --&gt; &lt;password&gt;&lt;/password&gt; &lt;/outgoingServer&gt; &lt;!-- 在连接到服务器之前需要用户手动进行配置的选项(比如, 部分邮件服务提供商情况默认关闭 POP3, IMAP 或 SMTP, 需要用户登录 Webamil 手动打开账户设置中的这些选项). 注意: 根据 XML 规范, 部分特殊符号需要进行转义, 如: &quot;&amp;&quot; &gt;&gt; &quot;&amp;amp;&quot; &quot;&lt;&quot; &gt;&gt; &quot;&amp;lt;&quot; &quot;&gt;&quot; &gt;&gt; &quot;&amp;gt;&quot; &quot;&#x27;&quot; &gt;&gt; &quot;&amp;apos;&quot; &quot;&quot;&quot; &gt;&gt; &quot;&amp;quot;&quot; 尚未实现, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=586364&quot;. --&gt; &lt;!-- visiturl 值为显示给用户的 URL. --&gt; &lt;enable visiturl=&quot;https://mail.google.com/mail/?ampshva=1#settings/fwdandpop&quot;&gt; &lt;!-- instruction: 说明文字 --&gt; &lt;instruction&gt;Check &#x27;Enable IMAP and POP&#x27; in Google settings page&lt;/instruction&gt; &lt;!-- instruction lang: 另一种语言的说明文字 --&gt; &lt;instruction lang=&quot;de&quot;&gt;Schalten Sie &#x27;IMAP und POP aktivieren&#x27; auf der Google Einstellungs-Seite an&lt;/instruction&gt; &lt;/enable&gt; &lt;!-- 一个电子邮件服务商用于描述配置文件的网页. 主要用于配置文件维护, 客户端并不使用这里的提供的信息. 并不一定需要完全使用服务商提供的配置, 比如服务商在配置文件中不使用 SSL, 但 SLL 实际可用, 此时就可以选择性配置 SLL. descr 应该包含来自服务商对客户的(多语言)说明. --&gt; &lt;documentation url=&quot;http://www.example.com/help/mail/thunderbird&quot;&gt; &lt;descr lang=&quot;en&quot;&gt;Configure Thunderbird 2.0 for IMAP&lt;/descr&gt; &lt;descr lang=&quot;de&quot;&gt;Thunderbird 2.0 mit IMAP konfigurieren&lt;/descr&gt; &lt;/documentation&gt; &lt;/emailProvider&gt; &lt;!-- 用于同步用户通讯录或联系人方式的配置. 尚未实现. Thunderbird 使用 RFC 6764 实现这类功能的自动发现. --&gt; &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt; &lt;addressBook type=&quot;carddav&quot;&gt; &lt;!-- 用户名 --&gt; &lt;username&gt; %EMAILADDRESS%&lt;/username&gt; &lt;!-- authentication: 身份验证方式, 参见 incomingServer 标签配置. &quot;http-basic&quot;: 使用 HTTP 服务器进行 WWW-Authenticate: Basic 身份验证. &quot;http-digest&quot;: 使用 HTTP 服务器进行 WWW-Authenticate: Digest 身份验证. &quot;OAuth2&quot;: OAuth2 身份验证. 使用和 Email 相同的口令. --&gt; &lt;authentication&gt;http-basic&lt;/authentication&gt; &lt;!-- 指定服务器 URL. --&gt; &lt;serverURL&gt;https://contacts.example.com/remote.php/dav&lt;/serverURL&gt; &lt;/addressBook&gt; &lt;!-- 用于同步用户日历方式的配置. 尚未实现. Thunderbird 使用 RFC 6764 实现这类功能的自动发现. --&gt; &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt; &lt;calendar type=&quot;caldav&quot;&gt; &lt;!-- 用户名 --&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;!-- 身份验证方式, 参见 addressBook 中的身份验证方式. --&gt; &lt;authentication&gt; http-basic&lt;/authentication&gt; &lt;!-- 指定服务器 URL. --&gt; &lt;serverURL&gt;https://calendar.example.com/remote.php/dav&lt;/serverURL&gt; &lt;/calendar&gt; &lt;!-- 用于用户进行文件共享方式的配置. 尚未实现. 可用于 Thunderbird 中的 FileLink 或者在用户桌面创建一个同步文件夹. --&gt; &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt; &lt;fileShare type=&quot;webdav&quot;&gt; &lt;!-- 用户名 --&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;!-- 身份验证方式, 参见 addressBook 中的身份验证方式. --&gt; &lt;authentication&gt;http-basic&lt;/authentication&gt; &lt;!-- 指定服务器 URL. --&gt; &lt;serverURL&gt;https://share.example.com/remote.php/dav&lt;/serverURL&gt; &lt;/fileShare&gt; &lt;!-- 可选, 用于访问 Webmail 的配置. 其中的 URL 值会在标准的网络浏览器中打开. --&gt; &lt;webMail&gt; &lt;!-- Webmail 的登录页面 URL, 用户必须手动输入用户名和密码进行登录. URL 需要使用 HTTPS. --&gt; &lt;loginPage url=&quot;https://mail.example.com/login/&quot; /&gt; &lt;!-- 与 loginAutomaticDOM 相同, 但网站会检查用户是否来自登录页. 会在浏览器中打开登录页面, 获取页面 DOM, 然后自动填写用户名和密码然后触发登录按钮. 登录按钮可能不是 HTML 而是一个 div, 所以触发这类按钮需要发送一个点击事件. URL 需要使用 HTTPS. --&gt; &lt;loginPageInfo url=&quot;https://mail.example.com/login/&quot;&gt; &lt;!-- username: 用户名 填写入 usernameField 中的内容. 格式(包括占位符)与 incomingServer 中的 username 相同. --&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;!-- 允许找到页面中的文本字段并填充它们. id 属性输出给 DOM name 属性. id 和 name 属性必须同时或存在其中一个. 如果存在, 则优先使用 id(例如使用 &quot;getElementByid()&quot;), 如果不存在, 则会尝试按名称查找元素. 不要把这个示例 XML 中的这些 id 当成是通用的, 在使用之前应该要验证格式(例如某些仅使用字符和数字的 id). 如果你还使用 jQuery 这样强大的函数, 还在 XML 的用户名 id 中返回了没有经过检查的代码, 那么它有可能会被执行. --&gt; &lt;usernameField id=&quot;email_field&quot; name=&quot;email&quot; /&gt; &lt;passwordField name=&quot;password&quot; /&gt; &lt;!-- 定义提交按钮, 用于填充完成后触发自动提交. id 和 name 属性参见 username.--&gt; &lt;loginButton id=&quot;submit_button&quot; name=&quot;login&quot; /&gt; &lt;/loginPageInfo&gt; &lt;/webMail&gt; &lt;!-- 尚未实现, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=564043&quot;. --&gt; &lt;!-- 在某些电子邮件服务提供商中, 用户名和密码并不与 IMAP, POP3 或 SMTP 的用户名和密码对应. 比如用户邮件地址是 &quot;Alice@example.com&quot;, 但用于登录电子邮件服务器的用户名是给定的用户 ID 或随机的用户名, 密码也可能同样不与电子邮件服务器的登录密码相同. 此时可以提供一个 inputField 标签, 这可以让客户端向用户显示一个输入框, 从而实现自定义的的用户名或密码. 输入框的提示由标签中的 label 属性定义, 其填充示例是 inputField 标签的值. 用户在对应 inputField 标签中输入的内容会写入到标签属性的 key 之中, 这个用于 key 属性变量值必须是大写字母. 同时被定义的变量可以在 XML 配置中的其他位置使用, 但并不建议用这种办法来让用户定义其他的配置属性(因为这样做的话, 提供该 XML 配置的意义就变得不再那么大, 并且用户体验比在 UI 上手动配置还要差). --&gt; &lt;inputField key=&quot;USERNAME&quot; label=&quot;Username&quot;&gt; 123456&lt;/inputField&gt; &lt;inputField key=&quot;NICKNAME&quot; label=&quot;Nickname&quot;&gt;Alice&lt;/inputField&gt; &lt;!-- 以上配置将生成如下的 UI: Username: [ ] example: 123456 Nickname: [ ] example: Alice 同时, 在配置文件的其他位置就能够使用 &quot;%USERNAME%&quot; 和 &quot;%NICKNAME%&quot; 来引用这两个用户输入的值. --&gt; &lt;!-- clientConfigUpdate: 客户端用于配置文件更新的 URL. --&gt; &lt;clientConfigUpdate url=&quot;https://www.example.com/config/mozilla.xml&quot; /&gt;&lt;/clientConfig&gt; 对于配置文件中的 incomingServer (传入服务器) 和 outgoingServer(传出服务器) 可能出现多次. 但应该按照优先使用顺序来排序, 通常会使用第一个出现的服务器配置, 只有当实际服务器表现不满足配置中要求的选项时(比如配置指定使用 SSL 但实际无法通过 SSL 连接到服务器), 才尝试选择第二个服务器配置. 如果 IMAP 和 POP3 服务器同时存在, 客户端应该同时列出(依旧遵循配置中的顺序显示来表示服务商的偏好), 让用户自行选择需要的服务器协议. 除上面配置文件中提到的, 依据用户在客户端的输入内容, 还存在以下通用占位符(变量): %EMAILADDRESS%: 用户的完整电子邮件地址. %EMAILLOCALPART%: 用户电子邮件地址中 &quot;@&quot; 前的部分. %EMAILDOMAIN%: 用户电子邮件地址中 &quot;@&quot; 后的部分. %REALNAME%: 真实名字(?) 依据这个规范编写的一个典型的 IMAP &amp; POP3 &amp; SMTP 服务器配置应该像这样: config-v1.1.xml1234567891011121314151617181920212223242526272829303132&lt;?xml version=&quot;1.0&quot;?&gt;&lt;clientConfig version=&quot;1.1&quot;&gt; &lt;emailProvider id=&quot;example.com&quot;&gt; &lt;domain&gt;example.com&lt;/domain&gt; &lt;displayName&gt;EXAMPLE Inc&lt;/displayName&gt; &lt;displayShortName&gt;CORG&lt;/displayShortName&gt; &lt;incomingServer type=&quot;imap&quot;&gt; &lt;hostname&gt;imap.example.com&lt;/hostname&gt; &lt;port&gt;994&lt;/port&gt; &lt;socketType&gt;SSL&lt;/socketType&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;authentication&gt;password-cleartext&lt;/authentication&gt; &lt;/incomingServer&gt; &lt;incomingServer type=&quot;pop3&quot;&gt; &lt;hostname&gt;pop3.example.com&lt;/hostname&gt; &lt;port&gt;995&lt;/port&gt; &lt;socketType&gt;SSL&lt;/socketType&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;authentication&gt;password-cleartext&lt;/authentication&gt; &lt;pop3&gt; &lt;leaveMessagesOnServer&gt;true&lt;/leaveMessagesOnServer&gt; &lt;/pop3&gt; &lt;/incomingServer&gt; &lt;outgoingServer type=&quot;smtp&quot;&gt; &lt;hostname&gt;smtp.example.com&lt;/hostname&gt; &lt;port&gt;465&lt;/port&gt; &lt;socketType&gt;SSL&lt;/socketType&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;authentication&gt;password-cleartext&lt;/authentication&gt; &lt;/outgoingServer&gt; &lt;clientConfigUpdate url=&quot;https://autoconfig.example.com/mail/config-v1.1.xml&quot; /&gt; &lt;/clientConfig&gt; 对于不需要的配置如果不是需要保留为空值, 那么应该在配置文件中移除. RFC 6186 RFC 6186 的响应是基于 DNS 的 SRV 记录, 所以实现响应的标准就是 SRV 记录(RFC 2782[4]). SRV 记录标准: 1_Service._Proto.Name TTL Class SRV Priority Weight Port Target 使用 ZONE 进行命名就是: 1_[服务类型]._[协议].[域名名称] [生存时间] IN SRV [优先级] [权重] [端口] [目标服务器] 比如: 1_minecraft._tcp.minecraft.example.com 3600 IN SRV 0 1 19132 minecraft.hoster.com. 部署与应用 依据在协议响应规范中提到的各自的发现行为, 除了 RFC 6186, 实现只需要在对应域名设置 DNS 即可. Autodiscover 和 Autoconfiguration 按照对应需要解析 DNS 并放置配置文件至对应域名的固定路径即可实现部署. Autodiscover 标准实现 编写正确规范的 XML 配置文件. 将电子邮件地址使用的域名解析到一个用于托管配置文件的网络服务器. 如: 1example.com 3600 IN A 1.0.0.1 将配置文件放入网络服务器, 使其路径为: /Autodiscover/Autodiscover.xml. 再次解析邮件地址域名的子域名 autodiscover 到配置文件服务器: 1autodiscover.example.com 3600 IN A 1.0.0.1 将配置文件放入网络服务器, 使其路径为: /Autodiscover/Autodiscover.xml 多个配置文件理论并不会相互覆盖, 因为只会使用最先被发现的的那一个配置文件, 其目的是为了冗余. 但是不同客户端可能存在不同的行为, 所以建议同时设置多个方法且保证配置文件内容相同. 如果当前 DNS 解析状况导致解析已存在或不允许再次占用, 那么应该向后选择不冲突的那一种实现方式. 当然使用 CNAME 也是可以的, 因为会自动到 DNS 中的 CNAME 域名寻找. 跳转实现 从 Autodiscover 响应规范小节可以看出, 配置文件中存在一个 RedirectUrl 的标签, 可以让电子邮件客户端解析到时跳转到下一个网络配置文件路径. 因此可以将当前电子邮件域名的两个用于 Autodiscover 的 DNS 记录解析后在网络服务器对应的路径放入一个只用于重定向跳转的 XML: XML 格式应该为: Autodiscover.xml1234567891011&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt; &lt;Response xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt; &lt;Account&gt; &lt;AccountType&gt;email&lt;/AccountType&gt; &lt;Action&gt;redirectUrl&lt;/Action&gt; &lt;RedirectUrl&gt;https://filesystem.example.com/autodiscover/example.com/Autodiscover.xml&lt;/RedirectUrl&gt; &lt;/Account&gt; &lt;/Response&gt;&lt;/Autodiscover&gt; 这样就能将当前电子邮件域名的配置文件重定向到另一个网络文件路径 这样做的好处是能够将大量的域名电子邮件配置集中在一个网络文件系统中管理, 这个文件系统可以是你的服务商管理也可以是企业的 IT 管理员管理, 作为客户及最终用户可以免去维护 XML 配置的步骤. 如果多个域名都使用了相同的电子邮件服务器或电子邮件服务提供商, 那么这些电子邮件服务器的提供的客户端配置一般都是相同的. 但相比直接使用 CNAME 的跳转发现, 还需要配置一个用于跳转的 XML, 所以其实并不太理想. 这样的跳转实现大多用在 Outlook 本地安装的情况, 在本地设备上一次性使用注册表或者组策略将 Outlook 的 Autodiscover 指向这个跳转文件. 除了使用 HTTP 跳转, 还能跳转到另一个邮箱地址进行发现, XML 格式为: Autodiscover.xml1234567891011&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt; &lt;Response xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt; &lt;Account&gt; &lt;AccountType&gt;email&lt;/AccountType&gt; &lt;Action&gt;redirectAddr&lt;/Action&gt; &lt;RedirectAddr&gt;others@example.net&lt;/RedirectAddr&gt; &lt;/Account&gt; &lt;/Response&gt;&lt;/Autodiscover&gt; 同样的, 这样会让客户端解析到这个 XML 文件后直接跳转到 others@example.net 对这个邮箱地址的 example.net 的域名进行配置文件发现. 相较于直接配置一个 XML 的好处是: 避免跳转 URL 的修改可能无法及时同步到用于本地配置. 所以使用了跳转到另一个邮箱域名进行重新发现. 多种配置模式适用于不同的电子邮件系统架构和组织部署环境, 需要按照需求选择合适自身的方法. Autoconfiguration 标准实现 编写正确规范的 XML 配置文件. 将电子邮件地址使用的域名的子域名 autoconfig 解析到一个用于托管配置文件的网络服务器, 如: 1autoconfig.example.com 3600 IN A 1.0.0.1 将配置文件放入网络服务器, 使其路径为: /mail/config-v1.1.xml. 编写正确规范的 XML 配置文件. 将电子邮件域名直接解析至配置文件服务器: 1example.com 3600 IN A 1.0.0.1 将配置文件放入网络服务器, 使其路径为: /.well-known/autoconfig/mail/config-v1.1.xml 编写正确规范的 XML 配置文件. 将其电子邮件域名的子域名 www 解析至网络服务器, 并将 XML 文件放入该网络文件服务器, 使其为任意便于管理的路径, 如 /autoconfig/example.com/config-v1.1.xml. 为电子邮箱域名添加一个 TXT 记录, 值为上一步放置的 XML 文件完整的 HTTP/HTTPS 网络路径: 1example.com 3600 IN TXT &quot;https://www.example.com/autoconfig/example.com/config-v1.1.xml&quot; 多个配置文件理论并不会相互覆盖, 因为只会使用最先被发现的的那一个配置文件, 其目的是为了冗余. 但是不同客户端可能存在不同的行为, 所以建议同时设置多个方法且保证配置文件内容相同. 如果当前 DNS 解析状况导致解析已存在或不允许再次占用, 那么应该向后选择不冲突的那一种实现方式. 中心化实现 本文将 Autoconfiguration 实现方法中, 需要依赖第三方中心化服务实现自动发现的方式称为 &quot;中心化实现&quot;. 比如这里要提到的 &quot;尝试从 Mozilla 服务器检索配置&quot;. 由 Thunderbird 维护并托管的电子邮件服务商配置文件数据库称为 &quot;ISPDB&quot;, 可免费供给任何用户(客户)使用. 它包含了最多的电子邮件服务商配置, 大多市场份额超过 0.1% 的服务商都包含在内, 几乎囊括了全球 50% 的电子邮件账户. 但要注意, 并不是随便一个服务商就能进入这个数据库, 比如自托管的个人电子邮件服务器就不符合要求. ISPDB 要求: If you are a big ISP (&gt; 100,000 users) providing email addresses solely under a few domains like &quot;example.com&quot; and &quot;example.de&quot;, you may either submit the configuration to the ISPDB or set up a configuration server.[5] &quot;如果您是大型 ISP(&gt; 100,000 个用户), 仅在 &quot;example.com&quot; 和 &quot;example.de&quot; 等几个域下提供电子邮件地址, 您可以将配置提交到 ISPDB 或设置配置服务器.&quot;[5:1] If you are a small company installing Thunderbird on your employees' desktops, you can place a configuration file in the Thunderbird installation folder.[5:2] 如果您是在员工桌面上安装 Thunderbird 的小公司, 您可以将配置文件放在 Thunderbird 安装文件夹中.[5:3] 如果想要为自己的电子邮件服务能够通过 Mozilla 服务器查找到服务器配置, 那么除去需要在客户端支持从 ISPDB 中检索, 还要为该服务的源 Git 仓库提交你的 Autoconfiguration 配置文件: thundernest/autoconfig: The ISPDB, Thunderbird's database of mail configuration files. 源仓库存在两分支的配置文件索引, 一个是 prod 分支, 直接用于 Thunderbird(https://autoconfig.thunderbird.net/autoconfig/v1.1/); 另一个是 master 分支, 用于暂存服务器(https://autoconfig-stage.thunderbird.net/autoconfig/v1.1/). RFC 6186 RFC 6186[2:2] 制定的目的就是为了解决从域名中发现电子邮件服务器配置的问题, SMTP[1:1] 本身存在对 MX 记录进行发现进而查找到对应的服务器主机, 但是现代邮件系统中的 MSA 不使用 MX 记录以及工作端口是 587. 于是这个规范设计来与 RFC 4409 联合使用. 在由 RFC 4409[6] 确立的现代的电子邮件提交系统里, 在用户发送电子邮件时, 需要通过 MSA(Message submission agent, 消息提交代理)进行处理才能传输到邮件服务器或中继服务器. 通常来说, 最终用户使用的传出服务器基本都是 MSA 系统, 这样避免了恶意提交对邮件服务器的侵害, 也能保护用户身份不被盗用. 这个标准中定义了对 MSA, IMAP 和 POP3 进行配置的新 SRV 记录类型, 并且还可以定义优先使用哪一种传入协议. 值得一提的是, RFC 6186 是由 Apple 起草发起的, RFC 4409 主要作者之一是 Qualcomm(高通). 实现标准 MSA 使用如下的 SRV 记录指向 MSA 服务: 1_submission._tcp 3600 IN SRV 0 1 587 mail.example.com. IMAP 使用 imap 标识一个普通 IMAP 服务类型. 对于 STARTTLS 应该是客户端和 IMAP 服务器必须实现的, 但有的服务商可能并不需要使用. 使用 imaps 表示一个使用 TLS 连接的 IMAP 服务器. 使用如下的 SRV 记录指向 IMAP 服务: 1_imap._tcp 3600 IN SRV 0 1 143 imap.example.com. 1_imaps._tcp 3600 IN SRV 0 1 993 imap.example.com. POP3 使用 pop3 标识一个可能会需要客户端使用 STLS[7] 进行连接的普通 POP3 服务器. 使用 pop3s 标识一个使用 TLS 连接的 POP3 服务器. 使用如下的 SRV 记录指向 POP3 服务: 1_pop3._tcp 3600 IN SRV 0 1 110 pop3.example.com. 1_pop3s._tcp 3600 IN SRV 0 1 995 pop3.example.com. 优先级指定 由于 SRV 记录允许为服务指定优先级, 所以如果同时在 DNS 中解析指向了多种服务类型, 那么管理员可以通过指定 SRV 记录优先级表示服务端对服务类型的偏好. SRV 记录与 MX 记录指定优先级的概念相同, 是以优先级数字编号越小代表越优先(权重恰恰相反, 数字越大表示权重越高, 但在 SRV 里优先使用优先级, 如果优先级相同再使用权重判定). 通过设置优先级, 将希望优先被采用的传入服务器配置记录使其小于其他的服务记录实现服务端偏好设置. 优先使用 IMAP 而不是 POP3: 1_imap._tcp 3600 IN SRV 0 1 143 imap.example.com. 1_pop3._tcp 3600 IN SRV 10 1 110 pop3.example.com. 通过将 SRV 记录中的目标服务器值设置为 . 来表示该域完全不支持此服务. 如果存在这种记录值, 则电子邮件客户端应该认为这种服务器不可用, 并继续使用 SRV 记录进行确定. 比如, 通过以下的记录值 1_imap._tcp 3600 IN SRV 0 0 0 . 1_imaps._tcp 3600 IN SRV 0 1 993 imap.example.com. 1_pop3._tcp 3600 IN SRV 0 0 0 . 1_pop3s._tcp 3600 IN SRV 10 1 995 pop3.example.com. 上面的记录值指定本域中存在使用 TLS 和不使用 TLS 的 IMAP 服务和 POP3 服务(带 s 和不带 s), 但是不使用 TLS 的 IMAP 和 POP3 服务不可用. 且使用 IMAPs 服务的优先级高于使用 POP3s, 因为 IMAPs 的优先级 0 要比 POP3s 的优先级 10 要小. 现状与未来 到此为止, 本文就介绍完目前所有去中心化且主流的实现自动发现方式了. 那么现在在电子邮件中的自动发现是一个什么样的情况? 未来呢? 现状 目前文中提到的自动发现, 对于 Autodiscover, 在 Exchange Online 所有的 Exchange Online 服务都需要组织管理员单独为域名设置一个 autodiscover 别名记录指向微软的 Outlook 服务器 autodiscover.outlook.com 并且由于关闭了基础身份验证, 还要用户先通过身份验证才能检索到 Microsoft 服务器中的配置; Exchange Server 目前依旧能够通过本地的 Active Directory 和 IIS 服务器发现到配置文件. 就目前来说, Autodiscover 还在是 Microsoft 自己设计能尚且还在运行的标准, 其他的非 Exchange 电子邮件服务器也能通过它的标准的部分来兼容实现自动发现. 对于 Autoconfiguration, 这是 Mozilla 的 Thunderbird 采用的一种发现标准, 比起 Autodiscover 为了兼容更多服务商的情况给出了很多 Autodiscover 中没有的选项. 但有的选项 Thunderbird 自己都没有实现支持, 或者还是存在疑问的地方. 最后衍生出了 Mozilla 自己的中心化 ISPDB 服务器, 主要的目的也是为了兼容. 而 RFC 6186 目前还是处于 &quot;拟议标准(Proposed Standard)&quot; 状态, 也就是说这个标准刚通过起草阶段被 IETF 审阅后公布, 而如果已经做到被互联网广泛采用, 它应该已经被标记上 &quot;标准追踪(Standards Track)&quot; 状态. 它首次发表于 2011 年, 最后一次修改是 2015 年. 对于这些针对服务端份规范, 尚且处于不成熟不统一的状态. 那么客户端的规范呢? 对于目前的电子邮件客户端, 大部分由电子邮件服务商直接提供的本地客户端都优先使用的是内置的配置数据库, 对于不存在于内置数据库中的还是会要求用户自己输入配置, 并不会尝试去通过以上的标准进行自动发现. 并且对于这些专有客户端, 用户也无从得知它的自动发现支持状态和实现标准. 除了专有客户端, 大部分开源的或其他开放的电子邮件客户端几乎都支持其中一种或多种自动发现, 也内置预设配置数据. 实在不济的也还能通过账号系统保存上一次得到的配置数据, 或者能够导入导出账号列表的数据. 对于电子邮件服务提供商, 大部分都不会提供配置自动发现的指引, 甚至不会在 DNS 记录中提供官方的配置. 就算一万个用户使用十万个域名, 他们使用的服务端配置都是相同, 但服务商依旧不会提供自动发现, 甚至不提供指引, 当然这里也不是指所有的代托管服务商, 有少部分的服务商具有完整的自动发现指引, 有的还提供官方的自动发现服务器, 能够让客户(用户)通过自己的域名 CNAME 实现支持. 对于用户, 本该并不需要知道什么是自动发现, 这应该是电子邮件管理员和电子邮件服务商提前配置好的. 但是由于客户端的问题, 或是管理员和服务商根本没有考虑自动发现, 导致每次登录新的客户端都需要手动配置至少两个(传入与传出)服务器的参数才能使用, 而且如果不是专业用户, 他们甚至会面对这些参数手无足措, 转而把问题转移到服务商和管理员身上(大部分不支持自动发现的客户端反而却能幸免于难). 那么, 又到了给不懂计算机的朋友修电脑的时候了, 但要面对的可能是你的用户. 但是, 即使是精明的用户, 也并不是都想或都了解过自动发现. 而是更愿意使用更成熟的, 内建客户端账号系统的电子邮件客户端, 将这些繁杂的服务器配置数据保存在第三方手中(包括密码). 未来 笔者期望的未来是用 RFC 实现的互联网标准, 但不会是目前的 RFC 6186, 因为它并不能解决除了下发配置之外的问题, 至少也要如 Mozilla Thunderbird 的 Autoconfiguration 中的一样, 为身份验证和个性化给出支持, 对于现代的 MSA 系统大部分都已经开始采用 API 与服务器交互, 而不是还在使用服务器通讯协议, MSA 的 587 端口同样难以抵御泛滥的网络攻击, 部分的服务商已经开始使用更进一步的身份验证保护 MSA 系统甚至禁止基本身份验证, 这其中包括现在多方强调的多因素身份验证以及零信任模型. 现代的电子邮件系统已经将用户和攻击者隔离在服务器之外很远的地方, 用户能通过身份验证表示攻击者也同样能, 只要还存在暴露的门, 那就一定会有乘虚而入的家伙. 所以, 如果电子邮件系统在未来依旧不会消亡, 那么对于自动发现应该实现的, 应该包括服务器安全策略和客户端策略, 而不是简单的指向一个端口和服务器. 又或者, 在 MSA 系统和用户之间再叠一层专用于身份验证和威胁防御的专用系统? 也许会叫 &quot;用户认证代理(User Verification Agent, UVA)&quot;?🥱 参考资料 Exchange Server中的自动发现服务 | Microsoft Learn Exchange 自动发现 | Microsoft Learn 计划自动在 Outlook 2010 中配置用户帐户 | Microsoft Learn Microsoft Remote Connectivity Analyzer Thunderbird:Autoconfiguration - MozillaWiki Thunderbird:Autoconfiguration:DNSBasedLookup - MozillaWiki Thunderbird Autoconfiguration - Config file format IMAP and ActiveSync Configuration for Outlook using Autodiscovery - Zoho Mail Autodiscover: Some quick methods to get it working - HowTo-Outlook Email Glossary – Validity Help Center 什么是 DNS SRV 记录？ | Cloudflare Zone file - Wikipedia RFC 2821 - Simple Mail Transfer Protocol (IETF) ↩︎ ↩︎ RFC 6186 - Use of SRV Records for Locating Email Submission/Access Services (IETF) ↩︎ ↩︎ ↩︎ thundernest/autoconfig: The ISPDB, Thunderbird's database of mail configuration files. (GitHub) ↩︎ RFC 2782 - A DNS RR for specifying the location of services (DNS SRV) (IETF) ↩︎ Thunderbird Autoconfiguration (Ben Bucksch) ↩︎ ↩︎ ↩︎ ↩︎ RFC 4409 - Message Submission for Mail (IETF) ↩︎ RFC 2595 - Using TLS with IMAP, POP3 and ACAP (IETF) ↩︎","summary":"本文已合并至邮件人百科中, 后续更新请前往: https://www.mailer.su/wiki/configure/autodiscovery/ 前言 电子邮件属于一种去中心化的通讯协议, 核心的电子邮件标准只有出入站协议的定义, 并没有定义一个电子邮件服务器如何为电子邮件客户端下发服务器配置, 所以在 Autodiscover 和 Autoconfiguration 之前, 大部分的电子邮件客户端都需要用户手动填写服务器传入和传出配置, 而获取这些配置往往需要用户自己到邮件服务器后台, Webmail 或者提供托管服务的服务商文档中搜寻, 这样的做法等于用户配置与服务器配置并不是自动同步, 在邮件服务器出现更改(迁移某部分的域名, 加密协议或端口)并不会及时通知客户端中跟随做出更改, 从而造成服务端改动后客户端的收发问题. 除去邮件服务器和用户之间的不同步问题, 没有那么多经验用户自己在首次配置客户端的时候可能会对那些参数感到莫名其妙: 为什么我的 SMTP 服务器和 IMAP 服务器域名不是我的邮件地址域名? 为什么要使用 STARTTLS 而不是 TLS? 为什么我的密码不是我的 Webmail 密码? 然后就是: 为什么我每次都要自己手动填这些乱七八糟的参数才能用? 为什么 Webmail 就可以一键登录? (🤦‍♂️) 本文主要叙述基于 DNS 查询, HTTP 请求和静态配置文件实现的 Autodiscover 和 Autoconfiguration. 不包含基于本地的如: 环境变量, 注册表, 网络部署和用户目录等; 以及基于远程网络服务器的: 动态配置文件, 身份验证和多配置文件等. 区别与实现 所以, 为了解决以上的 &quot;电子邮件客户端自动配置邮件服务器参数&quot; 的问题, 引出了 &quot;Autodiscover&quot; 和 &quot;Autoconfiguration&quot;, 顾名思义 Autodiscover 即为 &quot;自动发现&quot;, Autoconfiguration 即为 &quot;自动配置&quot;. 两者大同小异, 区别在于前者 Autodiscover 主要指的是存在于 Microsoft 开发的电子邮件系统 Exchange 中使用到的另一个也是 Microsoft 开发的同步协议 Exchange ActiveSync 中附带的为电子邮件客户端下发邮件服务器配置的功能; 后者主要是由 Thunderbrid 定义的一种为了实现前者类似功能的规范. Autodiscover Autodiscover 目前存在于 Exchange Server 和 Exchange Online 中, 由于 Microsoft 逐渐推广禁止使用基本身份验证, 所以这部分功能可能未来只会在 Exchange Server 中见到了, 目前 Exchange Online 主要从 Active Directory 用户目录中获取配置, 之后才会尝试使用传统的 Autodiscover 去发现配置. 发起 Autodiscover 的基本步骤: 从 Exchange Activesync 中请求配置: 尝试从 URL 中请求配置: https://example.com/Autodiscover/Autodiscover.xml 尝试从 URL 中请求配置: https://autodiscover.example.com/Autodiscover/Autodiscover.xml 尝试从主机名 Autodiscover DNS 中寻找重定向 URL: autodiscover.example.com &gt;&gt; CNAME &gt;&gt; autodiscover.exchangeserver.com &gt;&gt; HTTP 301/302 &gt;&gt; autodiscover.mailhoster.com 尝试从重定向 URL 中请求配置: https://autodiscover.mailhoster.com/Autodiscover/Autodiscover.xml 尝试从主机名 Autodiscover DNS 中寻找重定向 URL: autodiscover.mailhoster.com ... Autodiscover 就是不断从主机名和 URI 中寻找配置, 一层一层从上到下尝试寻找, 经过多层重定向之后最终找到文件名为 Autodiscover.xml 配置文件, 如果多次重定向之后还是没找到, 那客户端读取不到配置文件就还是需要手动配置了. Autoconfiguration Autoconfiguration 来自 Thunderbrid, 使用了 Autodiscover 类似的实现方法, 但步骤和规范上有些区别. 发起 Autoconfiguration 的基本步骤: 从 Thunderbrid 在本地磁盘上的安装目录中读取配置: C:/Program Files/Mozilla/Thunderbird/isp/example.com.xml 从电子邮件服务商获取配置: 尝试请求 http://autoconfig.emailaddressdomain/mail/config-v1.1.xml?emailaddress=username@example.com 或 http://example.com/.well-known/autoconfig/mail/config-v1.1.xml 文件路径. 尝试从 DNS 记录中寻找配置: 尝试查询用户名域名 example.com 中存在内容为 https://www.example.com/mozilla.xml 的 TXT 记录依据记录进行请求配置文件未应用. 另一种提议, 也是基于 DNS未应用: 尝试解析用户名域名中的 MX 记录, 若存在多个 MX 记录则取最高优先级的记录; 如果不存在 MX 记录, 则查询并使用用户名域名中符合 SMTP 协议(RFC 2821[1])的 A 记录. 依据上方的查询结果, 从结果主机名中查询存在 mailconf=https://... 形式的 TXT 记录, 依据记录寻找配置文件, 如果 URL 错误则继续向下寻找. 如果查询不到有效的 TXT 记录则选择第一条有效的 TXT 记录进行查找配置文件. 在对配置文件进行 GET 请求时中附带初始请求的用户邮件地址(mailto:username@example.com)为 referrer, 用于实现可能存在的动态配置文件下发. 解析配置文件并让用户选择需要的协议和其他建议配置. 继续进行登录验证. 尝试解析用户名域名 DNS 从中寻找 SRV 记录获取配置(RFC 6186[2])未应用. 尝试从 Mozilla 服务器检索配置: 从 autoconfig.thunderbird.net[3] 请求用户名域名中的服务商配置文件, 如: https://autoconfig.thunderbird.net/v1.1/google.com. 如果找不到公共配置文件, 则尝试使用一些通配字符和常用端口进行猜测, 如 imap.example.com, pop.example.com, pop3.example.com, smtp.example.com 或 mail.example.com. 继续失败, 则直接让用户进行手动配置. 以上标记有 未应用 的都代表存在这些提议, 但没有实际应用到 Thunderbird 邮件客户端和服务器系统中. RFC 6186 请直接阅读下一节中有关 RFC 6186 的响应规范. 协议响应规范 从上面小节对两种自动寻找配置的方式可以看出, 它们都依赖于一个扩展名为 .xml 及使用 XML 标记语言的编写的二进制文件. 两种实现方式虽然在过程上大同小异, 但它们要使用到的最终配置文件是存在差异的, 并不能通用. Microsoft 在 Exchange Server 多个版本也修改了对于这部分静态文件的定义和规范, 直到现在的 Exchange Online 加入身份验证之后基本上就无法用于静态文件的实现, 所以需要向前使用还能单纯依靠静态文件就能被客户端解析的规范. 如果要一同在服务器实现, 那么需要分别针对两种方式编写对应的 XML 源文件. 此外上面还稍微提到过, 还存在一个拟议中的规范 RFC 6186[2:1], 并不依赖于特定域名下的配置文件, 而是使用完全基于 DNS 的 SRV 记录查询实现配置发现. Autodiscover 以下是 Microsoft 提供的适用 Outlook 2010 邮件客户端的服务端配置文件模板: 计划自动在 Outlook 2010 中配置用户帐户 | Microsoft Learn 以下配置文件注释经过本人翻译. Autodiscover.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt; &lt;!-- Response: 必须本标签用于指示该 XML 是一个 Autodiscover 响应.--&gt; &lt;Response xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt; &lt;!-- User: 可选本标签用于提供用户信息, Autodiscover 必须使用 UTF-8 编码.--&gt; &lt;User&gt; &lt;!-- DisplayName: 可选服务端使用这个标签提供一个默认显示名称. 客户端可以选择接受或自定义, 这可以免除用户所需要的输入从而节省时间.--&gt; &lt;DisplayName&gt;John Doe&lt;/DisplayName&gt; &lt;/User&gt; &lt;!-- Account: 必须本标签用于指定账户类型, 如电子邮件(Email), 新闻组(Newsgroup)和 SIP 服务器等.--&gt; &lt;Account&gt; &lt;!-- AccountType: 必须本标签中的值用于指定账户类型.可用值:email: 表示该标签内用于定义电子邮件服务器的配置.nntp: 表示该标签内用于定义 NNTP 服务器的配置(Outlook 2007 没有使用).--&gt; &lt;AccountType&gt;email | nntp&lt;/AccountType&gt; &lt;!-- Action: 必须本标签中的值用于定义直接使用本文件的配置或重定向至另一个可提供 Autodiscover 配置的网络服务器.可用值:redirectUrl: 如果指定使用这个值, 那么在 RedirectUrl 标签中必须要包含要定向到的 Autodiscover 配置路径(http/https). 为了防止服务端的重定向将客户端置入请求循环中, 客户端应在 10 次重定向后停止继续重定向.redirectAddr: 如果指定使用这个值, 表示客户端应当使用 RedirectAddr 标签中的电子邮件地址进行 Autodiscover 配置查找, 而不是使用当前的用户电子邮箱地址.settings: 如果指定使用了这个值, 表示本 XML 文件中包含进行账户配置所需要的信息. 客户端将使用 Protocol 标签下的服务器配置进行配置客户端.--&gt; &lt;Action&gt;redirectUrl | redirectAddr | settings&lt;/Action&gt; &lt;!-- RedirectUrl: 如果 Action 标签值指定为 &quot;redirectUrl&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.本标签的值应是一个 http 或 https 协议的 URL, 用于客户端使用该值进一步获取配置.--&gt; &lt;RedirectUrl&gt;redirect.URL&lt;/RedirectUrl&gt; &lt;!-- RedirectAddr: 如果 Action 标签值指定为 &quot;redirectAddr&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.本标签的值应是一个电子邮件地址, 用于客户端使用该地址重新查找 Autodiscover 配置.--&gt; &lt;RedirectAddr&gt;email@address&lt;/RedirectAddr&gt; &lt;!-- Image: 可选本标签的值是一张网络 JPG 图片, 用作配置电子邮件服务提供商的品牌标志(Outlook 2007 没有使用).--&gt; &lt;Image&gt;http://path.to.image.com/image.jpg&lt;/Image&gt; &lt;!-- ServiceHome: 可选本标签的值是一个指向电子邮件服务提供商主页的 URL, 客户端可以选择是否将这个值显示给用户(Outlook 2007 没有使用).--&gt; &lt;ServiceHome&gt;http://web.page.com&lt;/ServiceHome&gt; &lt;!-- Protocol: 如果 Action 标签值指定为 &quot;settings&quot;, 则本标签值为必须; 如果未指定, 则应该移除本标签.本标签只能包含一个账户类型的配置信息, 多个 Protocol 标签可按照服务端的偏好进行配置, 但客户端可以自行选择偏好协议.--&gt; &lt;Protocol&gt; &lt;!-- Type: 必须.本标签的值指定使用何种账户类型.可选值:POP3: 指定连接到服务器的协议使用 POP3. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.SMTP: 指定连接到服务器的协议使用 SMTP. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.IMAP: 指定连接到服务器的协议使用 IMAP. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.DAV: 指定连接到服务器的协议使用 DAV. 仅适用于 AccountType 标签值为 &quot;email&quot; 时.WEB: 指定客户端使用 Server 标签中的 URL 从浏览器访问电子邮件. 仅适用于 AccountType 标签值为 &quot;email&quot; 时(Outlook 2007 没有使用).NNTP: 指定连接到服务器的协议使用 NNTP. 仅适用于 AccountType 标签值为 &quot;nntp&quot; 时(Outlook 2007 没有使用).--&gt; &lt;Type&gt;POP3 | SMTP | IMAP | DAV | WEB | NNTP&lt;/Type&gt; &lt;!-- ExpirationDate: 可选.本标签的值指定一个该配置文件最后有效的日期, 在该日期过后客户端应该自动重新查找 Autodiscover 配置. 如果未指定则默认为不过期.--&gt; &lt;ExpirationDate&gt;YYYYMMDD&lt;/ExpirationDate&gt; &lt;!-- TTL: 可选.本标签的值指定该配置文件的有效时间, 单位为小时. 自首次查找该配置文件起算, 经过本标签值的时间后客户端将再次查找 Autodiscover 配置. 如果本标签未指定值, 则默认使用 1 小时的 TTL.--&gt; &lt;TTL&gt;168&lt;/TTL&gt; &lt;!-- Server: 必须.本标签的值指定了与上方 Type 标签值中账户类型对应的服务器地址.对于 Type 标签值为 POP3, SMTP, IMAP 或 NNTP 的, 该值应是一个主机名或 IP 地址.对于 Type 标签值为 DAV 和 WEB 的, 该值应是一个 URL.--&gt; &lt;Server&gt;mail.contoso.com&lt;/Server&gt; &lt;!--服务器的 IP 地址或域名--&gt; &lt;!-- Port: 可选.本标签的值指定对 Server 标签值使用的端口号. 如果未指定则根据 Type 标签值使用默认设置. 如果 Server 标签值是一个 URL, 则不使用本标签值.--&gt; &lt;Port&gt;110&lt;/Port&gt; &lt;!-- LoginName: 可选.本标签值指定用户的登录名. 如果未指定, 则默认使用用户设置的电子邮件地址 &quot;@&quot; 前的字串符. 如果本标签值指定为一个域名, 则使用 [用户名]@[域名] 的格式进行配置, 如 &quot;Alice@example.com&quot;.--&gt; &lt;LoginName&gt;Alice&lt;/LoginName&gt; &lt;!-- DomainRequired: 可选. 默认为 off.本标签指定身份验证过程中是否需要域名, 如果设置为 on, 则在身份验证过程中需要域名, off 则为不需要. 如果未在 LoginName 标签中指定一个域名或未指定 LoginName 标签的值, 那么需要用户手动输入域名才能进行身份验证.--&gt; &lt;DomainRequired&gt;on | off&lt;/DomainRequired&gt; &lt;!-- DomainName: 可选.本标签值指定用户域名. 如果未指定值, 则默认将用户电子邮件地址作为 UPN 的格式([用户名]@[域名])来使用. 如 Alic@example.com.--&gt; &lt;DomainName&gt;&lt;/DomainName&gt; &lt;!-- SPA: (Secure Password Authentication 安全密码验证) 可选.本标签值指定是否需要安全密码验证. 如果未指定, 则默认为 on.--&gt; &lt;SPA&gt;on | off&lt;/SPA&gt; &lt;!-- SSL: 可选.本标签值指定是否需要进行安全登录. 如果未指定, 则默认为 on.--&gt; &lt;SSL&gt;on | off&lt;/SSL&gt; &lt;!-- AuthRequired: 可选.本标签值指定是否需要身份验证(基于密码). 如果未指定, 则默认为 on.--&gt; &lt;AuthRequired&gt;on | off&lt;/AuthRequired&gt; &lt;!-- 可选: 是否需要身份验证. --&gt; &lt;!-- UsePOPAuth: 可选.本标签值仅在 Type 标签值为 &quot;SMTP&quot; 时可用. 用于指定 POP3 类型的账户使用的身份验证信息是否也将用于 SMTP.--&gt; &lt;UsePOPAuth&gt;on | off&lt;/UsePOPAuth&gt; &lt;!-- SMTPLast: 可选. 默认为 off.本标签值指定在通过 SMTP 发件前是否需要先下载电子邮件. 通常 SMTP 下载邮件时都会检查身份验证是否成功, 所以一般为需要.--&gt; &lt;SMTPLast&gt;on | off&lt;/SMTPLast&gt; &lt;/Protocol&gt; &lt;/Account&gt; &lt;/Response&gt;&lt;/Autodiscover&gt; 使用这个规范编写的一个典型的 IMAP &amp; POP3 &amp; SMTP 服务器配置应该像这样: Autodiscover.xml1234567891011121314151617181920212223242526272829303132333435363738394041&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;&lt;Autodiscover xmlns=&quot;http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt; &lt;Response xmlns=&quot;http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt; &lt;Account&gt; &lt;AccountType&gt;email&lt;/AccountType&gt; &lt;Action&gt;settings&lt;/Action&gt; &lt;Protocol&gt; &lt;Type&gt;IMAP&lt;/Type&gt; &lt;Server&gt;imap.example.com&lt;/Server&gt; &lt;Port&gt;993&lt;/Port&gt; &lt;DomainRequired&gt;off&lt;/DomainRequired&gt; &lt;LoginName&gt;&lt;/LoginName&gt; &lt;SPA&gt;off&lt;/SPA&gt; &lt;SSL&gt;on&lt;/SSL&gt; &lt;AuthRequired&gt;on&lt;/AuthRequired&gt; &lt;/Protocol&gt; &lt;Protocol&gt; &lt;Type&gt;POP3&lt;/Type&gt; &lt;Server&gt;pop3.example.com&lt;/Server&gt; &lt;Port&gt;995&lt;/Port&gt; &lt;DomainRequired&gt;off&lt;/DomainRequired&gt; &lt;LoginName&gt;&lt;/LoginName&gt; &lt;SPA&gt;off&lt;/SPA&gt; &lt;SSL&gt;on&lt;/SSL&gt; &lt;AuthRequired&gt;on&lt;/AuthRequired&gt; &lt;/Protocol&gt; &lt;Protocol&gt; &lt;Type&gt;SMTP&lt;/Type&gt; &lt;Server&gt;smtp.example.com&lt;/Server&gt; &lt;Port&gt;465&lt;/Port&gt; &lt;DomainRequired&gt;off&lt;/DomainRequired&gt; &lt;LoginName&gt;&lt;/LoginName&gt; &lt;SPA&gt;off&lt;/SPA&gt; &lt;Encryption&gt;SSL&lt;/Encryption&gt; &lt;AuthRequired&gt;on&lt;/AuthRequired&gt; &lt;UsePOPAuth&gt;off&lt;/UsePOPAuth&gt; &lt;SMTPLast&gt;off&lt;/SMTPLast&gt; &lt;/Protocol&gt; &lt;/Account&gt; &lt;/Response&gt;&lt;/Autodiscover&gt; 由于是使用标准的电子邮件协议, 而 Microsoft 提供的给 Exchange 服务器中的协议模板中有部分配置项目并不是必须的, 比如 UPN 就只是 Microsoft 专门用来指定一个账户个体的简称, 实际上和电子邮箱地址并没有区别, 两者几乎是一模一样的定义. Autoconfiguration 从 MozillaWiki 中可以得到配置格式: Thunderbird:Autoconfiguration:ConfigFileFormat - MozillaWiki 以下配置文件注释经过本人翻译. config-v1.1.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247&lt;?xml version=&quot;1.0&quot;?&gt;&lt;clientConfig version=&quot;1.1&quot;&gt; &lt;!-- 电子邮件服务提供商 --&gt; &lt;emailProvider id=&quot;example.com&quot;&gt; &lt;!-- domain: 域名 --&gt; &lt;domain&gt;example.com&lt;/domain&gt; &lt;domain&gt;example.net&lt;/domain&gt; &lt;!-- displayName: 显示在客户端上的名称. --&gt; &lt;displayName&gt;Google Mail&lt;/displayName&gt; &lt;!-- displayShortName: 显示的简称. --&gt; &lt;displayShortName&gt;GMail&lt;/displayShortName&gt; &lt;!-- incomingServer type: 传入服务器类型 &quot;imap&quot;: IMAP &quot;pop3&quot;: POP3 --&gt; &lt;incomingServer type=&quot;pop3&quot;&gt; &lt;!-- hostname: 选定类型所使用的服务器主机名. --&gt; &lt;hostname&gt; pop.example.com&lt;/hostname&gt; &lt;!-- port: 选定类型所使用的服务器端口号. --&gt; &lt;port&gt;995&lt;/port&gt; &lt;!-- socketType: 连接类型 &quot;plain&quot;: 不加密. &quot;SSL&quot;: SSL 加密. &quot;STARTTLS&quot;: 使用 STARTTLS 进行机会性加密 --&gt; &lt;socketType&gt;SSL&lt;/socketType&gt; &lt;!-- username: 指定用户名. --&gt; &lt;username&gt;%EMAILLOCALPART%&lt;/username&gt; &lt;!-- authentication: 身份验证类型 &quot;password-cleartext&quot;: 直接发送明文密码进行验证. 如果没有加密连接协议还要使用明文密码则非常不安全. &quot;password-encrypted&quot;: 对于密码进行加密, 可以是 CRAM-MD5, DIGEST-MD5. 不包含 NTLM. &quot;NTLM&quot;: 使用 NTLM 或 HTLMv2 及后续版本. Windows 所使用的登录方法. &quot;GSSAPI&quot;: 使用 Kerberos 或 GSSAPI, 一种适用于大规模验证需要的单点登录方式. &quot;client-IP-address&quot;: 根据 IP 地址识别用户, 无需用户名和密码, 也不需要进行身份验证. &quot;TLS-client-cert&quot;: 使用客户端证书进行认证, 要求客户端提供 SSL 证书(如果需要, 可让用户自行选择证书后发送). Thunderbird 尚未支持. &quot;OAuth2&quot;: 使用 OAuth2, 仅在部分硬编码服务器上可用. 只能作为备用方法使用. 由于 OAuth2 规范的缺陷, 客户端一般需要发送客户端的凭证密钥到服务商, 这样做往往需要客户端要先在服务商注册客户端并获得服务商的同意. 这就不仅导致电子邮件服务商可以禁止用户使用任意电子邮件客户端, 还导致并不能支持所有的 OAuth2 服务器(这与开源的理念相悖). 最后 Thunderbird 只能将支持 OAuth2 的服务器和密钥硬编码进客户端里. 这也意味着并不能使用任意的 OAuth2 服务器. 只有在客户端中已经被定义好服务器才能使用. 在Thunderbird 中受支持的 OAuth2 服务器参见源代码: &quot;https://searchfox.org/comm-central/source/mailnews/base/src/OAuth2Providers.jsm&quot; &quot;none&quot;: 不进行验证. --&gt; &lt;authentication&gt; password-cleartext&lt;/authentication&gt; &lt;pop3&gt; &lt;!-- 移除以下内容则让客户端或用户自行选择以下参数配置. --&gt; &lt;!-- leaveMessagesOnServer: 是否将邮件保存在服务器上(true/false). --&gt; &lt;leaveMessagesOnServer&gt;true&lt;/leaveMessagesOnServer&gt; &lt;!-- downloadOnBiff: 下载时发送 Biff 通知(?)(true/false). --&gt; &lt;downloadOnBiff&gt;true&lt;/downloadOnBiff&gt; &lt;!-- daysToLeaveMessagesOnServer: 服务器中邮件的保留天数. --&gt; &lt;daysToLeaveMessagesOnServer&gt; 14&lt;/daysToLeaveMessagesOnServer&gt; &lt;!-- checkInterval: 从服务器检查的间隔(分钟数), 仅适用于不允许进行频繁请求检查的服务器. Thunderbird 暂不支持. --&gt; &lt;checkInterval minutes=&quot;15&quot; /&gt; &lt;/pop3&gt; &lt;!-- password: 值可选, 用户的密码. --&gt; &lt;password&gt;&lt;/password&gt; &lt;/incomingServer&gt; &lt;!-- outgoingServer type: 传出服务器类型 --&gt; &lt;outgoingServer type=&quot;smtp&quot;&gt; &lt;!-- hostname: 选定类型所使用的服务器主机名. --&gt; &lt;hostname&gt; smtp.googlemail.com&lt;/hostname&gt; &lt;!-- port: 选定类型所使用的服务器端口号. --&gt; &lt;port&gt;587&lt;/port&gt; &lt;!-- socketType: 连接类型 &quot;plain&quot;: 不加密. &quot;SSL&quot;: SSL 加密. &quot;STARTTLS&quot;: 使用 STARTTLS 进行机会性加密 --&gt; &lt;socketType&gt;STARTTLS&lt;/socketType&gt; &lt;!-- username: 指定用户名. --&gt; &lt;username&gt;%EMAILLOCALPART%&lt;/username&gt; &lt;!-- authentication: 身份验证类型 符合 RFC 2554, RFC 4954 的或其他的身份验证方法, 可用选项参考 incomingServer 标签中的 authentication; 除此之外还可用使用以下的方法: (关于 Thunderbird 对此的兼容性: Thunderbird 3.0 只支持 &quot;plain&quot;, &quot;secure&quot;, &quot;none&quot; 和 &quot;smtp-after-pop&quot;.) &quot;SMTP-after-POP&quot;: 在对 SMTP 进行验证前先对传入服务器进行验证. --&gt; &lt;authentication&gt; password-cleartext&lt;/authentication&gt; &lt;!-- restriction: 限定服务器在客户端上进行连接的条件(如果服务器在 authentication 之外还要求其他的验证选项). &quot;client-IP-address&quot;: 只有用户在特定的网络中才能连接到服务器且服务器才能工作. 如特定 ISP 网络或公司网络. 注意: &lt;authentication&gt;client-IP-address&lt;/&gt; 表示可以在没有任何身份验证的情况下连接并使用服务器. 使用 &lt;authentication&gt;password-cleartext&lt;/&gt; 和 &lt;restriction&gt;client-IP-address&lt;/&gt; 时表示用户需要处于正确的 IP 网络环境中并且应该进行身份验证. 建议不要使用实行这种验证措施的服务器, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=556267&quot;. 尚未实现. 规范(标签元素?)可能会改动. --&gt; &lt;restriction&gt;client-IP-address&lt;/restriction&gt; &lt;!-- 移除以下内容则让客户端或用户自行选择以下参数配置. --&gt; &lt;!-- 是否选择添加此服务器(true/false). --&gt; &lt;addThisServer&gt;true&lt;/addThisServer&gt; &lt;!-- 是否是配置文件中的全局首选服务器(true/false) --&gt; &lt;useGlobalPreferredServer&gt; true&lt;/useGlobalPreferredServer&gt; &lt;!-- password: 值可选, 用户的密码. --&gt; &lt;password&gt;&lt;/password&gt; &lt;/outgoingServer&gt; &lt;!-- 在连接到服务器之前需要用户手动进行配置的选项(比如, 部分邮件服务提供商情况默认关闭 POP3, IMAP 或 SMTP, 需要用户登录 Webamil 手动打开账户设置中的这些选项). 注意: 根据 XML 规范, 部分特殊符号需要进行转义, 如: &quot;&amp;&quot; &gt;&gt; &quot;&amp;amp;&quot; &quot;&lt;&quot; &gt;&gt; &quot;&amp;lt;&quot; &quot;&gt;&quot; &gt;&gt; &quot;&amp;gt;&quot; &quot;&#x27;&quot; &gt;&gt; &quot;&amp;apos;&quot; &quot;&quot;&quot; &gt;&gt; &quot;&amp;quot;&quot; 尚未实现, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=586364&quot;. --&gt; &lt;!-- visiturl 值为显示给用户的 URL. --&gt; &lt;enable visiturl=&quot;https://mail.google.com/mail/?ampshva=1#settings/fwdandpop&quot;&gt; &lt;!-- instruction: 说明文字 --&gt; &lt;instruction&gt;Check &#x27;Enable IMAP and POP&#x27; in Google settings page&lt;/instruction&gt; &lt;!-- instruction lang: 另一种语言的说明文字 --&gt; &lt;instruction lang=&quot;de&quot;&gt;Schalten Sie &#x27;IMAP und POP aktivieren&#x27; auf der Google Einstellungs-Seite an&lt;/instruction&gt; &lt;/enable&gt; &lt;!-- 一个电子邮件服务商用于描述配置文件的网页. 主要用于配置文件维护, 客户端并不使用这里的提供的信息. 并不一定需要完全使用服务商提供的配置, 比如服务商在配置文件中不使用 SSL, 但 SLL 实际可用, 此时就可以选择性配置 SLL. descr 应该包含来自服务商对客户的(多语言)说明. --&gt; &lt;documentation url=&quot;http://www.example.com/help/mail/thunderbird&quot;&gt; &lt;descr lang=&quot;en&quot;&gt;Configure Thunderbird 2.0 for IMAP&lt;/descr&gt; &lt;descr lang=&quot;de&quot;&gt;Thunderbird 2.0 mit IMAP konfigurieren&lt;/descr&gt; &lt;/documentation&gt; &lt;/emailProvider&gt; &lt;!-- 用于同步用户通讯录或联系人方式的配置. 尚未实现. Thunderbird 使用 RFC 6764 实现这类功能的自动发现. --&gt; &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt; &lt;addressBook type=&quot;carddav&quot;&gt; &lt;!-- 用户名 --&gt; &lt;username&gt; %EMAILADDRESS%&lt;/username&gt; &lt;!-- authentication: 身份验证方式, 参见 incomingServer 标签配置. &quot;http-basic&quot;: 使用 HTTP 服务器进行 WWW-Authenticate: Basic 身份验证. &quot;http-digest&quot;: 使用 HTTP 服务器进行 WWW-Authenticate: Digest 身份验证. &quot;OAuth2&quot;: OAuth2 身份验证. 使用和 Email 相同的口令. --&gt; &lt;authentication&gt;http-basic&lt;/authentication&gt; &lt;!-- 指定服务器 URL. --&gt; &lt;serverURL&gt;https://contacts.example.com/remote.php/dav&lt;/serverURL&gt; &lt;/addressBook&gt; &lt;!-- 用于同步用户日历方式的配置. 尚未实现. Thunderbird 使用 RFC 6764 实现这类功能的自动发现. --&gt; &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt; &lt;calendar type=&quot;caldav&quot;&gt; &lt;!-- 用户名 --&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;!-- 身份验证方式, 参见 addressBook 中的身份验证方式. --&gt; &lt;authentication&gt; http-basic&lt;/authentication&gt; &lt;!-- 指定服务器 URL. --&gt; &lt;serverURL&gt;https://calendar.example.com/remote.php/dav&lt;/serverURL&gt; &lt;/calendar&gt; &lt;!-- 用于用户进行文件共享方式的配置. 尚未实现. 可用于 Thunderbird 中的 FileLink 或者在用户桌面创建一个同步文件夹. --&gt; &lt;!-- 由于 RFC 6764 实现了相同的功能, 所以这部分配置已经被标注需要被移除. --&gt; &lt;fileShare type=&quot;webdav&quot;&gt; &lt;!-- 用户名 --&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;!-- 身份验证方式, 参见 addressBook 中的身份验证方式. --&gt; &lt;authentication&gt;http-basic&lt;/authentication&gt; &lt;!-- 指定服务器 URL. --&gt; &lt;serverURL&gt;https://share.example.com/remote.php/dav&lt;/serverURL&gt; &lt;/fileShare&gt; &lt;!-- 可选, 用于访问 Webmail 的配置. 其中的 URL 值会在标准的网络浏览器中打开. --&gt; &lt;webMail&gt; &lt;!-- Webmail 的登录页面 URL, 用户必须手动输入用户名和密码进行登录. URL 需要使用 HTTPS. --&gt; &lt;loginPage url=&quot;https://mail.example.com/login/&quot; /&gt; &lt;!-- 与 loginAutomaticDOM 相同, 但网站会检查用户是否来自登录页. 会在浏览器中打开登录页面, 获取页面 DOM, 然后自动填写用户名和密码然后触发登录按钮. 登录按钮可能不是 HTML 而是一个 div, 所以触发这类按钮需要发送一个点击事件. URL 需要使用 HTTPS. --&gt; &lt;loginPageInfo url=&quot;https://mail.example.com/login/&quot;&gt; &lt;!-- username: 用户名 填写入 usernameField 中的内容. 格式(包括占位符)与 incomingServer 中的 username 相同. --&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;!-- 允许找到页面中的文本字段并填充它们. id 属性输出给 DOM name 属性. id 和 name 属性必须同时或存在其中一个. 如果存在, 则优先使用 id(例如使用 &quot;getElementByid()&quot;), 如果不存在, 则会尝试按名称查找元素. 不要把这个示例 XML 中的这些 id 当成是通用的, 在使用之前应该要验证格式(例如某些仅使用字符和数字的 id). 如果你还使用 jQuery 这样强大的函数, 还在 XML 的用户名 id 中返回了没有经过检查的代码, 那么它有可能会被执行. --&gt; &lt;usernameField id=&quot;email_field&quot; name=&quot;email&quot; /&gt; &lt;passwordField name=&quot;password&quot; /&gt; &lt;!-- 定义提交按钮, 用于填充完成后触发自动提交. id 和 name 属性参见 username.--&gt; &lt;loginButton id=&quot;submit_button&quot; name=&quot;login&quot; /&gt; &lt;/loginPageInfo&gt; &lt;/webMail&gt; &lt;!-- 尚未实现, 详见: &quot;https://bugzilla.mozilla.org/show_bug.cgi?id=564043&quot;. --&gt; &lt;!-- 在某些电子邮件服务提供商中, 用户名和密码并不与 IMAP, POP3 或 SMTP 的用户名和密码对应. 比如用户邮件地址是 &quot;Alice@example.com&quot;, 但用于登录电子邮件服务器的用户名是给定的用户 ID 或随机的用户名, 密码也可能同样不与电子邮件服务器的登录密码相同. 此时可以提供一个 inputField 标签, 这可以让客户端向用户显示一个输入框, 从而实现自定义的的用户名或密码. 输入框的提示由标签中的 label 属性定义, 其填充示例是 inputField 标签的值. 用户在对应 inputField 标签中输入的内容会写入到标签属性的 key 之中, 这个用于 key 属性变量值必须是大写字母. 同时被定义的变量可以在 XML 配置中的其他位置使用, 但并不建议用这种办法来让用户定义其他的配置属性(因为这样做的话, 提供该 XML 配置的意义就变得不再那么大, 并且用户体验比在 UI 上手动配置还要差). --&gt; &lt;inputField key=&quot;USERNAME&quot; label=&quot;Username&quot;&gt; 123456&lt;/inputField&gt; &lt;inputField key=&quot;NICKNAME&quot; label=&quot;Nickname&quot;&gt;Alice&lt;/inputField&gt; &lt;!-- 以上配置将生成如下的 UI: Username: [ ] example: 123456 Nickname: [ ] example: Alice 同时, 在配置文件的其他位置就能够使用 &quot;%USERNAME%&quot; 和 &quot;%NICKNAME%&quot; 来引用这两个用户输入的值. --&gt; &lt;!-- clientConfigUpdate: 客户端用于配置文件更新的 URL. --&gt; &lt;clientConfigUpdate url=&quot;https://www.example.com/config/mozilla.xml&quot; /&gt;&lt;/clientConfig&gt; 对于配置文件中的 incomingServer (传入服务器) 和 outgoingServer(传出服务器) 可能出现多次. 但应该按照优先使用顺序来排序, 通常会使用第一个出现的服务器配置, 只有当实际服务器表现不满足配置中要求的选项时(比如配置指定使用 SSL 但实际无法通过 SSL 连接到服务器), 才尝试选择第二个服务器配置. 如果 IMAP 和 POP3 服务器同时存在, 客户端应该同时列出(依旧遵循配置中的顺序显示来表示服务商的偏好), 让用户自行选择需要的服务器协议. 除上面配置文件中提到的, 依据用户在客户端的输入内容, 还存在以下通用占位符(变量): %EMAILADDRESS%: 用户的完整电子邮件地址. %EMAILLOCALPART%: 用户电子邮件地址中 &quot;@&quot; 前的部分. %EMAILDOMAIN%: 用户电子邮件地址中 &quot;@&quot; 后的部分. %REALNAME%: 真实名字(?) 依据这个规范编写的一个典型的 IMAP &amp; POP3 &amp; SMTP 服务器配置应该像这样: config-v1.1.xml1234567891011121314151617181920212223242526272829303132&lt;?xml version=&quot;1.0&quot;?&gt;&lt;clientConfig version=&quot;1.1&quot;&gt; &lt;emailProvider id=&quot;example.com&quot;&gt; &lt;domain&gt;example.com&lt;/domain&gt; &lt;displayName&gt;EXAMPLE Inc&lt;/displayName&gt; &lt;displayShortName&gt;CORG&lt;/displayShortName&gt; &lt;incomingServer type=&quot;imap&quot;&gt; &lt;hostname&gt;imap.example.com&lt;/hostname&gt; &lt;port&gt;994&lt;/port&gt; &lt;socketType&gt;SSL&lt;/socketType&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;authentication&gt;password-cleartext&lt;/authentication&gt; &lt;/incomingServer&gt; &lt;incomingServer type=&quot;pop3&quot;&gt; &lt;hostname&gt;pop3.example.com&lt;/hostname&gt; &lt;port&gt;995&lt;/port&gt; &lt;socketType&gt;SSL&lt;/socketType&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;authentication&gt;password-cleartext&lt;/authentication&gt; &lt;pop3&gt; &lt;leaveMessagesOnServer&gt;true&lt;/leaveMessagesOnServer&gt; &lt;/pop3&gt; &lt;/incomingServer&gt; &lt;outgoingServer type=&quot;smtp&quot;&gt; &lt;hostname&gt;smtp.example.com&lt;/hostname&gt; &lt;port&gt;465&lt;/port&gt; &lt;socketType&gt;SSL&lt;/socketType&gt; &lt;username&gt;%EMAILADDRESS%&lt;/username&gt; &lt;authentication&gt;password-cleartext&lt;/authentication&gt; &lt;/outgoingServer&gt; &lt;clientConfigUpdate url=&quot;https://autoconfig.example.com/mail/config-v1.1.xml&quot; /&gt; &lt;/clientConfig&gt; 对于不需要的配置如果不是需要保留为空值, 那么应该在配置文件中移除. RFC 6186 RFC 6186 的响应是基于 DNS 的 SRV 记录, 所以实现响应的标准就是 SRV 记录(RFC 2782[4]). SRV 记录标准: 1_Service._Proto.Name TTL Class SRV Priority Weight Port Target 使用 ZONE 进行命名就是: 1_[服务类型]._[协议].[域名名称] [生存时间] IN SRV [优先级] [权重] [端口] [目标服务器] 比如: 1_minecraft._tcp.minecraft.example.com 3600 IN SRV 0 1 19132 minecraft.hoster.com. 部署与应用 依据在协议响应规范中提到的各自的发现行为, 除了 RFC 6186, 实现只需要在对应域名设置 DNS 即可. Autodiscover 和 Autoconfiguration 按照对应需要解析 DNS 并放置配置文件至对应域名的固定路径即可实现部署. Autodiscover 标准实现 编写正确规范的 XML 配置文件. 将电子邮件地址使用的域名解析到一个用于托管配置文件的网络服务器. 如: 1example.com 3600 IN A 1.0.0.1 将配置文件放入网络服务器, 使其路径为: /Autodiscover/Autodiscover.xml. 再次解析邮件地址域名的子域名 autodiscover 到配置文件服务器: 1autodiscover.example.com 3600 IN A 1.0.0.1 将配置文件放入网络服务器, 使其路径为: /Autodiscover/Autodiscover.xml 多个配置文件理论并不会相互覆盖, 因为只会使用最先被发现的的那一个配置文件, 其目的是为了冗余. 但是不同客户端可能存在不同的行为, 所以建议同时设置多个方法且保证配置文件内容相同. 如果当前 DNS 解析状况导致解析已存在或不允许再次占用, 那么应该向后选择不冲突的那一种实现方式. 当然使用 CNAME 也是可以的, 因为会自动到 DNS 中的 CNAME 域名寻找. 跳转实现 从 Autodiscover 响应规范小节可以看出, 配置文件中存在一个 RedirectUrl 的标签, 可以让电子邮件客户端解析到时跳转到下一个网络配置文件路径. 因此可以将当前电子邮件域名的两个用于 Autodiscover 的 DNS 记录解析后在网络服务器对应的路径放入一个只用于重定向跳转的 XML: XML 格式应该为: Autodiscover.xml1234567891011&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt; &lt;Response xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt; &lt;Account&gt; &lt;AccountType&gt;email&lt;/AccountType&gt; &lt;Action&gt;redirectUrl&lt;/Action&gt; &lt;RedirectUrl&gt;https://filesystem.example.com/autodiscover/example.com/Autodiscover.xml&lt;/RedirectUrl&gt; &lt;/Account&gt; &lt;/Response&gt;&lt;/Autodiscover&gt; 这样就能将当前电子邮件域名的配置文件重定向到另一个网络文件路径 这样做的好处是能够将大量的域名电子邮件配置集中在一个网络文件系统中管理, 这个文件系统可以是你的服务商管理也可以是企业的 IT 管理员管理, 作为客户及最终用户可以免去维护 XML 配置的步骤. 如果多个域名都使用了相同的电子邮件服务器或电子邮件服务提供商, 那么这些电子邮件服务器的提供的客户端配置一般都是相同的. 但相比直接使用 CNAME 的跳转发现, 还需要配置一个用于跳转的 XML, 所以其实并不太理想. 这样的跳转实现大多用在 Outlook 本地安装的情况, 在本地设备上一次性使用注册表或者组策略将 Outlook 的 Autodiscover 指向这个跳转文件. 除了使用 HTTP 跳转, 还能跳转到另一个邮箱地址进行发现, XML 格式为: Autodiscover.xml1234567891011&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;Autodiscover xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/responseschema/2006&quot;&gt; &lt;Response xmlns=&quot;https://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a&quot;&gt; &lt;Account&gt; &lt;AccountType&gt;email&lt;/AccountType&gt; &lt;Action&gt;redirectAddr&lt;/Action&gt; &lt;RedirectAddr&gt;others@example.net&lt;/RedirectAddr&gt; &lt;/Account&gt; &lt;/Response&gt;&lt;/Autodiscover&gt; 同样的, 这样会让客户端解析到这个 XML 文件后直接跳转到 others@example.net 对这个邮箱地址的 example.net 的域名进行配置文件发现. 相较于直接配置一个 XML 的好处是: 避免跳转 URL 的修改可能无法及时同步到用于本地配置. 所以使用了跳转到另一个邮箱域名进行重新发现. 多种配置模式适用于不同的电子邮件系统架构和组织部署环境, 需要按照需求选择合适自身的方法. Autoconfiguration 标准实现 编写正确规范的 XML 配置文件. 将电子邮件地址使用的域名的子域名 autoconfig 解析到一个用于托管配置文件的网络服务器, 如: 1autoconfig.example.com 3600 IN A 1.0.0.1 将配置文件放入网络服务器, 使其路径为: /mail/config-v1.1.xml. 编写正确规范的 XML 配置文件. 将电子邮件域名直接解析至配置文件服务器: 1example.com 3600 IN A 1.0.0.1 将配置文件放入网络服务器, 使其路径为: /.well-known/autoconfig/mail/config-v1.1.xml 编写正确规范的 XML 配置文件. 将其电子邮件域名的子域名 www 解析至网络服务器, 并将 XML 文件放入该网络文件服务器, 使其为任意便于管理的路径, 如 /autoconfig/example.com/config-v1.1.xml. 为电子邮箱域名添加一个 TXT 记录, 值为上一步放置的 XML 文件完整的 HTTP/HTTPS 网络路径: 1example.com 3600 IN TXT &quot;https://www.example.com/autoconfig/example.com/config-v1.1.xml&quot; 多个配置文件理论并不会相互覆盖, 因为只会使用最先被发现的的那一个配置文件, 其目的是为了冗余. 但是不同客户端可能存在不同的行为, 所以建议同时设置多个方法且保证配置文件内容相同. 如果当前 DNS 解析状况导致解析已存在或不允许再次占用, 那么应该向后选择不冲突的那一种实现方式. 中心化实现 本文将 Autoconfiguration 实现方法中, 需要依赖第三方中心化服务实现自动发现的方式称为 &quot;中心化实现&quot;. 比如这里要提到的 &quot;尝试从 Mozilla 服务器检索配置&quot;. 由 Thunderbird 维护并托管的电子邮件服务商配置文件数据库称为 &quot;ISPDB&quot;, 可免费供给任何用户(客户)使用. 它包含了最多的电子邮件服务商配置, 大多市场份额超过 0.1% 的服务商都包含在内, 几乎囊括了全球 50% 的电子邮件账户. 但要注意, 并不是随便一个服务商就能进入这个数据库, 比如自托管的个人电子邮件服务器就不符合要求. ISPDB 要求: If you are a big ISP (&gt; 100,000 users) providing email addresses solely under a few domains like &quot;example.com&quot; and &quot;example.de&quot;, you may either submit the configuration to the ISPDB or set up a configuration server.[5] &quot;如果您是大型 ISP(&gt; 100,000 个用户), 仅在 &quot;example.com&quot; 和 &quot;example.de&quot; 等几个域下提供电子邮件地址, 您可以将配置提交到 ISPDB 或设置配置服务器.&quot;[5:1] If you are a small company installing Thunderbird on your employees' desktops, you can place a configuration file in the Thunderbird installation folder.[5:2] 如果您是在员工桌面上安装 Thunderbird 的小公司, 您可以将配置文件放在 Thunderbird 安装文件夹中.[5:3] 如果想要为自己的电子邮件服务能够通过 Mozilla 服务器查找到服务器配置, 那么除去需要在客户端支持从 ISPDB 中检索, 还要为该服务的源 Git 仓库提交你的 Autoconfiguration 配置文件: thundernest/autoconfig: The ISPDB, Thunderbird's database of mail configuration files. 源仓库存在两分支的配置文件索引, 一个是 prod 分支, 直接用于 Thunderbird(https://autoconfig.thunderbird.net/autoconfig/v1.1/); 另一个是 master 分支, 用于暂存服务器(https://autoconfig-stage.thunderbird.net/autoconfig/v1.1/). RFC 6186 RFC 6186[2:2] 制定的目的就是为了解决从域名中发现电子邮件服务器配置的问题, SMTP[1:1] 本身存在对 MX 记录进行发现进而查找到对应的服务器主机, 但是现代邮件系统中的 MSA 不使用 MX 记录以及工作端口是 587. 于是这个规范设计来与 RFC 4409 联合使用. 在由 RFC 4409[6] 确立的现代的电子邮件提交系统里, 在用户发送电子邮件时, 需要通过 MSA(Message submission agent, 消息提交代理)进行处理才能传输到邮件服务器或中继服务器. 通常来说, 最终用户使用的传出服务器基本都是 MSA 系统, 这样避免了恶意提交对邮件服务器的侵害, 也能保护用户身份不被盗用. 这个标准中定义了对 MSA, IMAP 和 POP3 进行配置的新 SRV 记录类型, 并且还可以定义优先使用哪一种传入协议. 值得一提的是, RFC 6186 是由 Apple 起草发起的, RFC 4409 主要作者之一是 Qualcomm(高通). 实现标准 MSA 使用如下的 SRV 记录指向 MSA 服务: 1_submission._tcp 3600 IN SRV 0 1 587 mail.example.com. IMAP 使用 imap 标识一个普通 IMAP 服务类型. 对于 STARTTLS 应该是客户端和 IMAP 服务器必须实现的, 但有的服务商可能并不需要使用. 使用 imaps 表示一个使用 TLS 连接的 IMAP 服务器. 使用如下的 SRV 记录指向 IMAP 服务: 1_imap._tcp 3600 IN SRV 0 1 143 imap.example.com. 1_imaps._tcp 3600 IN SRV 0 1 993 imap.example.com. POP3 使用 pop3 标识一个可能会需要客户端使用 STLS[7] 进行连接的普通 POP3 服务器. 使用 pop3s 标识一个使用 TLS 连接的 POP3 服务器. 使用如下的 SRV 记录指向 POP3 服务: 1_pop3._tcp 3600 IN SRV 0 1 110 pop3.example.com. 1_pop3s._tcp 3600 IN SRV 0 1 995 pop3.example.com. 优先级指定 由于 SRV 记录允许为服务指定优先级, 所以如果同时在 DNS 中解析指向了多种服务类型, 那么管理员可以通过指定 SRV 记录优先级表示服务端对服务类型的偏好. SRV 记录与 MX 记录指定优先级的概念相同, 是以优先级数字编号越小代表越优先(权重恰恰相反, 数字越大表示权重越高, 但在 SRV 里优先使用优先级, 如果优先级相同再使用权重判定). 通过设置优先级, 将希望优先被采用的传入服务器配置记录使其小于其他的服务记录实现服务端偏好设置. 优先使用 IMAP 而不是 POP3: 1_imap._tcp 3600 IN SRV 0 1 143 imap.example.com. 1_pop3._tcp 3600 IN SRV 10 1 110 pop3.example.com. 通过将 SRV 记录中的目标服务器值设置为 . 来表示该域完全不支持此服务. 如果存在这种记录值, 则电子邮件客户端应该认为这种服务器不可用, 并继续使用 SRV 记录进行确定. 比如, 通过以下的记录值 1_imap._tcp 3600 IN SRV 0 0 0 . 1_imaps._tcp 3600 IN SRV 0 1 993 imap.example.com. 1_pop3._tcp 3600 IN SRV 0 0 0 . 1_pop3s._tcp 3600 IN SRV 10 1 995 pop3.example.com. 上面的记录值指定本域中存在使用 TLS 和不使用 TLS 的 IMAP 服务和 POP3 服务(带 s 和不带 s), 但是不使用 TLS 的 IMAP 和 POP3 服务不可用. 且使用 IMAPs 服务的优先级高于使用 POP3s, 因为 IMAPs 的优先级 0 要比 POP3s 的优先级 10 要小. 现状与未来 到此为止, 本文就介绍完目前所有去中心化且主流的实现自动发现方式了. 那么现在在电子邮件中的自动发现是一个什么样的情况? 未来呢? 现状 目前文中提到的自动发现, 对于 Autodiscover, 在 Exchange Online 所有的 Exchange Online 服务都需要组织管理员单独为域名设置一个 autodiscover 别名记录指向微软的 Outlook 服务器 autodiscover.outlook.com 并且由于关闭了基础身份验证, 还要用户先通过身份验证才能检索到 Microsoft 服务器中的配置; Exchange Server 目前依旧能够通过本地的 Active Directory 和 IIS 服务器发现到配置文件. 就目前来说, Autodiscover 还在是 Microsoft 自己设计能尚且还在运行的标准, 其他的非 Exchange 电子邮件服务器也能通过它的标准的部分来兼容实现自动发现. 对于 Autoconfiguration, 这是 Mozilla 的 Thunderbird 采用的一种发现标准, 比起 Autodiscover 为了兼容更多服务商的情况给出了很多 Autodiscover 中没有的选项. 但有的选项 Thunderbird 自己都没有实现支持, 或者还是存在疑问的地方. 最后衍生出了 Mozilla 自己的中心化 ISPDB 服务器, 主要的目的也是为了兼容. 而 RFC 6186 目前还是处于 &quot;拟议标准(Proposed Standard)&quot; 状态, 也就是说这个标准刚通过起草阶段被 IETF 审阅后公布, 而如果已经做到被互联网广泛采用, 它应该已经被标记上 &quot;标准追踪(Standards Track)&quot; 状态. 它首次发表于 2011 年, 最后一次修改是 2015 年. 对于这些针对服务端份规范, 尚且处于不成熟不统一的状态. 那么客户端的规范呢? 对于目前的电子邮件客户端, 大部分由电子邮件服务商直接提供的本地客户端都优先使用的是内置的配置数据库, 对于不存在于内置数据库中的还是会要求用户自己输入配置, 并不会尝试去通过以上的标准进行自动发现. 并且对于这些专有客户端, 用户也无从得知它的自动发现支持状态和实现标准. 除了专有客户端, 大部分开源的或其他开放的电子邮件客户端几乎都支持其中一种或多种自动发现, 也内置预设配置数据. 实在不济的也还能通过账号系统保存上一次得到的配置数据, 或者能够导入导出账号列表的数据. 对于电子邮件服务提供商, 大部分都不会提供配置自动发现的指引, 甚至不会在 DNS 记录中提供官方的配置. 就算一万个用户使用十万个域名, 他们使用的服务端配置都是相同, 但服务商依旧不会提供自动发现, 甚至不提供指引, 当然这里也不是指所有的代托管服务商, 有少部分的服务商具有完整的自动发现指引, 有的还提供官方的自动发现服务器, 能够让客户(用户)通过自己的域名 CNAME 实现支持. 对于用户, 本该并不需要知道什么是自动发现, 这应该是电子邮件管理员和电子邮件服务商提前配置好的. 但是由于客户端的问题, 或是管理员和服务商根本没有考虑自动发现, 导致每次登录新的客户端都需要手动配置至少两个(传入与传出)服务器的参数才能使用, 而且如果不是专业用户, 他们甚至会面对这些参数手无足措, 转而把问题转移到服务商和管理员身上(大部分不支持自动发现的客户端反而却能幸免于难). 那么, 又到了给不懂计算机的朋友修电脑的时候了, 但要面对的可能是你的用户. 但是, 即使是精明的用户, 也并不是都想或都了解过自动发现. 而是更愿意使用更成熟的, 内建客户端账号系统的电子邮件客户端, 将这些繁杂的服务器配置数据保存在第三方手中(包括密码). 未来 笔者期望的未来是用 RFC 实现的互联网标准, 但不会是目前的 RFC 6186, 因为它并不能解决除了下发配置之外的问题, 至少也要如 Mozilla Thunderbird 的 Autoconfiguration 中的一样, 为身份验证和个性化给出支持, 对于现代的 MSA 系统大部分都已经开始采用 API 与服务器交互, 而不是还在使用服务器通讯协议, MSA 的 587 端口同样难以抵御泛滥的网络攻击, 部分的服务商已经开始使用更进一步的身份验证保护 MSA 系统甚至禁止基本身份验证, 这其中包括现在多方强调的多因素身份验证以及零信任模型. 现代的电子邮件系统已经将用户和攻击者隔离在服务器之外很远的地方, 用户能通过身份验证表示攻击者也同样能, 只要还存在暴露的门, 那就一定会有乘虚而入的家伙. 所以, 如果电子邮件系统在未来依旧不会消亡, 那么对于自动发现应该实现的, 应该包括服务器安全策略和客户端策略, 而不是简单的指向一个端口和服务器. 又或者, 在 MSA 系统和用户之间再叠一层专用于身份验证和威胁防御的专用系统? 也许会叫 &quot;用户认证代理(User Verification Agent, UVA)&quot;?🥱 参考资料 Exchange Server中的自动发现服务 | Microsoft Learn Exchange 自动发现 | Microsoft Learn 计划自动在 Outlook 2010 中配置用户帐户 | Microsoft Learn Microsoft Remote Connectivity Analyzer Thunderbird:Autoconfiguration - MozillaWiki Thunderbird:Autoconfiguration:DNSBasedLookup - MozillaWiki Thunderbird Autoconfiguration - Config file format IMAP and ActiveSync Configuration for Outlook using Autodiscovery - Zoho Mail Autodiscover: Some quick methods to get it working - HowTo-Outlook Email Glossary – Validity Help Center 什么是 DNS SRV 记录？ | Cloudflare Zone file - Wikipedia RFC 2821 - Simple Mail Transfer Protocol (IETF) ↩︎ ↩︎ RFC 6186 - Use of SRV Records for Locating Email Submission/Access Services (IETF) ↩︎ ↩︎ ↩︎ thundernest/autoconfig: The ISPDB, Thunderbird's database of mail configuration files. (GitHub) ↩︎ RFC 2782 - A DNS RR for specifying the location of services (DNS SRV) (IETF) ↩︎ Thunderbird Autoconfiguration (Ben Bucksch) ↩︎ ↩︎ ↩︎ ↩︎ RFC 4409 - Message Submission for Mail (IETF) ↩︎ RFC 2595 - Using TLS with IMAP, POP3 and ACAP (IETF) ↩︎","date_published":"2023-04-20T09:18:00.000Z","tags":["资料库","电子邮件","电子邮件","自动发现","Autodiscover","Autoconfiguration","RFC 6186"]}]}