[{"content":"我们应该在小学还是初中应该就知道了「田忌赛马」的故事，今天发生的事与这有关。\n同事重构了一段代码，想要观察与重构前相比的性能变化，于是他写了这样一段逻辑：\n用旧版和新版跑一段相同的输入，然后输出出处理每条数据的耗时。\n将所有耗时放在一个数组里，排序后输出统计信息（比如平均数、各百分位数）\n然后他惊人地发现，在一个百分位数之前，是旧版的速度更快；而在这个百分位数之后，都是新版的速度更快。就平均数而言，也是新版的更快。\n从代码上分析，理应新版是更快的。那么究竟新版和旧版谁更快呢？为什么会有好几个百分位数都是旧版更快呢？\n他百思不得其解，觉得这似乎是一种灵异事件。于是求助于我。\n我一看，也觉得十分奇怪。了解了他的逻辑之后，建议他不要分别统计两组耗时的统计信息，而是不要排序（也就是逐条对应，这样每对耗时都是对应同一个输入情况的），直接用新版减去旧版，然后对这个从旧版到新版耗时的变化进行统计。\n这个时候结果就很明显了：只有最大的百分位是正数，其他都是负数。那么我们很明显就知道了具体的情况：新版整体上都比旧版快，但是会有一些 jitter。\n那么在知道了这个的情况下，再回头看看刚刚的「灵异事件」，就能发现这本质上是一种「田忌赛马」了。不妨让我来举个例子。\n为了简化讨论，我们不妨设排序后旧版耗时是 [2, 4, 6, 8, 10]，新版从代码上说理应比旧版快，理想情况是 [1, 3, 5, 7, 9]，这样进行统计，结论显然是新版处理所有输入耗时都更短。\n但是，如果我们出现了 jitter，比如这个 1 变成了 10，那么我们排序后新版耗时就是 [3, 5, 7, 9, 10]。这样再进行统计的话，我们就能得到意外的结论：新版处理所有输入耗时都更长。但是实际的情况呢？是对于大部分情况都比旧版要快，只是偶尔有 jitter 而已。\n因为对应关系被打乱了，而导致了完全相反的结论！这完全就是「田忌赛马」的味道！\n事情的后续就是同事知道了 jitter 的存在，从而进行了相应的修复，于是新版的性能稳定比旧版表现好。\n可喜可贺。\n","permalink":"https://wr786.github.io/p/tian-ji-sai-ma/","summary":"\u003cp\u003e我们应该在小学还是初中应该就知道了「田忌赛马」的故事，今天发生的事与这有关。\u003c/p\u003e\n\u003cp\u003e同事重构了一段代码，想要观察与重构前相比的性能变化，于是他写了这样一段逻辑：\u003c/p\u003e","title":"「田忌赛马」导致的统计偏差趣事一则"},{"content":"IGMP 是一个网络层协议，用于在 IPv4 的网络上设置多播。\n具体来说，IGMP 允许设备加入一个多播组。\n什么是多播（Multicast）？ 多播使得一个应用可以仅仅发送一个数据包，就能让网络中的一组 host 都收到，就像群发邮件一样。\n多播通过在多个设备之间共享 IP 地址来运作。指向该 IP 地址的任何网络流量都将到达共享该 IP 地址的所有设备。\n因为多播是一对多的，所以只能用类似 SOCK_DGRAM 的 socket，而不能用 SOCK_STREAM。\nIGMP 如何运作？ 连接到网络的计算机和其他设备在想要加入多播组时使用 IGMP。支持 IGMP 的路由器侦听来自设备的 IGMP 传输，以确定哪些设备属于哪些多播组。\nIGMP 使用为多播预留的 IP 地址。多播 IP 地址在 224.0.0.0 和 239.255.255.255 之间的范围内（即D类网段）。并被分为\n局部多播地址：在 224.0.0.0～224.0.0.255 之间，这是为路由协议和其他用途保留的地址，路由器并不转发属于此范围的 IP 包。 预留多播地址：在 224.0.1.0～238.255.255.255 之间，可用于全球范围（如 Internet ）或网络协议。 管理权限多播地址：在 239.0.0.0～239.255.255.255 之间，可供组织内部使用，类似于私有IP地址，不能用于 Internet，可限制多播范围。 每个多播组共享其中一个 IP 地址。当路由器接收到一系列指向该共享 IP 地址的数据包时，它将复制这些数据包，将副本发送给多播组的所有成员。 IGMP 多播组可以随时更改。设备可以在任何时候发送 IGMP “加入组”或“离开组”消息。IGMP 不限成员数量和位置。\n使用 命令行 加入指定多播组\n1 sudo ip addr add 233.54.12.234/32 dev eth1 autojoin 或者\n1 socat STDIO UDP4-RECV:22001,ip-add-membership=233.54.12.234:eth1 \u0026gt; /dev/null 查看加入了哪些多播组\n1 netstat -gn 程序 setsockopt()参数\nIP_ADD_MEMBERSHIP: 加入指定多播组 IP_DROP_MEMBERSHIP: 退出指定多播组 IP_MULTICAST_IF: 设置将要发出multicast包的interface IP_MULTICAST_TTL: 设置发出的multicast包的TTL，默认TTL为1。 IP_MULTICAST_LOOP: 设置是否要将发出的multicast包也发给发送者，如果发送者也是那个多播组的一员。 具体使用可见：https://tldp.org/HOWTO/Multicast-HOWTO-6.html\n一般要写一个收/发multicast的程序，需要：\n建立一个socket。 设置多播的参数，例如超时时间TTL、本地回环许可LOOP等。（纯接收的话不用设置） 加入多播组。 发送或接收数据。 退出多播组。 Multicast Without IGMP 在实际使用中，我们发现，在有些机器上，即使我们没有加入多播组，也能收到多播信息。\n这是由于接收者和发送源在同一内网下，所以不需要加入多播组就能收到multicast。\n参考 https://www.cloudflare.com/zh-cn/learning/network-layer/what-is-igmp/ https://www.tenouk.com/Module41c.html https://unix.stackexchange.com/questions/140384/creating-multicast-join-for-tcpdump-captures https://superuser.com/questions/425665/multicast-streaming-without-igmp ","permalink":"https://wr786.github.io/p/multicast_and_igmp/","summary":"\u003cp\u003eIGMP 是一个网络层协议，用于在 IPv4 的网络上设置多播。\u003c/p\u003e\n\u003cp\u003e具体来说，IGMP 允许设备加入一个多播组。\u003c/p\u003e\n\u003ch2 id=\"什么是多播multicast\"\u003e什么是多播（Multicast）？\u003c/h2\u003e\n\u003cp\u003e多播使得一个应用可以仅仅发送一个数据包，就能让网络中的一组 host 都收到，就像群发邮件一样。\u003c/p\u003e","title":"Multicast \u0026 IGMP"},{"content":"如果你有这样的情景：\n需要创建一个字典，而字典的key都是很短（长度不超过8）的字符串。 同时你又需要追求高性能，希望能达到纳秒级的时延优化。 那么你会怎么做呢？\n很常见的做法是：\n1 std::unordered_map\u0026lt;std::string, T\u0026gt; dict; 但是其实，我们可以针对这个场合做出神奇的优化：\n众所周知，字符串是由字符组成的，而每个字符占1个字节，也就是说相当于一个int8_t或者uint8_t。 而如果key都不超过8，那么$8 \\times 8 = 64$，我们完全可以将其convert为一个uint64_t。即\n1 2 3 std::unordered_map\u0026lt;uint64_t, T\u0026gt; dict; char str[10]; // strlen(str) \u0026lt;= 8 dict.insert({*reintepret_cast\u0026lt;uint64_t*\u0026gt;(str), _}); 这么一看你就懂了吧！无须多言。最后我们上个效率对比吧，看看能有多大的提升：\n你也可以在此在线观看这个benchmark。\n此外，你也可以考虑采取其它实现方式的map，比如这里有位大神总结的Comprehensive C++ Hashmap Benchmarks 2022，根据你需要的场景，选择更合适的map，也能继续压低时延。\n","permalink":"https://wr786.github.io/p/hpctricks_map_with_string_keys/","summary":"\u003cp\u003e如果你有这样的情景：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e需要创建一个字典，而字典的key都是很短（长度不超过8）的字符串。\u003c/li\u003e\n\u003cli\u003e同时你又需要追求高性能，希望能达到纳秒级的时延优化。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e那么你会怎么做呢？\u003c/p\u003e","title":"【高性能C++奇技淫巧】当你要用很多短字符串当字典key"},{"content":"为什么要平摊分析？ 算法往往是会对内存中的数据进行修改的，而同一个算法的多次执行，就会通过对数据的修改而互相影响。\n为了解决计算上的困难，以及操作之间的不独立而导致的估算上界过松，我们就需要用到平摊分析。\n平摊分析的三种方法 平摊分析总共有三种方法，概括起来就是：\n聚集分析：n次总代价 / n 记账法：把之后要做的操作的代价提前考虑 势能法：用势能释放来支付未来操作的代价 其中势能法是我们需要重点掌握的方法。\n聚集分析 Aggregate Method 平摊代价 = n次操作总代价 / n\n通过总运行时间求平均得到平摊时间，不需要对操作序列的概率分布做假设。\n栈S上的三种操作 比如课堂例题，栈上的三种操作(PUSH、POP、MULTIPOP)，我们可以得到一个总时间上界$O(n^2)$，但它并不紧。\n在栈上，所有POP和MULTIPOP弹出的对象数不会多余PUSH入栈的对象数。\n因此，若进行n次操作，PUSH的总规模是$O(n)$，这也就使得POP和MULTIPOP的总规模也只能是$O(n)$。那么n次操作的总代价为$O(n)$，我们计算得到平摊代价=$\\frac{O(n)}{n} = O(1)$\n二进制计数器 观察INCREMENT操作序列\n每次操作，$A[0]$都反转 每两次操作，$A[1]$反转 每$2^i$次操作，$A[i]$反转 所以总反转次数为： $$ \\sum_{i=0}^{\\lfloor log(n) \\rfloor}{\\lfloor \\frac{n}{2^i} \\rfloor} \u003c n\\sum_{i=0}^{\\infin}\\frac{1}{2^i} = 2n $$ 那么总时间为$O(n)$，每个操作平坦时间为$O(n) / n = O(1)$\n记账法 Accounting Method 不必算出每一步实际代价，我们用一个虚构的代价，满足对任意k，前k步满足：\n$第k步存款 = \\sum_{i=1}^{k}平摊代价_i - \\sum_{i=1}^k实际代价_i \\geq 0$\n如何设计平摊代价？提前考虑之后的代价。\n栈S上的三种操作 仍然，在栈上，所有POP和MULTIPOP弹出的对象数不会多余PUSH入栈的对象数。\n那么我们如何设计平摊代价？答案是把POP类操作的帐记在PUSH头上。\n每次PUSH操作，我们对入栈的对象收费2元，1元用于支付实际费用，另1元存起来，为了支付这个对象以后可能发生的POP操作。\n由于栈中的对象数不可能为负，而每个对象被POP的次数不会多于它被PUSH的次数，所以存款不会小于0。\n那么，我们可以对POP、MULTIPOP收费0元。因为PUSH时已经预先付过它们的费用了。\n进行n次操作，PUSH总共记账$O(2n)$，POP和MULTIPOP总共记账$0$，那么总代价也就是$O(2n) = O(n)$。\n平摊时间为$O(n)/n = O(1)$。\n二进制计数器 注意到，每次INCREMENT只会把一个0反转为1，但可能把多个1反转为0.\n我们每次INCREMENT收费2元，其中1元用来支付将一个0反转为1的实际费用，另1元存在这个反转出来的1上，为了支付以后可能发生的反转为0；那么由于之前的存款，每个被反转为0的1上原本都有1元存款，也就支付了这次INCREMENT的费用。\n而1的个数总是大于等于0的，所以存款不可能为负。\n势能法 Potential Method 利用数据结构$D$的函数$\\Phi$定义平摊代价：\n$平摊代价_i = 实际代价_i + \\Phi(D_i) - \\Phi(D_{i-1})$\n其中，对于任意$i$，都要满足$\\Phi(D_i) \\geq \\Phi(D_0)$，这样我们就能保证我们给出了实际代价的一个上界。\n势能法与记账法有些类似，但是记账法是将存款存储在某些特定的对象之上，而势能法的势能是体现在整个数据结构之上的。\n栈S上的三种操作 仍然是栈的三操作，我们定义势函数为栈中对象的个数：\n开始时，栈是空的，所以有$\\Phi(D_0) = 0$. 栈中对象数始终非负，所以满足$\\Phi(D_i) \\geq 0 = \\Phi(D_0)$. 那么我们就能保证我们给出了实际代价的一个上界。\n那么对于作用在一个包含$s$个对象的栈上的第$i$个操作，\nPUSH\n势能差：$\\Phi(D_i) - \\Phi(D_{i-1}) = s+1 - s = 1$\n平摊代价：$a_i = c_i + \\Phi(D_i) - \\Phi(D_{i-1}) = 1+1=2$\nMULTIPOP(S, k)\n实际代价：$c_i = min(s, k)$\n势能差：$\\Phi(D_i) - \\Phi(D_{i-1}) = -min(s, k)$\n平摊代价：$a_i = c_i + \\Phi(D_i) - \\Phi(D_{i-1}) = 0$\nPOP\n同理MULTIPOP，平摊代价也是0.\n我们分析出三种栈操作的平摊代价都是$O(1)$，那么n次操作的总平摊代价就是$O(n)$。\n而这是总实际代价的一个上界。所以n次操作的最坏时间复杂度为$O(n)$。\n二进制计数器 我们定义势函数为数组$A[0\u0026hellip;k-1]$中$1$的个数。\n设第$i$次INCREMENT操作将$t_i$个1反转为0，那么实际代价为$t_i+1$\n设第$i$次操作后，数组中1的个数为$b_i$，那么\n如果$b_i = 0$，则第$i$次操作反转了$k$个1，那么$b_{i-1} = t_i = k$ 如果$b_i \u0026gt; 0$，那么有$b_i = b_{i-1} - t_i + 1$ 这两种情形都满足：$b_i \\leq b_{i-1} - t_i + 1$ 势能差：$\\Phi(D_i) - \\Phi(D_{i-1}) \\leq (b_{i-1} - t_i + 1) - b_{i-1} = 1 - t_i$\n平摊代价：$a_i = c_i + \\Phi(D_i) - \\Phi(D_{i-1}) = (t_i + 1) + (1- t_i) = 2$\n补充习题 INSERT \u0026amp; REMOVE_BOTTOM_HALF 请设计一个数据结构，能够维护一组n个不同整数组成的集合S，能够支持以下两种操作：\nINSERT(x, S)：将x加入S。 REMOVE_BOTTOM_HALF(S)：从S移除最小的$\\lceil \\frac{n}{2} \\rceil$个整数。 描述你的算法并给出最坏情况下两个操作的时间复杂度。采用势能法进行平摊分析，得到两种操作的平摊时间，选择合适的分析策略，令INSERT(x, S)的平摊时间为$O(1)$，REMOVE_BOTTOM_HALF(S)的平摊时间为$0$. 我们用一个无序的数组（或者，动态表）来存放所有的整数。\n定义势函数$\\Phi(D_i) = k|D_i| = kn$，那么\nREMOVE_BOTTOM_HALF(S):\n注意到，这步操作我们可以利用算法$Select(n, k)$选出中位数，然后再扫描一遍数组，将保留下来的数字复制到一个新的数组中，然后令S指向这个新的数组头，在实际时间2n内完成。\n但是，为了严谨起见，我们设它是在实际时间$cn$内完成的。其中$c$为大于1的常数。\n我们令$k = 2c$，即$\\Phi(D_i) = 2c|D_i| = 2cn \\geq 0$，那么\n势能差：$\\Phi(D_i) - \\Phi(D_{i-1}) = 2c\\lfloor \\frac{n}{2} \\rfloor - 2cn$\n平摊时间： $$ \\begin{align} a_i \u0026= c_i + \\Phi(D_i) - \\Phi(D_{i-1}) \\\\ \u0026= cn + 2c\\lfloor \\frac{n}{2} \\rfloor - 2cn \\\\ \u0026= 2c\\lfloor\\frac{n}{2}\\rfloor - cn \\\\ \u0026\\leq 0 \\end{align} $$ 所以平摊时间为0.\nINSERT(x, S)：\n势能差：$\\Phi(D_i) - \\Phi(D_{i-1}) = 2cn - 2c(n-1) = 2c$\n平摊时间：$a_i = c_i + \\Phi(D_i) - \\Phi(D_{i-1}) = 1 + 2c = O(1)$\n显然，最坏情况下，INSERT操作的时间复杂度为$O(1)$，而根据上述分析，REMOVE_BOTTOM_HALF的最坏时间复杂度为$O(n)$。它们的平摊代价根据上述分析，也分别为$O(1)$与$0$。\n[竞争分析] MTF(Move-to-Front) 链表访问 MTF算法 考虑一个问题：\n我们有一个线性表（比如单链表），在它上面：\n我们访问第$i$个元素的访问代价为$i$\n交换两个相邻元素的代价为某个固定常数值\n我们的目标是：通过“交换”调整原链表，使得n次访问的总访问代价最小。\n如果访问序列是已知的，那么我们当然可以针对这个访问序列设计一个最优的调整策略。 但是，如果我们并不能提前知道访问序列，我们可以考虑采取MTF方式来调整链表。\nMTF利用了一个事实：对于现实中的问题，比如分页，如果第$i$个元素在时间$t$被访问，那么它比较有可能在时间$t$的不久后再次被访问。（访问局部性 in ICS）\nMTF算法： 当第$i$个元素被访问，我们将它移动到线性表的最前面(Move-to-Front)。 而这个“移动”操作是通过$i-1$次交换完成的。 所以，当第$i$个元素被访问，总代价为$i+ i-1 = 2i-1$\n我们可以利用平摊分析证明：\nMTF不会比任何其他调整策略（包括最优策略）效率的4倍更差，甚至不需要假设存在访问局部性。\n定义势函数 设任意一个调整策略为算法A。我们定义在时刻$t$时的势函数为MTF作用下的链表相对于A作用下的链表的逆序对数的2倍。\n比如，在时刻$t$时，MTF的链表为$(a, b, c, e, d)$，A的链表为$(a, b, c, d, e)$，那么此时的势函数结果就是2。\n由于初始时，两个算法下的链表都是初始链表，相对逆序对数为0，所以$\\Phi(0)=0$。 而在任意时刻下，相对逆序对数都不可能为负值，所以$\\Phi(t) \\geq \\Phi(0)$。 那么我们就能保证我们能给出实际代价的一个上界。\n计算平摊代价 现在，我们考虑访问一个元素$x$。设$x$在MTF的链表里在第$k$个位置，在A的链表里在第$i$个位置。我们先假设A并不会交换其他元素。\n在MTF算法中，访问元素$x$并交换到前面的总代价为$2k-1$。 在算法A中，访问元素$x$的代价为$i$，设其进行其他操作的代价为$Q$，那么总代价为$i+Q \\geq i$。\n因为MTF算法将$x$移动到了表前面，而这个$x$之前在MTF的链表中前面有$k-1$个元素，在A的链表中之前有$i-1$个元素，所以它最多增加$\\min{k-1, i-1}$个新相对逆序对。与此同时，它最少减少了$k-1-\\min{k-1, i-1}$个旧相对逆序对。\n而由势函数的定义，势的变化最多为2倍的两者之差，即$4\\min{k-1,i-1}-2(k-1)$\n所以，平摊代价为 $$ \\begin{align} c \u0026= a + \\Delta\\Phi \\\\ \u0026\\leq (2k-1) + 4\\min\\{k-1, i-1\\}-2(k-1) \\\\ \u0026\\leq 4\\min\\{k-1, i-1\\} + 1 \\\\ \u0026\\leq 4i \\\\ \u0026\\leq 4(i+Q) \\end{align} $$ 也就得出了MTF的平摊代价不会比A的平摊代价的4倍更高。\n思考：如果算法A会交换其他元素怎么办？ 不妨假设A交换了2个相邻元素。 这并不会影响到MTF的实际代价。这会增加$2$给新势能。 但同时，它也增加了算法A的$Q$部分$1$。 那么，MTF的均摊代价虽然增加了$2$，但是它的界限却增加了$4$，结论仍然成立。\n所以，无论A怎么交换其他元素，都不会影响我们的结论。\n更多平摊分析经典问题： Splay树 红黑树 斐波那契堆 并查集 最大流 (Push-Relabel 预流推进算法) 动态表 / 哈希表 替罪羊树 ","permalink":"https://wr786.github.io/p/amortized-analysis/","summary":"\u003ch2 id=\"为什么要平摊分析\"\u003e为什么要平摊分析？\u003c/h2\u003e\n\u003cp\u003e算法往往是会对内存中的数据进行修改的，而同一个算法的多次执行，就会通过对数据的修改而互相影响。\u003c/p\u003e\n\u003cp\u003e为了解决计算上的困难，以及\u003cstrong\u003e操作之间的不独立\u003c/strong\u003e而导致的估算上界过松，我们就需要用到平摊分析。\u003c/p\u003e","title":"平摊分析 Amortized Analysis"},{"content":"不同角度对安全的定义 密码系统安全性应满足的条件\n系统在理论上是不会被破译的，也不能在实践中被破译。 系统的机密性不依赖于加密/解密算法和系统的加密，而只取决于密钥的机密性。 易于添加/解密操作、在软件/硬件中快速且易于实现。 加密/解密算法对密钥空间的全部元素均可用。 测量密码系统安全性的基本标准\n计算安全：解密加密算法的计算能力与计算时间在实际条件下不可用。 安全证明：密码系统的解密依赖于对数学问题深入研究的解决方案。理论上保证安全。 无条件的安全：攻击者不能在用没有限制计算能力与时间的基础上破译加密算法。极限状态下的安全。 除了一次性加密算法，理论上还没有绝对安全的密码系统。在实际应用中，只要能够证明所采用的密码系统是计算安全的，就有理由相信加密算法是安全的。[1]\n安全地存储密码 委托给可信任的第三方存储——OpenID技术 OpenID的理念是用第三方来完成用户验证。\n目前国外的网站如谷歌、雅虎等，国内的如腾讯等都已经提供OpenID服务。\n如果我们开发一个网站并选择谷歌的OpenID服务，那么用户就可以用Gmail的账号和密码登录，接下来用户认证的事情将由谷歌完成。\n优点：\n没必要自己存储用户名和密码，也就没必要考虑存储密码的安全性问题，从而减少开发的成本。 用户不用在网站上注册新的用户名和密码。这样既免去了填写资料的麻烦，也减去了记住一对新的用户名和密码的负担。 用Hash算法加密密码 常用的几种单向的哈希算法：\nMD5（已被破解，不建议在产品中使用） SHA系列（SHA1、SHA256、SHA384、SHA512等） 只使用哈希算法还是不够安全：\n猜测密码，找有没有相同的哈希值（类似离线攻击） 彩虹表（事先计算好大量密码与对应的各种哈希算法的哈希值） 加盐提高安全性 为了应对彩虹表，我们可以先往明文密码加盐，然后再对加盐之后的密码用哈希算法加密。\n所谓的盐是一个随机的字符串，往明文密码里加盐就是把明文密码和一个随机的字符串拼接在一起。由于盐在密码校验的时候还要用到，因此通常盐和密码的哈希值是存储在一起的。\n需要注意的是：我们要确保往每个密码里添加随机的唯一的盐，而不是让所有密码共享一样的盐。（否则可以针对这个盐生成彩虹表）\n破解方法：\n穷举法。由于哈希算法的快速高效，任何6位的纯数字密码即使加盐之后也能在数秒之内破解。随着计算能力的提高，黑客们低成本并且高效地破解高级别密码愈发存在可能。 通过历次密码泄露事件收集大量常用密码。这些常用的密码即使加盐也很容易破解。 用BCrypt或者PBKDF2增加破解的难度 为了应对暴力破解法，我们需要非常耗时的哈希算法。BCrypt算法应运而生。\nBCrypt最大的特点是我们可以通过参数设置重复计算的次数。显然，重复计算的次数越多耗时越长。\n目前已有开源项目（http://bcrypt.sourceforge.net/）实现了BCrypt算法并被业界广泛采用。\nPBKDF2同样也可以通过参数设定重复计算的次数从而延长计算时间。[3]\n安全地传输密码 非对称加密 非对称加密的模式是：\n乙方生成两把密钥（公钥和私钥）。公钥是公开的，任何人都可以获得，私钥则是保密的 甲方获取乙方的公钥，然后用它对信息加密 乙方得到加密后的信息，用私钥解密。 即使黑客拿到了公钥，没有私钥也是没有办法解密，不考虑彩虹表的情况，完全可以长期使用一对秘钥。[6]\nGPG加密\nGPG全称 GNU Privacy Guard，是非对称加密。\n如果你想给谁发送加密信息，首先你要得到他的公钥，然后通过该公钥加密后传给他，对方利用自己的私钥就可解密并读取文件了。[4]\nRSA算法\nRSA算法原理：http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html\n对称加密 对称加密的模式是：\n甲方选择某一种加密规则，对信息进行加密 乙方使用同一种规则，对信息进行解密[6] 对称加密两种常用算法：\n分组密码：每次只对固定长度的比特块进行加密，比如DES; 流密码：将提供的大量比特作为输入并可连续运行，从而加密；[5] 客户端和服务端进行通信，采用对称加密，如果只使用一个秘钥，很容易破解；如果每次用不同的秘钥，海量秘钥的管理和传输成本又会比较高。[6]\nHTTPS 基于HTTP协议，通过SSL或TLS提供加密处理数据、验证对方身份以及数据完整性保护。\n特点：\n内容加密：采用混合加密技术，中间者无法直接查看明文内容。 验证身份：通过证书认证客户端访问的是自己的服务器。 保护数据完整性：防止传输的内容被中间人冒充或篡改。[7] 其它可能的攻击方式及相应的防范措施 这里只考虑从网站服务器上破解用户密码的方法。\n在线攻击 通过代码在网站的用户验证环节试探用户的用户名和密码。\n攻击之前需要获取网站的网站协议(http,https,ftp,pop3,…)、完整的登录url（一般不可见）、登录失败信息(Login failed,请检查用户名、密码,…)。\n攻击流程：比如要攻击用户\u0026rsquo;Admin\u0026rsquo;的密码：使用程序循环模拟用户登录行为，从字典中依次选取密码，组装后向网站发送登录请求(request)，接收网站的反馈信息（response）并加以分析，如果没有返回登录失败信息，说明攻击成功。\n现有的软件工具：\n获取网站基本信息：BurpSuite 表单密码破解：hydra 防范措施：\n验证码、滑块拼图、限制失败登陆次数等。无法彻底杜绝。[2] 离线攻击 将获取的哈希密码保存到本地，利用自己的计算机对密码进行离线破解。\n必须要先判断密码的加密方式。之后将字典中的密码进行依次加密、比对。[2]\n现有的软件工具：\nJohn the Ripper hashcat 防范措施：\n优化哈希密码的存储技术，采取特殊的加密方式等。 参考 [1] 渔翁信息. 密码系统安全性的定义 [EB/OL]. https://www.fisec.cn/1244.html, 2019-02-12\n[2] 平平说科技. 黑客技术入门：密码攻击的原理和方法 [EB/OL]. https://cloud.tencent.com/developer/news/313489, 2018-09-13\n[3] 星朝. 如何安全地存储密码 [EB/OL]. https://www.cnblogs.com/jpfss/p/11024665.html, 2019-06-14\n[4] 守株待兔. GPG 加密解密简明教程 [EB/OL]. http://blog.sina.com.cn/s/blog_71f3890901011ig0.html, 2012-04-19\n[5] kph_Hajash. 网络信息传输的安全机制 [EB/OL]. https://blog.csdn.net/chuanglan/article/details/80627366, 2018-06-08\n[6] 徐辛承. 如何加密传输和存储用户密码 [EB/OL]. https://zhuanlan.zhihu.com/p/36603247, 2018-05-09\n[7] 会飞的狗~. HTTP和HTTPS协议，看一篇就够了 [EB/OL]. https://blog.csdn.net/xiaoming100001/article/details/81109617, 2019-08-03\n","permalink":"https://wr786.github.io/p/how_do_websites_save_passwords_safely/","summary":"\u003ch1 id=\"不同角度对安全的定义\"\u003e不同角度对安全的定义\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e密码系统安全性应满足的条件\u003c/strong\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e系统在理论上是不会被破译的，也不能在实践中被破译。\u003c/li\u003e\n\u003cli\u003e系统的机密性不依赖于加密/解密算法和系统的加密，而只取决于密钥的机密性。\u003c/li\u003e\n\u003cli\u003e易于添加/解密操作、在软件/硬件中快速且易于实现。\u003c/li\u003e\n\u003cli\u003e加密/解密算法对密钥空间的全部元素均可用。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cstrong\u003e测量密码系统安全性的基本标准\u003c/strong\u003e\u003c/p\u003e","title":"网站如何安全地存储和传输用户密码"},{"content":"友链页，下面是友情链接。\n","permalink":"https://wr786.github.io/links/","summary":"\u003cp\u003e友链页，下面是友情链接。\u003c/p\u003e","title":"Links"}]