TGTGInsighttelegram intelligenceLIVE / telegram public index
返回频道列表
Hypercube's Channel avatar

TGINSIGHT CHAT

Hypercube's Channel

@SmartHypercube_channel

科技

@SmartHypercube 随便发最近关注的东西 欢迎找我私聊讨论 可以使用 Telegram 的转发功能转发消息

Subscribers218频道当前订阅规模
Tracked posts244频道帖子计数
Recent reach3,757最近帖子视图总和
Recent posts

最近帖子

第 12/21 页 · 共 244 条

发布 1月6日

Hackergame 将选手 ID 加上电子签名称为 token,用于题目认证选手。考虑到题目中的认证逻辑可以被选手看到,但不应该能生成他人的 token,所以不能用 HMAC。现有做法是:将选手 ID 转为字符串,与椭圆曲线签名再 base64 的结果拼接,例如“42:MEUCIQCE……”。题目需要: A1. 将收到的 token 按冒号切开。 A2. 将右侧 base64 解码后验证是否为左侧的合法签名。 A3. 用整个 token 或只用左侧隔离不同选手的环境,以及限制资源用量。 A4. 用整个 token(加盐)哈希作为题目答案。不能只用左侧是因为不应该能生成他人的答案。 关键问题是一个选手能否对应多个不同但都合法的右侧部分。如果能的话,A3 必须只用左侧,不能用整个 token。A4 一个选手会对应多个不同的题目答案,处理起来很棘手。如果不能的话就全简单了,A3 和 A4 都可以直接用整个 token。 想确保一个选手只能对应唯一 token 的话,需要注意以下细节: B1. 按冒号切开的这一步不要写出漏洞,例如不要接受“42:garbage:MEUCIQCE……”。 B2. 椭圆曲线签名可以被人为变换产生另一个不同但也能通过数学验证的签名(malleability),解决这个漏洞的标准做法是规定只有某一个合法,另一个非法。 B3. 很多 base64 库会把不同输入对应到相同输出,例如“A/B=”改成“A/ B=”、“A/B”、“A_B”、“A/C=”可能都可以得到相同输出,应当只允许标准形式。Python 所有版本的 base64 库都有此问题。 B4. 尤其不要不验证就直接相信左侧的 ID。 libsodium 可以解决以上所有问题,它不像 OpenSSL 等库提供底层算法,而是提供绝大多数程序实际需要的高层功能,考虑了各种细节。crypto_sign 函数把 ID 变成 token,crypto_sign_open 函数变回来。 C1. 不用自己切分 token 了。 C2. 它生成的 token 格式不直观,一定程度上避免绕过 crypto_sign_open 直接看 ID。 C3. crypto_sign_open 能确保一个选手对应唯一 token,遇到其他形式或验证失败都不会返回 ID。 延伸阅读: https://hdevalence.ca/blog/2020-10-04-its-25519am

337 views

发布 12月31日

我每天吃饭的时候会在直播平台上找一个循环连播电视剧的频道看,但最近发现某个这样的频道有一个小问题:它循环长度约等于 24 小时,导致我总是看相同的一些集。我在想如果我做这样循环连播的频道,应该把循环长度控制在多少最合适呢? 我的第一反应是应该和 24 互素,但时间是连续的,并不是以小时为最小单位离散的。第二反应是应该令最大公因数最小,但这意味着任何无理数循环长度都一样最优,显然不是我想找的答案。我其实希望每天看到的位置和前几天看过的都相隔足够远。 我认为答案应该是黄金分割比,令循环长度为 24 小时的 0.382、0.618、1.618 等倍数。这正好和另一个我思考过的问题相同:在色调的一圈 360 度上选择颜色,该怎么选出 n 个颜色,让它们两两都相隔尽量远?当 n 已知时 n 等分即可。当 n 未知时(不断要求再增加一个颜色),如果 m 等分了前 m 个颜色,第 m+1 个颜色只能放在某两个颜色中间,离两侧都太近。按照黄金分割比,总把第 i 个颜色选在 i*0.618*360 度处,按我的理解是最优的。植物的叶片也近似是按这个规则生长的,可以在总数量未知的情况下尽量均匀分布。 参考: https://en.wikipedia.org/wiki/Golden_angle

292 views

发布 12月23日

尝试录屏并剪辑了 Advent of Code 中两道题的做题过程,之后可能还会发更多: day 18: https://youtu.be/i4EB36076Pw day 19: https://youtu.be/toJVMxv-AuE 我以前思考过如果直播或录制我写代码的过程,有没有可能别人会想看。我一直挺怀疑的,不过前两天和 @zzh1996 聊天时他提到一些细节做法可能有参考的价值,例如: - 使用 copilot 的频率 - 使用自动补全的频率 - 怎么快速看特定函数的文档 - 边写边编译检查还是最后统一解决编译报错 我觉得有道理,正好 Advent of Code 每道题比较简单、互相独立,更适合做成视频,就用它试试。 我以为别人不会对我的城市天际线存档感兴趣,因为只是我按自己的想法和目标玩的,对别人没啥用。但居然有人收藏下载,所以我又知道什么呢😂说不定这种录屏也会对别人有用吧。

285 views

发布 12月20日

看 Steam 年度回顾提到“创意工坊物品订阅者数”,疑惑我又不在创意工坊发布东西,哪来的订阅者?结果检查了一下,很久以前随便存到创意工坊的几个城市天际线存档居然有这么多订阅者和收藏者??比我认真做的 YouTube 视频反响好多了😂

254 views

发布 12月19日

https://github.com/SmartHypercube/lambda-response-limit 冷知识:AWS Lambda 官方说返回的 payload 最大 6MB,实际上每种语言的 runtime 有自己的编码规则,编码后最多 6Mi + 100 字节。 另外,不仅每种语言的 runtime 的编码规则不一致,而且这些规则都和真正发回 HTTP response 时编码的规则不一致。在 Python 中返回 {'s': '/' * 6291567} 或在 Node.js 中返回 {s: '/'.repeat(6291568)} 都能正好用尽最大长度(注意到 Node.js 能多塞进一个 / 字符,因为 Node.js 检查长度时,冒号后面没有空格),但收到的 HTTP response 的长度分别是 12583102 和 12583104,远远超过 6MB。真正发回 HTTP response 时,冒号和逗号后面没有空格,但 / 字符会不必要地被编码成 \/,这产生了以上结果。

267 views

发布 12月16日

Telegram Bot API 提到: 1. update 的 ID 从某个正整数开始连续递增,但如果至少一周没有 update,下一个 ID 会重新随机选择,而非连续递增。 2. getUpdates 接口的 offset 参数应当设置为收到的最大 ID + 1,小于它的 update 均被视为已确认。 3. 所有整数字段,除非特别说明,都可以安全地用 32 位有符号数存储。 这几条规则合在一起让我完全摸不着头脑——如果持续收 2^31 条 update 怎么办?如果 ID 被重新随机选择成更小的值了,调用 getUpdates 岂不是会意外确认掉还没收到过的新消息?为什么我实际测试时,指定更大的 offset 仍然可以拿到更小 ID 的 update? 读了相关代码后,我明白了为什么大家写的 bot 一般都不会出问题了,但仍然觉得整个设计挺怪异的: 1. 长时间没有 update 或者 ID 达到 2e9 都会导致重新随机选择 ID,随机范围是 2e6 到 1e9。 2. offset 参数比最新 ID 大 10 或比最早的未确认的 ID 小 1e5 就会被忽略,不确认任何 update,只正常返回实际列表。 3. 如果 ID 达到 2e9 时存在未确认的 update,似乎会修改它们的 ID(也可能是丢掉,这里我不太确定代码中一些函数的语义)。

261 views

发布 12月10日

我正在用 Haskell 完成今年的 https://adventofcode.com/ ,这里会在 12-01 到 12-25 每天给出两道简单的编程题,推荐大家来玩。我的解题代码: https://github.com/SmartHypercube/aoc2023-haskell

250 views

发布 12月2日

看到 https://t.me/toMyBlackParade/416 推荐 Bloons TD 6 这个游戏,最近玩了比较多,感觉好玩😆

649 views

发布 11月25日

我一直听说过两个乐理知识: 1. 如果同时演奏的音符频率之比可以约分为小整数比例,例如大和弦的三个音符是 4:5:6,就会听起来和谐。 2. 现代音乐一般使用一个等比数列中的频率,公比为 2^(1/12),也就是把 1:2 等比地分成 12 步。 它们显然是不互相兼容的,1 产生的都是有理数,2 产生的几乎都是无理数。之前才意识到 1 有一个问题:从一个初始频率开始,每次乘以 2 和每次乘以 1.5 分别得到的频率序列中,永远不包含相等的频率。虽然每个序列内部是和谐的,但两个序列之间无法约分,因为一侧有越来越多的素因子 2,另一侧有越来越多的素因子 3。 “纯律”就是按 1 的思想的,“十二平均律”则是 2。采用 12 步恰好可以产生非常接近 2:3 3:4 4:5 等小整数比例的频率(分别相隔 7 5 4 步),但不可能做到完全相等,总是有一点误差。以这些误差为代价,换来了可以随意改变音符的绝对频率,而保持相对频率关系完全不变。 因此也就不难理解为什么十二平均律中 12 个音并不被平等地使用了,人们一般按照“101011010101”的模式选出 7 个主要使用(可以循环移位,有 12 种选法),而不太用另外 5 个,因为这 7 个频率之间的关系更和谐一些。很多流行歌曲还会只用 5 个音,按照“101010010100”的模式(这个模式串就是上一个模式串循环移位后取反的结果,这是有原因的)。当你看到一首歌的简谱中只有 1 2 3 5 6 这 5 个数字时,就是这种情况了,例如《七子之歌——澳门》。

311 views

发布 11月19日

如果想把一些数据嵌入到网页中,供 JS 读取使用,一种做法是把数据放在 <script id="foo" type="application/json"> 标签中。(这里假设数据是 JSON 格式,其他格式同理。) 但这自然会带来注入漏洞相关的问题,不能将不可信的 JSON 直接这样放进 <script> 标签中,因为如果其中包含 </script> 就可以“逃出”这个标签了。这个问题看似很好解决,HTML 是相对比较容易 quote 的语言,只要把 & < > 三个字符分别换成 &amp; &lt; &gt; 就足够好了。但是实际上 <script> 标签内部不支持这些转义序列,这样替换会导致用 JS 读出数据时读到替换后的版本,还得费心再替换回来。 (顺便一提,如果想在 JS 里面写 foo = "</script>",写成 foo = "&lt;/script>" 并不等价,应该写成 foo = "<\/script>" 或 foo = "<" + "/script>"。) Django 自带的 json_script 功能会巧妙地把 & < > 分别替换为 \\u0026 \\u003C \\u003E,甚至不用改 JSON 序列化代码,只用在序列化结果上执行这个替换,就能保证对于所有合法的 JSON,替换前后语义完全相同,反序列化时感知不到差异。 不是所有格式都这么巧地有这样简单的替换方法的,一般地说,<script> 标签内很难安全地放不可信的数据。用 src 属性或 Fetch API 从另一个 URL 加载会方便得多。

280 views

发布 11月11日

前端开发时要考虑的东西好多啊,我最近才意识到自己之前开发的东西都存在这样一类 bug:使用浏览器的“后退”“前进”功能,或者“恢复关闭的标签页”功能时,可能会产生输入控件看起来已经填写了内容,但 JS 框架(Vue.js 和 React 都有此问题)则认为内容为空(或者 JS 代码中设置的默认值)的情况。如果在这种情况下直接提交表单,由 JS 进行处理,就会产生令人费解的错误,甚至导致误操作。 这是因为 Vue.js 和 React 都支持双向地把 JS 对象中的一个属性和网页上的一个输入控件绑定起来,属性被赋值时自动更新输入控件的值,输入控件被编辑时也自动更新属性的值。但如果一开始属性和输入控件的值就不相等,又没有任何一侧发生过变化,两侧就会一直不同步。这种情况要么是程序员故意为之,要么是浏览器在特定操作时(后退、前进、恢复关闭的标签页)自动恢复输入控件内容导致的。 我觉得根本在于浏览器支持很多复杂的功能,而通过写 JS 为网页引入额外的功能时,很难考虑到浏览器已有的各种功能分别会与新功能产生什么相互作用。

275 views

发布 11月4日

定义递归函数时,一般需要在函数体中引用正在定义的函数名: fact = x => x == 0 ? 1 : x * fact(x-1) 但想不引用也很容易,用 quine 的思路,把自己作为参数传给自己就行,并且每次调用自己时也要先把自己传给自己: fact = (self => x => x == 0 ? 1 : x * self(self)(x-1))(self => x => x == 0 ? 1 : x * self(self)(x-1)) 为了避免复制粘贴相同代码,我们可以用一个函数帮我们“打结”: 令 knot = f => f(f) fact = knot(self => x => x == 0 ? 1 : x * self(self)(x-1)) 调用自己时写 self(self)(x-1) 比较丑,可以先写成 rec(x-1),然后用一个函数帮忙“翻译”: 令 wrapper = f => self => x => f(self(self))(x) fact = knot(wrapper(rec => x => x == 0 ? 1 : x * rec(x-1))) 如果我们把先 wrapper 再 knot 合并为一步,就得到了 Z 组合子: Z = f => knot(wrapper(f)) Z = f => (self => x => f(self(self))(x))(self => x => f(self(self))(x)) 在惰性求值的语言中,x => f(self(self))(x) 可以化简为 f(self(self)) 而不会产生无限递归,这就是 Y 组合子,它在严格求值的语言中会无限递归: Y = f => (self => f(self(self)))(self => f(self(self))) Z 组合子和 Y 组合子可以求不动点,上面出现过的这个函数的不动点就是阶乘函数 fact: rec => x => x == 0 ? 1 : x * rec(x-1) 求不动点就是把函数返回的结果“时间倒流”作为参数提供给函数。想象这个函数收到的参数 rec 如果就是它的返回值本身(x => x == 0 ? 1 : x * rec(x-1)),那么一层层展开后正好就是阶乘函数: x == 0 ? 1 : x * (x-1 == 0 ? 1 : (x-1) * (x-2 == 0 ? 1 : (x-2) * (...))) 但 knot 并不是求不动点的函数,先 wrapper 再 knot 才能求不动点。 参考资料: https://lptk.github.io/programming/2019/10/15/simple-essence-y-combinator.html

296 views
12•••1011121314•••2021