@差生文具多
@差生文具多
关注的小组(6)
动态 帖子 20 评论 111 短评 5 收到的赞 235 送出的赞 159
  1. 差生文具多   在小组 2047 发表文章

    【搬运】Rust | 批判性回顾

    原文:Rust: A Critical Retrospective:https://www.bunniestudios.com/blog/?p=6375

    翻译:机器之心/张汉东:mp.weixin.qq.com/s/eb_U2rirLjCqCLH8qs8k5Q

    译者注:说明:国内机器之心公众号对这篇文章进行了编译,发布了标题为《编写完10万行代码,我发了篇长文吐槽Rust》的一篇文章,我看了以后发现机器之心的这篇文章对原文原意的传达并不正确与完整,并且省略了很多对 Rust 开发者有帮助的关键细节,比如,作者如何防范 Rust 供应链攻击等,以及 Rust 超出作者预期的优点有哪些。所以特此全文翻译,供大家参考。当然,也要感谢机器之心对这篇文章的传播,让我有机会看到此文。

    由于在大流行期间我有几年无法旅行,所以我决定利用我新获得的时间并真正专注于 Rust。在编写了超过 10万行 Rust 代码之后,我想我开始对这门语言有了一种感觉,并且像每一个脾气暴躁的工程师一样,我已经形成了我自己的观点,因为这是互联网,所以我要分享。

    我学习 Rust 的原因是充实 Xobs 编写的 Xous[2] OS 的一部分。Xous 是一个用纯 Rust 编写的微内核消息传递操作系统。它的近亲可能是 QNX[3]。Xous[4] 是为轻量级(物联网/嵌入式规模)安全优先平台(如 Precursor[5] )编写的,支持 MMU 以实现硬件强制的页面级内存保护。

    在过去的一年里,我们设法为操作系统增加了很多功能:网络(TCP/UDP/DNS)、中间件图形抽象(用于模版和多语言文本)、存储(以加密的、看似可否认的数据库的形式,称为PDDB[6])、可信启动,以及具有自配置和密封(sealing)属性的密钥管理库。

    我们决定编写自己的操作系统而不是使用 SeL4、Tock、QNX 或 Linux 等现有实现的原因之一是我们想真正了解我们设备中的每一行代码在做什么。特别是对于 Linux,它的源代码库是如此庞大和动态,即使它是开源的,你也不可能审计内核中的每一行。代码更改的速度比任何人都可以审计的要快,除非是自己开发的。Xous的范围也很窄,只支持我们的平台,以尽可能地保持内核中不必要的复杂性。

    范围狭窄意味着我们还可以充分利用CPU运行在FPGA中[7]的优势。因此,Xous的目标是一个不寻常的RV32-IMAC配置:一个有MMU+AES扩展的配置。毕竟是2022年,而且晶体管很便宜:为什么我们所有的微控制器都不像桌面上的同类产品那样具有页级存储器保护功能?作为一个FPGA,也意味着我们有能力在硬件层面修复API错误,使内核更加精简和简化。这在通过抽象破坏(abstraction-busting)过程的工作中尤其重要,比如从RAM中挂起和恢复。但这都是另一篇文章的内容:这篇文章是关于Rust本身,以及它如何作为Xous的系统编程语言。

    Rust 的「卖点」

    在我们启动Xous的时候,我们看了大量的系统编程语言,Rust脱颖而出。尽管它的 "no-std "支持在当时还不成熟,但它是一种强类型、内存安全的语言,拥有良好的工具和一个蓬勃发展的生态系统。我个人是强类型语言的超级粉丝,内存安全不仅对系统编程有好处,它还能让优化器更好地生成代码,此外它还能让并发性不那么可怕。实际上,我希望Precursor有一个CPU,它对标记指针和内存能力有硬件支持,类似于在CHERI[8]上所做的,但在与做CHERI的团队讨论后,显然他们非常专注于使C语言变得更好,没有带宽来支持Rust(尽管这可能正在改变[9])。从总体上看,C语言对CHERI的需求远远大于Rust对CHERI的需求,所以这是一个公平的资源优先排序。然而,我是一个安全方面的粉丝,所以我仍然希望有一天,硬件强制的胖指针会进入Rust。

    虽然如此,我并不打算回到C语言阵营,只是为了踢一踢硬件改造的"轮胎",以弥补C语言的一个糟糕的方面。Rust 光鲜的手册还宣传它能够通过其严格的 「借用检查器」在错误发生之前就加以预防。此外,它的发布理念应该是为了避免我所说的 「Python的问题」:“如果你不主动跟上语言的最新版本,你的代码就会停止工作”。另外,与Python不同的是,Rust并非天生就不卫生,因为宣传的安装包的方式也不是错误的安装包的方式。与Python相反,在Python中,关于包的官方文档会引导你把它们添加到系统环境中,但却会被Python的“长老们”骂:“但你当然应该使用venv/virtualenv/conda/pipenv/...,大家都知道”。如果这个细节没有被归入官方教程的16章中的第12章[10],我对Python的体验会好得多。当有人取消发布一个流行的包时,Rust 也应该比 Node 更好地避免“哎呀,我删除了互联网”问题,至少如果你为你的包使用完全指定的语义版本。

    从长远来看,Xous背后的哲学是,最终它应该 "变得足够好",到那时我们就应该停止对它的操纵。我相信,工程师的使命就是最终把自己的工作搞好:系统应该变得足够稳定和牢固,以至于它 "只是能用",没有任何注意事项。在这一点上,任何额外的工程都只会增加错误或臃肿。Rust的 "稳定就是永恒 "的理念,以及承诺永远不破坏向后兼容的理念,从让Xous变得如此完美,以至于不再需要我这个工程师的角度来看,是非常一致的,从而使我能够将更多的时间和精力用于支持用户和他们的应用程序。

    Rust 欠打磨的地方

    网上已经有很多写给 Rust 的「情书」了,所以,我打算先列举我遇到的一些 Rust 的缺陷。

    语法中的噪音(Line Noise)

    这是一个肤浅的抱怨,但我发现 Rust 的语法很密集、很沉重,而且难以阅读,就像试图阅读附带噪音的UART输出一样:

    Trying::to_read::<&'a heavy>(syntax, |like| { this. can_be( maddening ) }).map(|_| ())?;
    

    用更通俗的话说。上面这句话的作用是在对象(实际上是结构体')Trying上调用一个名为to_read的方法,其类型注释为&heavy,生命周期为 'a,参数为 syntax,闭包的参数为like,在另一个名为 this 的结构体实例上调用can_be方法,参数为 maddening,任何非错误返回值都映射到Rust单元类型(),如果有错误,会被自动unwrap并返回给调用者。

    深呼吸。当然,我有一些地方是错的,但你会明白这种语法有多密集。

    在此基础上,你可以将宏和指令分层,而这些宏和指令不需要遵循Rust的其他语法规则。例如,如果你想条件编译代码,你可以使用一个指令,如:

    #[cfg(all(not(baremetal), any(feature = “hazmat”, feature = “debug_print”)))]
    

    这就是说,如果启用了 hazmat 或 debug_print 特性(feature),而且你不是在裸机上运行,就使用下面的代码块(我肯定也弄错了)。对我来说,这个语法最令人困惑的部分是使用单一的=来表示等价而不是赋值,因为,配置指令中的东西不是Rust代码。它就像一种独立的元语言,有一个你可以查询的键值对的字典。

    我甚至不打算讨论Rust宏的不可读性--即使我自己写了一些Rust宏,我也不得不承认,我觉得它们 "只是勉强能用",而且可能在它们的某个地方有“龙”(Bug)。这不是你在一门自称可靠的语言中应该有的感觉。是的,这是我的错,因为我不够聪明,无法解析语言的语法,但同时,我的生活中还有其他事情要做,比如构建硬件。

    无论如何,这是一个肤浅的抱怨。随着时间的流逝,我最终克服了学习曲线并变得更加自在,但这是一条艰难而陡峭的曲线。这部分是因为所有的 Rust 文档要么是用eli5 风格[11]编写的(形容文档写的非常简陋),要么你会看到一个正式的语法定义[12](从技术上讲,定义一个“feature”在那里,但没有用简单的英语概括)。

    要说明的是,我非常同情写好文档有多难,所以这不是在挖苦那些努力写出这么多优秀语言文档的人。我真的很欣赏文档生态系统的总体质量和丰富性。

    Rust只是在语法方面有一个陡峭的学习曲线(至少对我来说)。

    Rust 虽然强大,但并不简单

    Rust 很强大。我很欣赏它的标准库,它包含了 HashMaps、Vecs 和 Threads 等数据结构,既“美味”又令人上瘾。一旦我们在 Xous 中获得了 std 支持,就再回不去了。来自 C 和汇编的背景,Rust 的标准库感觉丰富且有用。我读过一些负面评价,说它缺乏一些功能,但就我的目的而言,它确实达到了最佳状态。

    话虽如此,我对 Rust std 库的依赖并没有在构建可审计的代码库方面带来任何好处。我以前对Linux的批评之一是:“天哪,内核的源代码包括红黑树的实现,怎么会有人来审计它呢”。

    现在,在写过操作系统之后,我对这些丰富的动态数据结构的重要性有了深刻的理解。然而,Xous 没有在其存储库中包含 HashMap 的实现这一事实并不意味着我们比 Linux 更简单:事实上,我们只是把一大堆代码“隐藏”了起来。仅仅是标准库的 "collection "部分就代表了大约1万多行源代码,而且复杂度非常高。

    因此,虽然 Rust 的 std 库允许 Xous 代码库专注于成为内核而不是其自己的标准库,但从构建最小攻击面、“完全由一个人可审计”代码库的角度来看,我认为我们对 Rust 的 std 库的依赖意味着我们在这个目标上失败了,尤其对于我们需要继续跟踪 Rust 的最新版本(我将在下一节中解释为什么我们必须这样做)来说。

    理想情况下,在某些时候,事情会 "稳定下来",我们可以把Rust 仓库分叉,然后说 "这是我们的攻击面,我们不打算改变它"。即便如此,Rust std 存储库的大小仍使 Xous 存储库相形见绌,这还不包括编译器本身的复杂性。

    Rust 还未完善

    接下来的这一点与Rust还不适合做完全可审计的内核的原因相吻合:语言还未完善。例如,在我们编写Xous的时候,引入了常量泛型(const generic)。在这之前,Rust没有处理大于32个元素的数组(Array)的能力。这种限制有点让人抓狂,即使在今天也有一些缺点,比如Default trait 无法初始化大于32个元素的数组。这种阻碍导致我们把许多东西限制在32个元素:例如,当我们在进程之间传递 SSID 扫描的结果时,该结构只为最多 32 个结果保留空间,因为去更大、更多通用结构是不值得的。这是直接驱动面向用户的功能的语言级别限制。

    另外,在编写Xous的过程中,像内联汇编(inline assembly)和工作空间(workspaces)这样的东西终于成熟了,这意味着我们需要回到过去,重新审视我们所做的一些“危险”的事情,使那些关键的几行用汇编编写的初始启动代码能够集成到我们的构建系统中。

    我经常问自己“我们什么时候才能离开 Rust 发布火车(火车式发布)”,我认为的答案是他们最终让alloc稳定下来。目前,no-std target 无法访问堆,除非它们跳上“Nightly”列车,在这种情况下,你又回到了 Python 式的噩梦,你的代码经常因语言版本而中断。

    我们绝对给了用no-std和 Stable Rust编写操作系统一个公正的机会。Xous第一年的开发都是用no-std完成的,在内存空间和复杂性方面付出了代价。我们有可能只用预先分配好的静态数据结构来编写操作系统,但我们必须在所有情况下满足最坏情况下的元素数量,这导致了臃肿。此外,我们还不得不推出很多自己的核心数据结构。

    大约一年前,当Xobs将Rust的std库移植到Xous时,这一切都改变了。这意味着我们能够在稳定的Rust中访问堆,但这是有代价的:现在Xous被捆绑在一个特定的Rust版本上,因为每个版本的Rust都有自己独特的std打包版本。这个版本的绑定是有原因的:std是可以将内存分配和线程创建等基本 Unsafe 的硬件结构变成 Safe 的Rust结构的地方。(我最近还了解到一个有趣的事实。Rust对于大多数 target 没有本地分配器--它只是简单地使用libc的malloc()和free()函数!) 换句话说,Rust能够有力地保证稳定版本的“火车”不破坏旧的功能,部分原因是所有的松散部分都被扫进了std。

    我必须不断提醒自己,拥有 std 并不能消除关键代码中严重安全漏洞的风险——它只是将许多关键代码移到了标准库中。是的,它是由一群比我聪明的天才程序员维护的,但归根结底,我们都只是人类,我们都是软件供应链攻击的公平目标。

    Rust有一个发条式的发布时间表:每六周就会推送一个新版本。由于我们的 "std "分叉与特定的Rust版本相联系,这意味着每六周,Xobs都要承担更新我们的分叉并为其建立新的 "std "版本的艰巨任务(我们不是Rust的一级平台,这意味着我们必须维护自己的 "std "库)。这意味着我们同样迫使所有Xous开发者在他们的工具链上运行rustup update,这样我们才能保持与语言的兼容性。

    这可能是不可持续的。最终,我们需要锁定代码库,但我没有一个明确的退出策略。也许我们可以考虑回到 "nostd "的下一个时间点,就是我们可以获得稳定的 "alloc "功能,这让我们可以再次访问堆。这样我们就可以把Xous从Rust发布的列车上解开,但我们仍然需要回填一些功能,如 Vec、HashMap、Thread和 Arc/Mutex/Rc/RefCell/Box结构,使 Xous 能够有效地进行编码。

    不幸的是,alloc' crate 非常难,而且已经开发了很多年了。虽然如此,我真的很欣赏Rust在开发这一功能背后的透明度,以及为稳定这一功能所做的努力和思考。

    译者注:这一点,也许学习一下 Android 和 Fuchsia 如何引入 Stable Rust 工具链会对他有所启发。当然,因为 Xous 是纯 Rust 实现,而 Android 和 Fuchsia 是部分模块或组件用 Rust,后者可能没有 Xous 的麻烦。

    Rust 对供应链安全的观点有局限

    我认为这一立场被rustup.rs安装页面推荐的安装方法很好地概括了: “嗨,在你机器上运行这个来自随机服务器的 shell 脚本。”

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    

    公平地说,你可以下载脚本并在运行前检查它,这比 Windows .MSI安装程序的vscode要好得多。然而,这种做法充斥着整个构建生态系统:每当你从crates.io Pull 一个新的crate时,一个名为build.rs的桩代码就有可能被编译和执行。这一点,加上 "宽松 "的版本定位(你可以指定一个版本,例如,简单的 "2",这意味着你将抓取任何最新发布的版本,主要版本为2),使我对通过crates.io生态系统发起软件供应链攻击的可能性感到不安。

    Crates.io还受到一种错别字的影响,很难确定哪些是 "好 "的,哪些是 "坏 "的;一些名字和你想要的一模一样的 crate,结果只是旧的或被放弃的早期尝试,给你提供了你想要的功能,而更受欢迎的、积极维护的 crate 不得不采取不太直观的名字,有时与其他 crate 只有一两个字符的差别(公平地说,这不是Rust的包管理系统特有的问题)。

    还有一个事实是,依赖关系是链式的--当你从crates.io Pull 一个东西的时候,你也 Pull 了该crate的所有附属依赖关系,以及所有最终会在你的机器上运行的build.rs脚本。因此,仅仅审核Cargo.toml文件中明确指定的crate是不够的,你还必须审核所有依赖的crate,以防止潜在的供应链攻击(upply chain attacks)。

    幸运的是,Rust允许你使用 Cargo.lock 文件将 crate 固定在一个特定的版本上,你可以完全指定一个依赖 crate ,直到次要版本。在Xous中,我们试图通过发布Cargo.lock文件和指定所有第一阶的依赖 crate 到次要版本的政策来缓解这一问题。我们还对某些 crate 做了本地备份(vendored)和 forked,否则会使我们的依赖树增长而没有什么好处。

    这就是说,我们的大部分调试和测试框架都依赖于一些相当花哨和复杂的 crate,这些crate Pull 了大量的依赖关系,而令我懊恼的是,即使我试图为我们的目标硬件运行一个构建,用于在主机上运行模拟的依赖 crate 仍然被 Pull 了下来,build.rs脚本即使没有运行,至少也被构建了。

    针对这种情况,我写了一个叫 crate-scraper[13]的小工具,它可以下载Cargo.toml文件中指定的每一个源码包,并将它们存储在本地,这样我们就可以有一个用于构建Xous版本的代码快照。它还可以进行快速的 "分析",即搜索名为build.rs的文件,并将它们整理成一个文件,这样我就可以更快速地通过grep来寻找明显的问题。当然,人工审查并不是检测嵌入build.rs文件中的巧妙伪装的恶意软件的实用方法,但它至少让我了解我们正在处理的攻击面的规模: 它是令人惊叹的[14],大约有5700行来自不同第三方的代码,它们操纵文件、目录和环境变量,并且在我每次进行构建时在我的机器上运行其他程序。

    我不确定这个问题是否有好的解决方案,但是,如果你是超级偏执狂,而且你的目标是能够构建可信赖的固件,那么就要警惕Rust的广泛的软件供应链攻击面。

    你无法重现(reproduce)别人的 Rust 构建

    我对Rust的最后一个问题是,在不同的计算机之间,构建是不可重现的(如果我们禁用我在Xous中为$reasons而嵌入的时间戳,它们至少在同一台机器上的构建之间是可重现的)。

    我认为这主要是因为Rust将源代码的完整路径作为二进制文件中的恐慌(panic)和调试(debug)字符串的一部分拉进来。这导致了一些不舒服的情况,我们在 Windows 上构建了工作,但在 Linux 下失败了,因为我们的路径名在两者上的长度非常不同,这会导致一些内存对象在目标内存中移动。公平地说,这些失败都是由于我们在 Xous 中存在的错误,这些错误已经得到修复。但是,知道我们最终会有用户向我们报告我们无法重现的错误,这感觉并不好,因为他们在构建系统上的路径与我们的不同。对于想要通过构建自己的版本并将哈希值与我们的哈希值进行比较来审核我们发布的用户来说,这也是一个问题。

    在Rust的维护者那里有一些bug,以解决可重复构建的问题,但由于他们必须处理语言中的许多问题,我对这个问题能否很快得到解决并不感到乐观。假设导致不可重现性的唯一原因是二进制文件中包含了操作系统的路径,那么解决这个问题的一个办法就是重新配置我们的构建系统,使其在某种chroot环境或虚拟机中运行,以一种几乎任何人都能重现的方式修复路径。我说 "几乎任何人 "是因为这个修复方法将取决于操作系统,所以我们将能够在例如Linux下获得可重复的构建,但它不能帮助Windows用户,因为chroot环境不是一个东西。

    Rust 超乎预期的地方

    尽管这里列出了所有的抱怨,但我认为如果我必须重新做一遍,Rust 仍然是我用于 Xous 的有力竞争语言。我做过 C、Python 和 Java 的大型项目,所有这些项目最终都背负着“不断增加的技术债务”(可能有一个软件工程师的术语来描述这种情况,我只是不知道)。问题往往是从一些数据结构开始的,我在第一遍的时候不能完全弄好,因为我还不知道这个系统是如何组成的;所以为了弄清楚这个系统是如何组成的,我就用一个半生不熟的数据结构拼凑出一些代码。

    于是就开始陷入混乱:一旦我对事情的运作有了一个概念,我就回去修改数据结构,但现在在其他地方出现了一些未曾预料到的、微妙的破坏。也许这是一个逐个击破的问题,或者一个符号的极性似乎被颠倒了。也许这是个轻微的竞态条件,很难说清楚。没关系,我可以通过把<=改成<,或者修改符号,或者增加一个锁来解决这个问题。我仍然在充实这个系统,对整个结构有一个概念。最终,这些小的 hack 代码(意指非正规写法,只图完成功能)往往会转移到每一个依赖的模块中,因为事情的全部原因是由于 "作弊 "而产生的;当我回去切除这些 hack 代码时,我最终得出结论,它不值得努力,所以下一个最好的选择是把整个事情烧掉重写...但不幸的是,我们已经落后于计划和预算,所以重写从未发生,而hack代码继续生存。

    Rust是一种很难编写代码的语言,因为它使这些 "作弊 "变得很难:只要你有纪律,不使用 Unsafe 的结构来使作弊变得容易。然而,真正的困难并不意味着不可能--在构建Xous的过程中,肯定有一些作弊行为被掩盖了起来。

    这就是 Rust 真正超出我预期的地方。该语言的结构和工具非常擅长追捕这些作弊并对重构代码库有帮助,从而能达到“治愈癌症而不杀死患者”的效果,可以这么说。这就是 Rust 非常严格的类型和借用检查器将生产力负债转换为生产力资产的点。

    我把它比作在穿过建筑物的复杂电缆束中更换电缆。在 Rust 中,可以保证电缆槽中的每一根线,无论线束变得多么复杂和糟糕,都是可分离的,并且两端都有清晰的标签。因此,您始终可以“拉到一端”并通过更改结构中元素的类型或方法的返回类型来查看另一端在哪里。在较不严格类型的语言中,您不会获得此属性;电缆可以在电缆槽内的某处合并并相互影响,因此在进行更改后,您只能通过手动测试“嗡嗡作响”每条电缆。即使这样,您也永远无法确定当有人打开浴室灯时,您更换的东西是否会导致咖啡机关闭。

    这里有一个能说明Rust的重构能力在Xous中的作用的示例。在我们的图形子系统(我称之为GAM(Graphical Abstraction Manager[15]))中,处理信任级别的方式有一个问题。系统中的每个Canvas都有一个u8'分配给它,这是一个信任等级。当我开始写GAM时,我只知道我想要一些关于Canvas的可信任度的概念,所以我添加了这个变量,但并不确定它到底会被如何使用。几个月后,系统增加了带布局的上下文概念,布局是定义特定类型互动的多画布结构。现在,你可以有多个信任等级与一个上下文相关联,但我已经忘记了我之前放在Canvas结构中的信任变量--并且在上下文结构中也添加了另一个信任等级数字。你可以看到这是怎么回事:只要我有简单的测试案例,一切都能正常工作,但当我们开始在应用程序上弹出窗口,然后在窗口之上的菜单等等,疯狂的行为开始显现,因为我已经混淆了信任值的存储位置。有时我在更新Context中的值,有时我在更新Canvas中的值。它有时会表现为一个逐个击破的错误,有时则表现为一个并发错误。

    这一直是困扰我的一件难以描述的事,而GAM已经成长为一个有许多移动部件的5千行的畸形代码。最后,我决定必须对此做些什么,但我真的不希望这样做。我认为我搞砸了什么,这次调查将以重写整个模块而告终。

    幸运的是,Rust给我留下了一根小小的绳子。Clippy,也就是Rust中的 "linter",在我认为应该使用信任度变量的地方发出了一个警告--我在Context中存储了它,但之后没有人提到它。这很奇怪--它应该在每次重绘上下文时都是必要的!因此,我开始删除变量。所以,我开始删除这个变量,看看有什么问题。这很快让我想起,当画布被创建时,我也在画布内存储了信任度,这就是为什么我有这个悬空的引用。一旦我有了这个线索,我就能够重构信任的计算,只参考那个基础真理的来源。这也让我发现了其他一直潜伏着的错误,因为事实上我从来没有行使过一些我认为是常规使用的代码路径。经过几个小时的探究,我对这一切是如何运作的有了清晰的认识,我用简单易懂的API重构了信任计算系统,而不必折腾整个代码库。

    这只是我在维护Xous代码库时使用Rust的许多积极经验之一。这是我第一次抬头挺胸,以积极的态度走进一个大版本,因为这是第一次,我觉得也许我有机会能够以诚实的方式处理困难的bug。我花在脑子里找借口的时间越来越少,以证明为什么事情是这样做的,为什么我们不能接受那个 Pull Request,而花更多的时间思考所有的事情可以变得更好,因为我知道Clippy在支持我。

    对看此篇文章的 Rust 开发者的告诫

    无论如何,对于一个硬件人(指作者本人)来说,这是很多关于软件的吐槽。很快就会有软件开发者提醒我,首先,我做的是电路和铝壳,而不是写代码的,所以我没有资格抱怨软件。他们是对的。我实际上没有接受过“以正确的方式”编写代码的“正式”培训。当我在大学时,我学习了麦克斯韦方程,而不是算法。我永远不可能成为一名专业的程序员,因为我连最简单的编码面试都过不了。不要让我写链表:我已经知道我不知道如何正确地去做;你不需要向我证明这一点。这是因为每当我发现自己在写一个链表(或任何其他基础数据结构)时,我都会立即停下来,质疑所有让我走到那一步的人生选择:这不是库的用途吗?难道我真的需要重新发明轮子吗?如果在编码面试中表现出色与实际编码能力之间存在任何关联,那么您绝对应该对我的意见持保留态度。

    尽管如此,在花了几年时间使用Rust并阅读了无数关于该语言的文章之后,我觉得也许发表一篇关于该语言的批判性观点的文章会让人耳目一新。

    参考资料

    [1] Rust: A Critical Retrospective:https://www.bunniestudios.com/blog/?p=6375: https://www.bunniestudios.com/blog/?p=6375

    [2] Xous: https://betrusted.io/xous-book/

    [3] QNX: https://en.wikipedia.org/wiki/QNX

    [4] Xous: https://github.com/betrusted-io/betrusted-wiki/wiki

    [5] Precursor: https://precursor.dev/

    [6] 称为PDDB: https://www.bunniestudios.com/blog/?p=6307

    [7] CPU运行在FPGA中: https://github.com/betrusted-io/betrusted-soc

    [8] CHERI: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/

    [9] 正在改变: https://gankra.github.io/blah/fix-rust-pointers/

    [10] 第12章: https://docs.python.org/3/tutorial/venv.html

    [11] eli5 风格: https://doc.rust-lang.org/rust-by-example/attribute/cfg.html

    [12] 正式的语法定义: https://doc.rust-lang.org/reference/conditional-compilation.html

    [13] crate-scraper: https://github.com/betrusted-io/crate-scraper

    [14] 令人惊叹的: https://github.com/betrusted-io/crate-scraper/blob/main/builds.rs

    [15] Graphical Abstraction Manager: https://github.com/betrusted-io/xous-core/tree/main/services/gam

  2. 差生文具多   在小组 2047 回答问题

    如今频发的重大公共事件会让民心思变么?

    最近无非就是上海封城和几个公共安全事件而已,与其说是民心思变,不如说是对这10年来社会毫无进步的失望。对未来的预期变差是真的,但远没有到大多数人活不下去的程度。

    更何况中产和脑子活络的底层,还可以走移民这条路,没必要留在国内和共产党死磕。

  3. 差生文具多   在小组 2047 回答问题

    64事件在共产党的无数罪恶中可以说是微不足道,但为何政府却对64最为敏感?

    六四事件中的学生得到了相当一部分政治局常委和军头的支持。邓小平在镇压前夕已经预料到自己跑路的可能性,进入北京的戒严部队,也做好了和其它部队交火的准备。换句话说,六四事件差点颠覆掉共产党政权。

    邓小平在六四事件只担任中央军委的党职,并不是共产党的总书记,也没有担任任何公职,但是邓小平在六四事件时却强行废除赵紫阳的职务,并提拔对学生态度更强硬的江泽民作为接班人。从这个角度讲,现在的二共领导体系,从六四后推是不具有程序上的合法性的。纪念六四,就是质疑二共的合法性,此事当然是绝对敏感的事件了。

  4. 差生文具多   在小组 2047 回复文章

    对于泉州一类【网络白名单】地区突破网络封锁的一种方案及其可行性讨论

    我对楼主表述的理解是:境内部署一批分布式的节点,通过P2P的方式中转流量到境外服务器,以规避地区性白名单。

    这样的做法至少有以下几点问题:

    • P2P流量无论怎样混淆、伪装,对端的IP总是可见的。如果你的翻墙服务对公众公开,网警就一定可以运行客户端程序,获取境内节点的IP地址。

      由志愿者运行的节点会对志愿者构成法律风险,网警可以很容易恐吓、处罚运行节点的志愿者,让流量无法在境内中转。

    • 这种方法无法隐藏境外服务器的IP地址。如果境内节点是由可信的人部署的,那么他们就面临着被逮捕、处罚的危险;如果任何人都可以参与部署境内节点,那么网警就可以运行中转程序,获取境外服务器的IP地址。

    现阶段最有效的翻墙方式,我认为仍然依靠密码学方法消除流量统计特征,海量境外IP池避免IP定点封锁,这就离不开中心化部署和运营的服务器。

    翻墙本质上是政治问题,而非技术问题,所有翻墙手段都基于【中国离不开西方】这一事实。比方说,中国不封锁域名前置,是因为很多非政治敏感的西方网站也部署在这些域名下,中国要和西方开展贸易、技术交流,就需要这些域名可以正常访问。如果有一天信息封锁升级到全国域名白名单,甚至IP白名单,那什么技术都没有用了。

  5. 差生文具多   在小组 2047 回答问题

    美国的驴象两党为什么可以占据几乎全部的民选公职?

    两级分化可能和胜者全得的选举人团制度有关。

    美国大多数州会把所有选举人票分配给该州普选得票最高者,因此一个政党推出的候选人要么拿到全州最高普选票,要么落选。这种制度下即使小党拿到了一定比例的普选票,但只要竞争不过大党,就没有生存空间。

    如果把选举人票按普选票比例分配,那么得票不高的小党也有机会瓜分大党的选举人名额,这种制度下小党就可以获得充分的话语权。

  6. 差生文具多   在小组 2047 回答问题

    江泽民怒斥记者的事件,你们觉得是记者故意找茬,还是蛤蛤恼羞成怒?

    应对公众媒体是现代政治家的基本素养,江泽民面对张宝华一串提问,语无伦次,恼羞成怒,很明显素质不够格。

    膜蛤表面为膜,实质为辱,目的就是解构中共塑造的领导人权威。《怒斥记者》本来就是拿江泽民开涮的段子,结果还真有红小将膜出感情了。

  7. 差生文具多   在小组 2047 回答问题

    反贼普遍说中国GDP造假,实际早就衰退了,那为什么各大国际组织和权威机构承认中国的经济增长呢?

    反贼普遍说中国GDP造假,实际早就衰退了.

    请先找出“反贼普遍说GDP造假”的证据来,不要虚构指控。

  8. 差生文具多   在小组 2047 回复文章

    中文推特墙国化,根本就是一个海外版微博

    这个,推特质量低……好像不是中文推特的问题吧。

    推特又不像微博那样去强迫你关注什么人的帐户,新用户注册以后的默认推送都不错,中文圈里端传媒、CDT、大翻译,以及一众香港活动人士的推文质量也很高。换句话说,你看到什么样的内容,完全取决于你关注了什么人的帐户。

    就比如我们的刘大使上班时间看什么黑丝,那是你自己去关注的,赖不到推特头上,对吧。

    当然我得说推特的搜索结果确实被黄推污染的挺严重,但是第一推特搜索本来就不好用,第二不关注不就好了。

  9. 差生文具多   在小组 2047 回答问题

    中国科技在发展中国家属于什么水平?

    中国科技的体量在发展中国家中是顶级水平,即使和发达国家相比也不逊色。

    世界知识产权组织发布的《2021年全球创新指数》显示,中国的创新指数在2016年进入全球前30名,2019年进入全球前15名。2021年,中国的创新指数排名全球12名,低于法国,高于日本,香港,在中等收入国家组中排名第一。

    全球创新指数主要体现创新环境,属于综合性指标。全球创新指数包括监管环境,商业环境,基础设施,信贷投资开放程度和知识型劳动力。

    例如,中国的科技公司要采购任何上游设备,绝大多数均可在本地解决,这反映了中国的基础设施和产业链优势,是创新指数的加分项。但同理,中国相对不透明的监管环境则变成了创新指数的减分项。各项综合,中国排名全球12名,既非“全球唯一的完整工业体系”这样厉害,也非“遍地贪污腐败”这样不堪。

    中国的R&D经费占GDP比例为2.401%,排名全球13,低于美国,日本,台湾等国,高于法国、荷兰,挪威,也高于其它所有发展中国家。

    R&D经费占GDP比例和一个国家的产业结构有关,R&D经费占GDP比例越高,说明知识型产业的比重也越高。考虑到中国的GDP体量,中国的知识型产业体量为世界第二,仅低于美国。

    因此中国的科技创业环境不像很多人想象的那么不堪,科技产业的体量确实庞大。

    但是从很多第一手的经验看,中国在半导体,精密仪器,医疗设备、高端化学品上仍然非常依赖发达国家的技术,中国目前的科技体量,有赖于庞大的人口技术和承接全球产业链。但是“科技水平”只有一线人员的一手经验,这个指标很难量化,目前也没有见过这方面的定量研究和报告。

  10. 差生文具多   在小组 2047 发表文章

    这两天在找卫生工兼职,请问洗地和清场哪一个工资更高?

    如题,听说洗地是底薪+每平米5角加成,多洗多赚,清场是每月定薪+退职补助,固定工资。

    缺点是工资被拖欠不敢讨薪,会被其他同事洗地的洗地,被清场的清场。

  11. 差生文具多   在小组 2047 回复文章

    墙内自学编程的一点心得

    rust虽然复杂,但看官网上的教程就能学会

    rust是系统编程语言,要用rust写出有实际价值的程序,只学会语言还不够,需要有高并发编程/系统编程的经验。比如很基础的进程通信、锁机制、async/await、socket这些都要掌握,否则单线程下只能写出玩具程序。

    不清楚楼主的学习阶段,楼主可以自己试着实现一个高性能网站后端,或者网络代理协议(比如geph),如果能写出这样的软件,相信很多公司会抢着要人的。

    python也是一门工夫在诗外的语言,python编程不难,难的是应用领域。比如数据科学至少要熟悉numpy、pandas、tensorflow这些包,除此之外还有相应的算法,简单如PCA,复杂如DNN。举个例子,现在的业务需求是训练神经网络,用无人机摄像头识别特定行人并跟踪,那么工程师应该能想到使用哪种技术架构并自己实现之。

    对外部知识依赖最少的领域就是前端HTML+CSS+JS三件套了,不需要懂任何数学或计算机系统知识,就可以写出像样的程序。另外,除了网页前端、APP开发也比较容易上手。绝大多数成功的转码经验都是转到这两个方向的。

  12. 差生文具多   在小组 2047 回复文章

    “中国人民智未开,不够理性,一下子放开选举,会不会选出个小鲜肉当总统?” --论【孔多塞陪审团定理】

    一下子放开选举,会不会选出个小鲜肉当总统?

    这就掉入网评员的话术陷阱了,选小鲜肉当总统有啥不好?很多人都有明君思想,总是想着天降皇上为自己作主,结果李世民没等来,等来了张献忠。

    依我看,小鲜肉总统形象好、沟通能力强。民主国家领导人最重要的能力就是沟通能力,有时候沟通能力还会在国家生死存亡关头发挥重要作用。凭这一点,就强过“疯狂宇宙”“通商宽衣”的习近平总书记,换作习总书记,黑土地早被T72犁过二十遍了。

    其次小鲜肉总统政治资历浅,折腾不起来。有话云治大国若烹小鲜,又有话云稳定和发展是各方利益的最大公约数,换一个没有背景、四平八稳的小鲜肉当总统,正好国家少折腾、不折腾,人民发大财。换作英明神武的习总书记,一会儿“东升西降”,一会儿“伟大复兴”,结果还没复兴成,自己先动态清零了。

    再说了,英雄不问出处。已有的经验证明,年轻男演员当总统无一不是优秀的政治家,反倒是经验丰富的老油条、家天下的山大王遇事最慌、跑得最快。

  13. 差生文具多   在小组 2047 回答问题

    如何在墙内通过自由职业柔软地躺平?

    前后端通吃的话,可以去web设计的freelance网站上注册,接做网站的活,当然懂设计还能进一步提高自己的议价权。如果消费水平低,干一个月阔以玩三个月。

  14. 差生文具多   在小组 2047 回答问题

    本人想开设一个良心机场,但是害怕被抓,心理挺矛盾的,请教一下各位大佬如何选择

    机场这个圈子很乱,做大了有盈利,别人眼红就会举报。你的朋友开机场被抓,很可能就是被同行举报。另外,做机场有一笔不小的现金流,也会被警察盯上,光是为了罚款,警察就有足够的动力去抓人。

    如果楼主想做良心机场,那么可以考虑只接受加密货币付款。加密货币的缺点是门槛高,会把很多用户拦在外面,优点是匿名,对于法币付款方式(例如银行卡,第三方支付网关),只要警察有足够的动力总是可以查到人的。

  15. 差生文具多   在小组 2047 回答问题

    我同学是粉蛆该怎么办???

    建议和你现实中的同学搞好关系,不要争论这些,因为你和他争论政治观点没有用。

    人是很难被说服的,观点交锋到后面就不是为了真理而辩论,而是为了捍卫面子而辩论。双方一旦进入了defensive状态,谁都说服不了谁。

    你说服了他,最多也就转化一个人,还费不少精力。你说服不了他,对方反手一个举报,直接社会信用分-8964。

    真正有用的讨论是speech,在2047这类公开平台讨论也姑且算是某种speech。你想,你微信群里跟同学怄气,会被举报给网警,在匿名网上说8964反而会有成百上千的浏览量。这样对比起来,在匿名网上发言简直是爽爆了。

  16. 差生文具多   在小组 2047 发表文章

    【怀旧向】方舟子:网站设计的四项基本原则

    网站设计的四项基本原则——网站评点之一



    ·方舟子·

    (原载《中国青年报》电脑周刊)

    在开始正式评点网站之前,我需要先表明自己的评点标准,提出自己的“网站观”。网站观可以分成两派:一派是先锋派,更注重的是网站的艺术性,重视表面的设计更甚于实质的内容,尽量采用最新的技术,而不管现在还在普遍使用的各种浏览器是否都能支持这些技术。他们要求访问者迁就他们的设计,有的在第一页就预先声明:本网站需要用某某浏览器某某版某某分辨率阅读。那些未达其要求的访问者若扬长而去,对设计者来说似乎是无关痛痒的事。另一派是保守派,更注重的是网站的实用性,尽量与各种版本的浏览器兼容,尽可能多地争取访问者。

    我是属于保守派的,如果嫌“保守”不好听,也不妨称为迎合大众的“实用派”,而把先锋派改叫孤芳自赏的“艺术派”。根据实用派的哲学,我提出网站设计的四项基本原则如下:设计简明实用,资料富有特色,内容定期更新,浏览普遍适用。下面再详细地讲一讲这四项原则。

    一、设计简明实用。一个装潢精美、漂亮的网站,在访问者第一次访问时,也许能留下良好的第一印象,但第二次访问时,这种印象就要打大折扣,第三、第四……次之后,对设计艺术的印象也就等于零。所以,如果希望访问者能频频回访的话,大可不必在网站的艺术设计上煞费苦心,因为吸引回访靠的是内容而不是设计艺术,何况装潢的精美往往是以牺牲下载速度和浏览器的兼容性为代价的。因此之故,在评价一个网站时,我首先看其设计是否简明实用,而不是是否精美漂亮,值得特别注意的有几点:

    1. 不用大量的装饰性图象。现在绝大部分人还是通过电话线拨号上网,为避免这些人等得不耐烦,也为了避免加重网络负担,每一页装饰性图象的总量不要超过40K为宜。能用2色的不用16色,能用16色的不用256色或全色,分辨率也不应超过72dpi。有的网站迎面而来就是一幅大达几十上百K的全色题图,简直就是想把访问者拒之门外。

    2. 慎用背景。如果喜欢用背景的话,也应该用浅色的、图案简单的、若有若无的背景,以不影响读者阅读为准。许多网站喜欢用花哨的背景,也有的喜欢用黑色背景,对许多人的眼睛来说,都是难以忍受的。只要想想,世上的书几乎全都是白底黑字,也就不难明白其中的道理。

    3. 绝对不用背景音乐。许多人是在上班时间或三更半夜上网浏览的,进入某个网站后突然间乐声大作,是很令人尴尬的一件事。何况网页背景音乐的音响效果都非常差,喜欢边听音乐边浏览的人大可以自己用家中的音响播放。如果是手上有一首绝响不与人共享于心不忍,那也应该让访问者有选择是否播放的自由。

    4. 少用或者不用Java。我这不是存心跟SUN过不去。我承认Java有许多其他用途,我也承认若在网页上执行某些复杂的功能时也许Java有其优势,问题是,绝大多数的网站使用Java,不过是用来执行发布公告、更换图标、简单检索之类的简单功能,用Javascript或perl可以执行得更快、更好,用Java反而容易出错乃至导致死机,即使执行顺利,也要整台机器都停下等着它下载、执行(至少在我的机器上是这样的),令人难以容忍。有的人误以为在网站上使用Java可以让人觉得“很专业”,其实完全不是那么回事。会自己编写Java程序也许算得上专业,但是这些在网上使用的Java小程序到处都有现成的可抓,没有人会因为你用了它们就觉得有什么了不起。

    二、资料富有特色。目前中文网站似乎有一种追求“大而全”的倾向,以为如果无所不包、面面俱到,就能吸引最广大读者。这个如意算盘本来也打得不错,但是在已有了许多大而全的网站之后,再加入你一个,又能有什么效果?何况许多网站的框架固然搭得很“大”,“全”却是未必的,毕竟不是人人都有那份精力。所以,在评价一个网站时,我更注重资料的“特而全”,先选一个还很少见的主题,然后再尽量地收集与之相关的资料。这样的网站,更有存在的价值,也更能拥有一批热心的读者。可惜在这方面,中文网站做得实在不够,举例来说,世界闻名的外国古典诗人都有一个或数个内容翔实甚至颇具专业性的网站,而我至今还没见到有屈原、陶渊明、李白、杜甫、苏轼的主题站点。

    三、内容定期更新。做网站不是一锤子买卖,心血来潮做完了就可以从此弃之不顾。这个信息社会日新月异,有责任心的网站管理员应该随时删除过时的信息,增添新的内容。大型的网站应该一、两天更新一次,中型的网站应该一、两个月更新一次,小型的网站应该一、两季度更新一次,至少那些已过时的链接应该即时删除、更换。

    四、浏览普遍适用。国内的网民也许都已经被IE征服,所以国内的网站基本上都是按IE的标准设计的。但是海外的网民大部分仍坚持使用Netscape。所以,网站的设计,应该同时兼顾这两种浏览器,而且不能只根据最新的版本设计,还要顾及前一个版本。那些只有某个浏览器或最新版本才具有的功能,就先不要使用。指定访问者使用某个版本的某种浏览器是很令人反感的,而更令人反感的是,有的网站竟然还要求访问者先下载特制的浏览软件才能阅读其网站。在这么做之前,不妨先想想:你的网站的魅力是否强到这种程度,读者乐意于为了阅读你的网站而特地去安装一个软件?

    既然这四项基本原则还未被载入“网络宪法”强制执行,而且网站的连接速度也与访问者的所在位置有关,那么,在开始评价特定的网站之前,我觉得应该在本文的最后向读者交代一下我所在的位置和使用的操作系统及浏览器:我是在美国加州南部,用英文版视窗95和Netscape4.5上网的,外挂中文之星2.5阅读国标码,外挂南极星阅读大五码。

    1999.3.13.

  17. 差生文具多   在小组 2047 回复文章

    汇报一下我在社区“志愿”服务期间都干了什么

    实时保供、及时保供、“统筹”保供……虽然这种事在意料之中,不过希望我们永远不要对不公正麻木。

    感谢你的分享。

  18. 差生文具多   在小组 2047 回复文章

    为河马测核酸——政治运动造就的恐惧和夸张

    这次防疫运动确实与文革和大跃进有很多相似性,共同点是政治运动之前,言论环境的封闭和倒退;政治运动之中,自上而下的KPI体制,各级为了最小化责任,层层加码完成行政目标。

    这次防疫运动中清零责任类似大跃进中的KPI:中央落实到省,省落实到市,市落实到区,党政负责人作为责任人,完不成清零指标就撤职。压力传导到社会的末梢,就会出现全楼隔离,入户消杀等等惨状。

    我不认可“上面本意是好的,都是下面执行的错”这种说法,这种指令式的、压迫式的系统,本身就是制度之恶。这种制度下,官吏为了完成KPI变得残忍、无情、暴戾。民众在极权的压迫下变得怯懦和沉沦。温家宝曾经说过“政治体制不改革,文革还会卷土重来”,现在深以为然。

  19. 差生文具多   在小组 2047 回答问题

    墙内小粉红经常说“美帝国主义亡我之心不死”,我觉得这有点可笑。根据美国官方的说法,“美帝国主义亡我之心”死于哪一年?

    首先需要明确“美帝国主义亡我之心”当中的“我”,指的是中国共产党,而非文化和地理意义上的中国。

    50年代末,毛泽东为了应对美国的和平演变理论,提出了“美帝国主义亡我之心不死”的口号。毛之后,历代中共领导人都把和平演变看作头号外部威胁,因此这个口号背后的执政理念一直延续到今天。

    需要说明的是,和平演变理论是杜勒斯在若干次非官方演讲和论文中提到的,和平演变理论从未进入美国政府的官方政策。美国的政策圈认为,随着西方思想的传播和中、苏、越生活水平的提高,和平演变是必然,而非人为推动的结果。

  20. 差生文具多   在小组 2047 回复文章

    【读书笔记】秦晖:传统中华帝国的乡村基层控制——汉唐间的乡村组织

    感觉这篇笔记很有意思。提及中国传统乡村的治理,人们的刻板印象通常是宗族社会、皇权不下县。秦晖的研究揭示出传统社会的原子化要比想象中严重的多,编户齐民的吏治帝国才是传统的常态。

    我突然联想到,“皇权”不下县,正是民国时期的常态,民国时期可能是中国历史上少数中央权力难以触及到基层的时代。

  21. 差生文具多   在小组 2047 回复文章

    【读书笔记】秦晖:中国经济史上的怪圈:“抑兼并”与“不抑兼并”

    有一点问题,即楼主认为这里“过程的公正”是指什么?

    我注意到中国古代土地兼并和资本的扩张有某种相似性。小企业(自耕农)资产规模小,缺乏抗风险能力,遇到危机不得不出售资产(土地),由此总是会随着时间推移,资本或土地集中到少数人手中。

  22. 差生文具多   在小组 2047 回答问题

    如何看待「49 年入国军」这个梗?

    49年的解放军大概率会:

    • 在朝鲜战场上堵枪眼、举炸药、冻成冰雕连,供全国人民作为榜样学习。

    • 从朝鲜战场上回来,三年吃了个大饱。

    • 好不容易没撑死,接下来十年健了个大身。

    49年解放军的后代还有可能赶上:

    • 90年代全民大就业。

    • 2020全民大保健。

  23. 差生文具多   在小组 2047 回复文章

    【转载】深入浅出的政治学常识——为什么有人的地方就有政府?

    @natasha #185760 是的,好多政治学入门科普,国外译本翻译质量差,国人作者的科普又大多夹杂中共意识形态。这本书讲得通俗易懂,查了一下作者李宇晖应该是这位

    https://ecdm.fandom.com/zh/wiki/李宇晖

    看作者的介绍是美国加州大学政治学在读博士,属于新一代“民运”(非贬义),可惜知名度不高,网上没什么资料。

  24. 差生文具多   在小组 2047 回复文章

    【转载】深入浅出的政治学常识——为什么有人的地方就有政府?

    这篇文章真的挺不错的,我觉得哪怕看50%都受益匪浅。

    标题感觉起的不太好,“给孩子看的政治学”这样的标题会让人低估正文的信息量,其实政治常识大约99%的成年人都未必了解。

  25. 差生文具多   在小组 2047 回答问题

    如何评价“坚决克服轻视、无所谓、自以为是等思想,始终保持清醒头脑,毫不动摇坚持“动态清零”总方针,坚决同一切歪曲、怀疑、否定我国防疫方针政策的言行作斗争。”?

    这里的重点难道不是

    应检尽检、应隔尽隔、应收尽收、应治尽治

    如果不说出处,我还以为这话是陈全国同志讲话稿摘录出来的。

    几年前高压治理、网格化管控措施在边疆试验成熟,将来一段时间这些措施就会借着疫情管控推广到内地和中心城市。

  26. 差生文具多   在小组 2047 回答问题

    设计一种字体或者文字组织方式,使之能够以较高的概率使社交平台的文字识别系统误判(将敏感词识别为非敏感),会对改善当前恶劣的网络环境有帮助吗?

    你说的这种技术已经有了,叫做反爬虫字体,简单来说就是自己定义一套CSS,网页中实际的字体编码则是私有编码(不仅是私有编码,还会定期更换,加大爬取难度)。网页渲染的时候,根据私有编码画出文字,爬虫只能爬到乱码。

    另外是否有人了解商业公司训练和部署一个覆盖全国的用于文字识别的神经网络至少要多长时间?

    OCR的准确率没有想象中那么高,特别是文字存在排版、有干扰的情况下准确率更低,达不到实用水平。如果一定担心OCR,在图片上划一些线即可,做法类似验证码。

    我的建议是不要想着去和国内的审查系统做斗争,普及便易的翻墙手段才是王道。无论开发出怎样的混淆方法绕过机器审查,最终都过不了人工审查这一关。换句话说,你的战友能看懂的,审查人员也能看懂,更不用说审查方还可以用事后惩罚等措施无限推高发言的机会成本。

  27. 差生文具多   在小组 2047 回答问题

    中共是否闭关锁国后会掐断国际互联网?

    分三个问题:

    一、

    中共计划在2025年完成全国各级应急广播系统及平台建设……在有网友质疑该系统,是不是为了以后断网做准备的?

    这位"网友"想得太多了,应急广播系统是很基础的民防设施,不必什么都和“中共作恶”挂钩。

    请见美国的紧急广播系统,必要时候会掐断民用广播(含短信)向民众报警

    https://zh.wikipedia.org/zh-hans/紧急广播系统

    此广播系统最著名的事件就是2018年夏威夷导弹虚警

    二、中共是否闭关锁国?

    不好说。中共的最高利益是保证政权的连续性,如果闭关锁国有利于中共的存续,那么中共就会这么去做。

    需要知道,中共不是铁板一块,它是一个权力寻租和官员效益考核的缝合体,这其中既包含习近平/赵家人的利益,也包含权力机器上大大小小官员的利益。或许有些高层很享受定于一尊的感觉,但闭关锁国对其它各方利益都是重大打击,是否闭关锁国要看两方博弈的结果。

    我个人认为中共在政治和文化上会愈加封闭,体现在盲目自大和排外;经济上的开放程度基本不变,以国内市场绑架西方利益;在人才流动上会只进不出,尽可能吸收西方国家一批中高层华人带着技术和管理经验回国创业(例如最近的GPU创业潮)。

    三、假如闭关锁国,中共是否会掐断互联网?

    当然会,都闭关锁国了,难道留着互联网方便大家起事不成?

  28. 差生文具多   在小组 2047 发表文章

    如何在网站上部署hCaptcha

    详细内容参考hCaptcha官方文档:https://docs.hcaptcha.com/

    本文可作为教程和文档的补充。

    使用hCaptcha前需要注册账号,注册完成后,会得到两个令牌

    • 一个令牌叫site key,是32个字符的uuid。site key放在前端,也就是需要插入到显示验证码的网页上,因此是可以公开的。

    • 另一个令牌叫Secret Key,是40位的16进制数。Secret Key放在后端,不可公开。

    部署hCaptcha分两步:

    1. 在网页上插入hCaptcha控件,这段代码负责显示hcaptcha控件。比如说你希望在个人博客的评论区上添加验证码,就把这个控件放到发布按钮附近。

    2. 在服务器代码上部署后端验证代码,这段代码会把用户的验证码递交给hcaptcha服务器。比如你希望别人发布评论的时候填写验证码,就把后端验证代码添加进发布评论的逻辑里。

    在hcaptcha验证过程中,存在三方:用户(填写验证码的人)、网站服务器(你要部署hcaptcha的服务器)、和hcaptcha的官方服务器。hcaptcha的工作流程是:

    1. 用户填写验证码;

    2. 验证码上传到网站服务器上,上传的内容称为Response;

    3. 网站服务器把Response和自己的Secret Key一起打包,发送给hcaptcha的官方服务器

    4. hcaptcha的官方服务器把验证结果发送给网站服务器

    5. 网站服务器根据验证结果,决定是否放行。

    6. 在网页上插入hCaptcha控件

    1.1 首先在网页上引入hcaptcha脚本:

    <scri src="https://js.hcaptcha.com/1/api.js" async defer></scri>
    

    1.2 然后在欲展示验证码的位置,插入以下html

    <diw class="h-captcha" data-sitekey="your_site_key"></diw>
    

    (这里把script写作scri,div写作diw,防止被WAF拦截)

    这里可以随意插入html,但必须满足以下三个要求:

    1. 必须包含“h-captcha”的类名;
    2. 必须包含“data-sitekey”的属性,属性值是你的site key;
    3. html内部必须是空值(在这里,div块之间不能有内容)

    做完以上两步后,hcaptcha脚本就会自动在你的网页中插入验证码控件了,无需其它操作。

    1. 在后端引入hCaptcha验证

    2.1 在用户提交请求时,携带Response

    这需要修改提交请求的表单,因后端程序不同而异。

    2.2 向hcaptcha服务器发起请求。

    现在,后端需要打包Response和secret key,向hcaptcha服务器发起请求。

    不同的后端语言,发起请求的方式不同,官方给出的例子是curl:

    curl https://hcaptcha.com/siteverify \
      -X POST \
      -d "response=CLIENT-RESPONSE&secret=YOUR-SECRET"
    

    hcaptcha的官方服务器返回的是json,取里面的“success”字段,如果是true,即为通过,反之不通过。

    Python的例子如下(使用request)

    def verify(secret, response) -> bool:
        # 用request发起post请求,表单数据里有secret和response两个字段
        r = requests.post('https://hcaptcha.com/siteverify', data={
            'secret': secret, 
            'response': response
        })
        
        # 从json里读取success字段
        valid = json.loads(r.text).get('success')
        if valid:
            return True
        else:
            return False
    

    Rust语言的例子如下(使用Reqwest)

    
    /// response: 前端提交的验证码
    /// secret: secret key,需要保密
    pub async fn verify_captcha(response: &str, secret: &str) -> Result<bool, std::io::Error> {
        use serde_derive::Deserialize;
        
        #[derive(Deserialize)]
        struct Response {
            success: bool,
            challenge_ts: String,
            hostname: String,
            error_codes: Option<Vec<String>>,
        }
    
        const URL: &str = "https://hcaptcha.com/siteverify";
        let form = [("response", response), ("secret", secret)];
        let client = reqwest::ClientBuilder::new().build()?;
    
        match client.post(URL).form(&form).send().await {
            Ok(resp) if resp.status().as_u16() == 200 => match resp.json::<Response>().await {
                Ok(r) if r.success => Ok(true),
                _ => Ok(false),
            },
            _ => Ok(false),
        }
    }
    

    在后端逻辑中加上上述的“verify”函数,就可以使用hcaptcha验证码了。

  29. 差生文具多   在小组 站务 回复文章

    2047的备份计划

    2047是否打算用B2或者Amazon S3之类的云存储备份?

  30. 差生文具多   在小组 2047 回答问题

    如何看待法轮功天安门自焚事件,你觉得这件事情是真的还是假的?

    我觉得自焚这个问题争来争去,到底还是没跳出中共的舆论陷阱。

    我并不是鼓励或者赞同自焚,但是在历史上,自焚作为一种最极端的抗议与表达诉求的方式,明明很正常好吧?60年代南越僧人释广德自焚抗议吴廷琰政府对佛教徒的打压。照片传出以后震动西方,间接导致吴廷琰政府垮台。

    另一些自焚抗议的人例如西藏藏人,最近十年来一直有藏人用这种最痛苦的方式抗议中共对藏人的迫害。还有70年代的乌克兰人奥勒克萨(Oleksa Hirnyk),自焚抗议苏联当局对乌克兰文化的打压。近期的如2020年俄罗斯女记者伊琳娜,在俄罗斯政府门前自焚抗议当局迫害新闻自由,这个事件有摄像头全程录像,过于惨烈我就不放了。

    再次声明我并不鼓励或者赞同自焚,只是需要指出以极端手段结束生命来抗议,从古至今一直存在,从历史上看这是很正常的现象。古罗马政治家卡托切腹,经过缓慢而痛苦的死亡表示对凯撒的抗议,日本古代也有类似的腹谏,武士切腹以死明志,表达对领主的抗议或挽回名誉。

    把自焚和邪教挂钩本身就是一种宣传战,你无论承认或反驳,都中了宣传的套路。就算真的有人自焚,我认为这和藏人一样,只是一种极端的抗议而已,和所谓的蛊惑、洗脑并不挂钩。

    最后说回自焚事件的真实性本身,共产党主动烧人或者法轮功“教唆”自焚的可能性都不大,毕竟法轮功教义禁止自杀。我认为这件事情小概率是法轮功教徒自发行为(类似藏人抗议),大概率是共产党方面作假。法轮功方面针对自焚事件提出了很多疑点,相对有理有据,如果这些疑点不能合理解释,我更倾向作假这种解释。

  31. 差生文具多   在小组 2047 回答问题

    难民怎么好意思嫌弃食物不够好吃?

    这些难民不是盲流,他们在战争爆发之前也有自己的生活。你看不上的难民,很多原来也是公司白领,学校老师,过着朝九晚五的小日子。他们都是有自己梦想的人,他们有的还想着努力工作,攒钱在基辅安家。

    但是战争让他们无法生活。那些把孩子辛苦养大的父母,正期望给他找个好大学,但是陪伴孩子十几年,抵不过俄军的一颗子弹;那些努力工作的小白领,正欢天喜地的搬新家,就看着自己的房子在火箭弹前化为乌有。

    这个女人几天前还做了美甲,和你的邻居、家人、朋友没有任何区别,几天之后就变成了街头的尸体。

    所以这些过着平静生活的人必须要逃亡,就算抛下自己打理了几十年的家,也不得不做难民。

    我真心觉得你们大国人讲话多积一点口德,不要看见难民两个字就一脸鄙弃,多想想如果自己换到这个位置会怎样?某大国真的就以后保证不出现这种事情?他们和你一样也是有自己生活的人,也有做人的尊严,不是养猪场一样的对待就能打发的。

    当然有些大国人本来就非常热爱猪圈,觉得人就应该和猪一样管起来。某些大国人不仅认为自己是猪,还觉得全世界其他地方的人也不要做人,最好来和他们一起做猪,这样世界就完美了。

  32. 差生文具多   在小组 2047 发表文章

    用Nginx配合Cloudflare,打造CC防火墙

    Cloudflare免费版提供不计上限的DDOS防护,几乎可以拦下100%的网络层DDOS攻击。然而,如果服务器的配置很差,那么即使是流量很小、不足以触发Cloudflare防护的HTTP DDOS攻击也可以使服务器宕机。

    这里介绍一种防御HTTP DDOS攻击(也叫CC攻击)的方法,用Nginx的限速功能,拦截绕过Cloudflare的漏网之鱼。

    Nginx使用漏桶算法限速,漏桶算法把传入请求看作往桶里注水,桶会以一定的速度往外漏水(放过合法请求),漏桶自身也有一定的容量,可以承受突发的注水(接受突发请求),如果桶满水就会溢出(拒绝超出限制的请求)。

    Nginx使用漏桶算法限速用limit_req_zonelimit_req这两个指令。一个和cloudflare配合使用的例子如下:

    http {
    
        limit_req_zone $http_cf_connecting_ip zone=my_limiter:20m rate=2r/s;
    
        server {
    
            limit_req zone=my_limiter burst=30;
    
        }
    }
    

    limit_req_zone指令的用途是分配内存,对每个来访问的IP,记录其一段时间内的访问次数,因此这个指令必须放在http块里。上面指令的意思是,设置一个新的限速器,命名为my_limiter,占用内存20MB,此限速器可接受的最大访问频率为每秒2次。

    因为服务器套了cloudflare,所以nginx看到的是CF的IP,而不是用户的IP。cloudflare会把用户的IP放在cf_connecting_ip请求头里,可以用Nginx变量$http_cf_connecting_ip表示从http请求头中获取用户IP,对每个用户的IP进行限速。

    注意这里的IP以文本形式记录,因此会浪费一些空间,不过20MB的内存足以支持数万的IP连接。

    limit_req可以放在http、server、或location块里,表示指定该区域的可接受的突发请求速率。

    nginx会把拦截的请求写入error.log,可以在日志看到nginx的拦截记录,被拦截的请求不会交给后端处理,而是返回503错误。

    这样的配置足以防御一些小型的DOS攻击或者高频爬虫。

  33. 差生文具多   在小组 2047 发表文章

    给有志于匿名建站的坛友,科普一下防止CDN被绕过的方法

    很多墙外匿名站出于安全的角度,使用CDN隐藏真实IP,不幸的是有很多方法可以绕过CDN。

    IPv4的总数是有限的(42亿个),网络上有很多扫描器在一刻不停的扫描,一天就可以过一遍全网IP。如果网站没有做好防御,这些扫描器就会找到IP地址上面对应的网站。很多扫描器的数据甚至是公开可查的。

    幸运的是,只要做足防御,就完全不用担心源站IP泄露。这里科普一下防止源站IP泄露的方法。

    设置防火墙

    cloudflare会提供回源IP的名单,见https://www.cloudflare.com/ips/。我们要做的就是把这些IP全部添加进防火墙白名单。

    这样除了cloudflare,其它一切ip就都没法连接服务器的https端口了。

    防火墙白名单的具体规则是:

    1. 默认拒绝所有的入站IP。防火墙的拒绝模式有reject和drop两种,reject会明确返回一个拒绝信息,drop则直接丢弃入站包。我们要选deny而不是reject;
    2. 允许cloudflare的IP地址,从443端口连接服务器;
    3. 允许任意IP连接22端口,这是为了能SSH连上服务器。SSH加固是另外一个话题,这里暂且不提。

    本文这里没有用防火墙限制出站连接,如果对安全有需求,也可以禁用所有的出站连接。通常而言,对入站规则做设置就足以防止IP扫描了。

    我通常用的防火墙是nftables,在debian/ubuntu系统上,可以这样设置防火墙:

    (1). 安装nftables

    sudo apt install nftables
    

    (2). 编辑nftables文件

    sudo vim /etc/nftables.conf
    

    然后写入如下的配置文件

    flush ruleset
    
    table inet firewall {
        chain inbound {
            type filter hook input priority 0; policy drop;
            ct state vmap { established : accept, related : accept, invalid : drop }
            iifname lo accept
    
            ip saddr {
                173.245.48.0/20,
                103.21.244.0/22,
                103.22.200.0/22,
                103.31.4.0/22,
                141.101.64.0/18,
                108.162.192.0/18,
                190.93.240.0/20,
                188.114.96.0/20,
                197.234.240.0/22,
                198.41.128.0/17,
                162.158.0.0/15,
                104.16.0.0/13,
                104.24.0.0/14,
                172.64.0.0/13,
                131.0.72.0/22
            } tcp dport { 443 } accept
    
            tcp dport 22 accept
        }
    
        chain forward {
            type filter hook forward priority 0; policy drop;
        }
    }
    
    

    上面这个配置文件会:

    • 默认drop所有入站包
    • 默认drop所有转发包
    • 默认接受所有出站包
    • 接受所有来自cloudflare ip地址,向443端口建立的TCP连接
    • 接受全网IP向22端口连接
    • 允许本地环回上的连接(数据库和Apache等服务器需要)

    (3). 最后保存配置并刷新

    sudo nft -f /etc/nftables.conf
    

    输入sudo nft list ruleset,如果显示上面的配置文件,就说明保存成功。

    对nftables不熟的朋友,也可以用Ubuntu自带的ufw建立防火墙,安全性是一样的。

    # 启用ufw
    ufw enable
    
    # 允许所有出站,禁止所有入站
    ufw default allow outgoing 
    ufw default deny incoming
    
    # 允许22端口
    ufw allow 22
    
    # 允许cloudflare ip地址连接443
    ufw allow from 173.245.48.0/20 to any port 443 proto tcp
    ufw allow from 103.21.244.0/22 to any port 443 proto tcp
    
    # (以下略)
    

    打开cloudflare的客户端验证

    按照上面的方法,就可以100%防止IP扫描。不过,黑客可以向cloudflare works上传脚本,利用cloudflare的IP地址去扫描。这种攻击形式很少见,不过,为了防止这种形式的探测,我们还要打开验证CF客户端证书。

    具体而言,需要打开nginx配置里的以下选项:

    ssl_verify_client on;
    ssl_client_certificate /xxxxx/cloudflare_certificate.pem;
    

    最后,需要让nginx拒绝无效请求,打开以下选项

    ssl_reject_handshake on;
    

    但是需要注意,最后这个指令只在1.19.4以上的nginx版本可用,你可能需要自己编译nginx。

    其它注意事项

    除了ip扫描,如果网站在配置CF前已经有了DNS记录,那么从这些DNS记录也可以匹配到源站IP。如果是全新的服务器,在配置完成之前,一定不要连上公网。