ICMP攻击及防范

ICMP协议概述

IP协议本身无法处理诸如“得到一个包发送失败的事实”,“对网络进行诊断”这些操作。ICMP协议就是为了上述操作而包含在IP协议中的一个子协议。

ICMP协议(Internet Control Message Protocol)全称Internet控制报文协议,是TCP/IP协议族的一个子协议,用于在主机,路由器之间传递控制信息。最常见的是我们使用的ping命令就是通过发送ICMP包实现的。

ICMP包位于IP包内部,ICMP的包头一般来说紧跟着IP包头(通常IP包头为20字节,则ICMP包头从第21字节开始)。

ICMP包包含Type, Code, Checksum和Data域,其中Type指定了ICMP的类别,可大致分为两类——一种是请求及其回应,另一种是错误信息反馈。

以Echo请求为例,一个Type=8的包发向目标IP,如果目标服务器成功收到,回复一个Type=0请求。倘若中间出现路由错误,则中间路由器返回一个Type=3的包。

ICMP攻击及其防范

ICMP攻击有非常多不同的种类,接下来将列举一些常见的攻击。

ICMP隧道

首先讲一讲ping指令的原理,ping指令首先发送一个ICMP的ECHO REQUEST包给目标IP,并在data域包含一个唯一的16位标识符(这个标识符在Linux中是顺序生成的,而在Windows中是随机生成的),通常操作系统还会在data域后面添加一些无用的信息使得ICMP包长到达一个指定的长度。目标主机收到了ping指令后,将data域原封不动包裹在一个ECHO REPLY包中发回来。

对于一个ICMP ECHO REQUEST/REPLY包,由于其做的是类似于网络控制方面的事情,故通常不会被防火墙在意(除非防火墙一一分析每个包的内在data域有没有异常),而其data域可以携带任意的数据,因此可以借助ICMP包实现一个从服务器发送消息的程序。我在github上面也找到了一个实现ICMP隧道的程序,大家可以参考一下。

对于ICMP tunneling的攻击,有一些非常原始的防范方式,包括限制包的大小以及直接丢弃所有的ICMP包。但是在实际上这些方式都不可行。也有一些比较复杂的防范方式,包括在本机监听包,并在发现异常是,通过网络中的控制节点拦截攻击的流量。

DDOS攻击

PING FLOOD攻击

两个对称的主机,攻击者在一个主机上使用多线程发送大量包向目标主机。只要攻击者的主机的带宽大于被攻击者,被攻击者的带宽就会被完全占满,而真正的包无法到达。

对于这类攻击,可以在子网入口的路由器上设置过滤表,拦截攻击者IP。

Smurf攻击

Smurf-attack是一种原始的DOS攻击,攻击者伪造目标主机的IP为source IP,并发送一个广播的ICMP ECHO REQUEST指令。当指令被网络的其他主机回应后,回应的ECHO REPLY将涌入目标主机,导致目标主机瘫痪。

一般Smurf-attack攻击的解决方式有——对于主机,设置相应的网段,不在该网段的ICMP ECHO REQUEST都不会被相应。或者是对于一个子网路由器,丢弃掉发入子网的广播ECHO REQUEST。由于现在新的路由器都默认disable广播流量,因此smurf攻击基本已经成为历史了。

伪造IP的方式不一定在攻击者主机上面实现,比如在 DNS Amplification Attack 攻击中,攻击者可以通过UDP包,将目标主机的真实地址关联一个无关的IP,注入DNS服务器,将大量无关请求全部导向目标主机。

网络信息收集

通过ICMP攻击可以用来收集网络拓扑结构,首先,通过ECHO REQUEST可以探知一个主机的存在性,而通过一个TTL依次递减的一系列ECHO REQUEST包,观察其ICMP Time Exceeded 包的发送源即可获得一条网络的边。

甚至通过ECHO REPLY包,我们还可以探知目标机器的OS类型。返回TTL=128的机器是Windows系的,而TTL=64的机器是Linux系的。同时,对于一个任意Code域非空的ICMP包,Windows系主机将返回一个Code域为0的包,而Linux则不会。进一步,不同的系统版本对于TIMESTAMP REQ包的回应也不尽相同,我们可以借此更进一步确认。

这类行为因为不会造成严重的后果,故一般没有进行防范。

Ping of Death攻击

通过发送若干段IP超大包,并让目标主机接受后,拼接出一个大小超过65536的包,导致栈溢出。这种攻击仅发生在早起服务器上,新的服务器都通过修改其收包算法的边界条件判定解决了这个攻击。

总结

对于ICMP攻击的防范一般都需要用户服务器精细配置防火墙,或是在边界路由器中设置过滤规则。普通的丢弃ICMP包也是可行的,但是会使得很多网络诊断工具不可用。在参考了有限资料后,我认为ICMPv4和ICMPv6在上述攻击的防御上几乎没有区别,ICMPv6协议本身没有增加额外的防范机制的支持。

参考文献

ICMPv4 and ICMPv6: https://notes.shichao.io/tcpv1/ch8/

ICMP attacks:  https://resources.infosecinstitute.com/icmp-attacks/#gref

ICMP tunnels: https://www.notsosecure.com/icmp-tunnels-a-case-study/

ICMP floods: https://www.cloudflare.com/learning/ddos/ping-icmp-flood-ddos-attack/

原创文章地址:【ICMP攻击及防范】转载时请注明出处mhy12345.xyz

Express的Session不同步问题

近期使用express做项目,发现遇到了一个神奇的bug,从现象上来看是用户的session并不同步。具体来说有如下现象——

  • 页面A在session中加入了某个字段。并输出session,发现字段已经成功加入。
  • 页面A通过setTimeout设置5秒后再次输出session,发现字段仍然存在。
  • 页面B在页面A加载后的5秒内访问同用户的session,发现字段不见了。

经过研究,解决方式为在session创建时,指明resave选项为false——

resave字段为false的时候,倘若一个请求未修改session本身,则session不会重新写回内存。通常倘若两个同时发生的请求,只有一个修改了session。当resave为true时,未修改session的请求的session写回操作很容易覆盖另一个请求对于session的修改。

原创文章地址:【Express的Session不同步问题】转载时请注明出处mhy12345.xyz

Structural Deep Embedding for Hyper-Networks 阅读笔记

所谓的Network Embedding就是指,对于一个网络(或者简单地说,图),给每个节点一个特征向量X_i,而该特征向量能够表征网络中节点的结构信息。这篇文章是Network-Embedding中的一篇较为重要的文章。


Abstract

网络嵌入(Network Embedding)近年来吸引了很多人的注意,传统的方法多局限于由点与点二元组关系形成的图,但是,在实际生活中,图本身很难是标准的二元组关系,在有些情况下,一条关系(relationship)会涉及到三个甚至更多的主体,我们可以称之为超边(hyperedge)。当超边成为一个不可分割的独立整体,则传统算法会遇到不少困难。本文将介绍一种算法,用于对于超网络(hyper-network)的嵌入。并且证明,传统算法不可能成功Embedding存在不可分割超边的网络。

Introduction

对于hyper-network的embedding,一个非常简单的方式是将其转化为普通图,这里有两种常用做法,一种是直接将图中的超边转化为一个“团(clique)”,另一种是为超边本身分配一个虚拟节点,并向其包含的所有元素连边。他们分别被称作clique expansion和star expansion。在这些算法中,我们都假设了超边是可分的,即,一个超边包含的点集的子集仍然是有意义的,这在实际生活中是不存在的。上述的算法的诸多问题便是这篇文章需要解决的——

  • 节点的不可分解性:如电影的推荐中的<user, moive, tag>三元组的子集 <user,tag>是不合法的。
  • 结构保留特性:实现局部细节的embed是相对简单的,但是网络整体结构的embed却相对复杂。而我们还需要解决同时embed局部、全局的特征。

对于第一个问题,文章设计了不可分解的多元组相似性函数,这种函数本身的结构保证了超边的子集不会存在。我们从理论上证明该函数是非线性性的,如果使用带非线性层的神经网络实现,则可以实现强力的非线性性。

对于第二个问题,我们使用自编码器来保证相似的节点拥有相似的embedding。

Related Work

早期的图Embedding使用了线性代数的方式,使用图的主特征向量来作为图的编码。由于线性代数中的特征向量分解是一个相对昂贵的方式,因此实际上这种做法并不常用

近年来,一个叫做RamdomWalk的算法提出新的思路,通过刻画一系列随机游走的路径来Embed一个图。在此之后,很多学者都在其基础上进行了创新,诸如使用更好的刻画函数,或者引入深度学习来优化。

然而,上述所有的算法,假设了图是标准的由边的二元组构成的。在存在超边的途中,现有的算法不是无法保证超边的不可分解性,就是过于低效。

Notations and Definitions

术语

如右图所示,相比于图论中的“无向图”,我们的超图更类似于

一个“有向图”。也可类比于之前提到的<user,movie,tag>三元组,节点本身是有类别的。我们定义点集

V = \{V_t\}_{t=1}^{T}

表示所有节点可以按照类别分为T类。

超边边集则可以表示为

E=\{(v_1,v_2,\dots,v_{n_i})\} (n_i \geq 2)

最终第j类的第i个节点的Embedding可以计算为

X_i^j

对于N个节点,我们可以定义相似性函数

\mathcal{S}(X_1,X_2,\dots,X_n)

相似性

节点的一阶相似性(First-order Proximity)刻画了N个节点的直接相似性,其定义非常简单,只要这N个节点组成一个超边,则这N个节点的一阶相似性是1。当然,这里就隐含定义了这N个节点的任意子集都不存在一阶相似性。

显然,单纯的一阶相似性定义是不完善的,这时,我们可以进一步定义二阶相似性。

二阶相似性定义在任意两个节点上,当两个节点x_ix_j共有邻居时,他们应该是二阶相似的。这也应该反映到节点的Embedding上面。举一个例子,在上面那张图上,A_1的邻居应该是\{(L_2,U_1),(L_1,U_2)\},而由于A_1A_2拥有共同的邻居(L_1,U_2)则他们应该是二阶相似的。

Deep Hyper-Network Embedding

模型设计

我们计算的网络Embedding需要保证一阶相似性,即对于模型生成的Embedding值X_i,倘若一个点集组成了超边,则他们的X_i的一阶相似性函数应该尽可能大,反之尽可能小——

特征1:我们令X_i为模型生成的Embedding值,则

  • (v_1, v_2, \dots , v_n) \in E, \mathcal(S)(X_1, X_2, \dots, X_n)应该尽可能大(或者说至少得大于某个比较大的阈值)
  • (v_1, v_2, \dots , v_n) \notin E, \mathcal(S)(X_1, X_2, \dots, X_n)应该尽可能小(或者说至少得小于某个比较小的阈值)

有了上述定义,文章进一步通过列举反例,证明了倘若\mathcal(S)仅仅是依赖于X_1,X_2,\dots, X_n的一个线性函数\mathcal(S) = \sum_i W_i X_i,是无法正确刻画特征1的。当然,了解过深度学习的读者应该就能发现,这恰好是深度学习中的非线性层引入的动机。

后续讨论我们默认N=3,更大的N对应算法很容易在N=3的情况修改得到。下图是网络的结构图——

在输入节点Embeding (X_i, X_j, X_k),我们可以通过非线性函数将他们映射到一个非线性空间L中。

L_{ijk} = \sigma(W_a^{(2)} X_i^a + W_b^{(2)} X_j^b + W_c^{(2)} X_k^c + b^{(2)})

而我们进一步将L映射到一个概率空间S

S_{ijk} = \mathcal{S} (X_i^a, X_j^b, X_k^c) =  \sigma(W^{(3)} * L_{ijk} + b^{(3)})

上面两层共同构成了我们的非线性相似性函数\mathcal{S},最终的目标函数定义如下

\mathcal{L}_1 = -(R_{ijk} \log S_{ijk} + (1-R_{ijk}) \log (1-S_{ijk}))

上式中的R_{ijk}被定义为“节点i,j,k是否组成一个超边”的0\1值函数。

接下来,我们考虑如何生成二阶相似性。之前我们介绍了模型中的第二层和第三层,而模型的第一层就是用来保证二阶相似性的。从图中,我们可以发现,模型使用了一个AutoEncoder,那么任何AutoEncoder都需要一个可供编码的东西,我们这里使用AutoEncoder编码什么呢?作者设计了一个编码矩阵A_i.

H表示在图G(V,E)的关联矩阵,h(v,e)=1当且仅当v \in e,而D表示图的度数矩阵。令A = H H^T -D。在离散数学中,可以证明,A矩阵的第i行A_i刻画了节点i的邻居的信息。而AutoEncoder就可以如下构造——

X_i = \sigma(W^{(1)} * A_i + b_i)

\hat{A}_i = \sigma(\hat{W}^{(1)} * X_i + b_i)

对于AutoEncoder的损失函数,我们有一个小优化,即只计算A_i不等于0的项。

\mathcal{L}_2 = \sum_t ||sign(A_i^t) \cdot (A_i^t-\hat{A}_i) ||_F^2

最终模型的损失为

\mathcal{L} = \mathcal{L}_1 + \alpha \mathcal{L}_2

模型训练

使用SGD算法训练。

Experiment

Conclusion

 

原创文章地址:【Structural Deep Embedding for Hyper-Networks 阅读笔记】转载时请注明出处mhy12345.xyz

在Vue.js中使用Quill富文本编辑器自定义块级元素

再一次开发中,我试图在vue.js搭建的网站中引入富文本编辑器,我是用了vue2-editor. 这个库将开源富文本编辑器Quill包装进了vue.js的组件中。从易用性角度来说,非常令人满意。

不过,在我们的设计中,我们尝试使用富文本实现一个问卷系统,例如对于选择题,我们希望把一个包含了四个选项的“不可分割块级元素”作为一个整体,像插入图片一样插到富文本编辑器中。

实际上,插入块级元素本身是有先例可循的,比如视频插入模块,可以再富文本编辑器中插入一个iframe块。其代码可在github上面看到——

仿照上面的代码,我们实现我们自己的题目添加控件——

这是我们设计的独立功能模块,通过命令  Quill.register(SelectProblemEmbeder); 进行引入。在保存时,我们需要同时保存富文本的HTML信息,以及DELTAS信息,HTML信息可以用于渲染内容,可以通过vue2-editor暴露在外的content获取,而DELTAS信息可以用于保存富文本编辑的状态,供后续恢复。可以使用 let quill_deltas = JSON.stringify(this.quill.getContents());得到。其中, this.quill 是通过$refs获取到的模块内部信息。恢复时,我们读取上一次编辑的DELTAS信息,使用 this.quill.setContents(quill_deltas); 即可回复之前的状态。

原创文章地址:【在Vue.js中使用Quill富文本编辑器自定义块级元素】转载时请注明出处mhy12345.xyz

Efficient Large-scale Approximate Nearest Neighbor Search on the GPU 阅读笔记 – 实现与分析

由于篇幅太长,这篇文章的Introduction和Related Work在另一篇文章里。


PQT的简介

乘积量化树(Product Quantization Tree)是基于多层倒排表(Inverted Multi-Index)和分层PQ算法形成的。其核心思想将之前的一个码书(Codebook)修改为基于层次化的树形结构。树形结构可以加速实时的查询,以及离线的排序。相比于之前的多层倒排表,PQT拥有更多地簇(Bins)。最后PQT还巧妙地运用一些技巧在重排序中服用之前计算过的距离值。

右图阐述了一个PQT查询的例子。查询向量[x]_1[x]_2的各个部分一次经过4个Cluster的粗排序和5个Cluster的精排序。对于第一层而言,只有w个最佳的Cluster可以到下一层继续搜索,如图中阴影部分所示(w=2)。

PQT的结构

将样本集合中所有的向量x \in \mathcal{X}归类为簇B_l从而形成若干个互不相交的子集,\mathcal{X} = \bigcup_{k=1}^{K} B_k。接下来,我们将详述如何将一个向量映射到簇中,即如何构造映射m : \mathcal{X} \rightarrow \mathcal{I}_1 \times \mathcal{I}_2 \times \dots \times \mathcal{I}_P

索引树包含两层量化器,第一层是传统的P部分(P part)的PQ量化器,在这个量化器中,每一部分的码表含有k_1个码字。在第一层量化器之后,选出的簇进一步使用第二层k_2个码字的量化器量化。这种方法中,总共存在的簇有K = (k_1 \cdot k_2)^P个。

对于码书的训练,每一部分独立生成码书,首先量化器使用Lloyd Iteration中的Linde-Buzo-Gray算法生成,所有第一层量化器分出的簇进一步使用第二层量化。

对比在多层倒排表中,使用了两级的乘积量化,其中第二级用于排序,我们也用两级索引,至于为什么是两级,只是从经验上来看,两级能够最好得平衡簇的总个数。

PQT的询问操作

一个实际的询问操作包含四个步骤——

  • tree traversal
  • bin proposal
  • vector proposal
  • re-ranking

在tree-traversal步骤中,正如之前对PQT结构的叙述,每一个向量都能归类为一个level-2的簇中。通过剪枝的策略,tree-traversal步骤使用的向量比较次数实际上是相对较少的。在比较了所有k_1个level-1簇后,询问向量距离每一个簇的距离被排序,并且只有最近的w个簇被进一步计算。在提取出了最近的w个level-1簇,接下来这些level-1簇所对应的level-2簇的中心再和询问向量进行比较。在标准参数下,在四百万簇的数据集下,只需要80次向量比较。具体细节可参见论文。

在bin-proposal步骤中,从我们获取的“最好的”簇i=(i_{1,1},i_{2,1},\dots,i_{P,1})开始,我们仍然需要一系列临近的簇,以保证在后续的处理中,有足够多的向量可供选择。正如右图所示,我们需要找到一个顺序,合理组织“次好”的向量,使得遍历完所有较近向量的这组合。着这种便利,通常来说,可以看做是一个斜线从左下向右上方移动维护的下方区域。具体的实现方式,绿线是Dijkstra-based traverse,但是无法再GPU上面实现,而红色、绿色线则是可行的。对于每一个簇,我们都可以在离线阶段预处理出最近的簇。

使用Line Quantization进行重排序

通过上述的索引,每一个询问向量都被归类于最近的簇,并获得了一个量化损失(quantization loss)。在传统方法中,我们就需要依次对于簇所包含的所有向量进行比较,但是显然这样会有巨大的花销。而在我们的算法中,我们重用了一些信息以达到更优的效率。

离线预处理

在传统方法中,一个簇所包含的所有向量是不可分的,这就导致了我们必须花巨大的计算力去依次比较所有的向量。 文中提出的Line Quantization巧妙地解决了向量不可分的问题。即,虽然每一个向量【黑点】相对于level-2簇中心【红点】是不可区分。但是他却可以被投影到两个最近level-1簇中心的连线上【紫点】。巧妙的是,对于所有level-1中心,询问的x_1点与他们的距离都是已经算过的!

右图描述了如何计算对于任意询问向量y距离某一个数据库样本x的距离。其中,c_i,c_j为两个距离x很近的level-1簇中心。

 

(1)   \begin{align*} d(y,x) &= \sum_{p=1}^{P} d([y]_p, [x]_p)^2 \\ & \apporx \sum_{p=1}^{P} h_p(y,x)^2 \\ & \apporx \sum_{p=1}^{P} (b_p^2 + \lambda_p^2 \cdot c_p^2 + \lambda_p \cdot (a_p^2-b_p^2-c_p^2)) \end{align*}

通过这个式子,我们可以用我们之前计算过的,询问向量距离level-1簇中心的距离信息,为x找到一个更加精确的距离估计值。

在GPU上实现

这个模型非常适合在GPU上面实现,不过由于我并不太了解GPU的实现,顾就不在这里赘述,读者可以对照论文自行理解。

原创文章地址:【Efficient Large-scale Approximate Nearest Neighbor Search on the GPU 阅读笔记 – 实现与分析】转载时请注明出处mhy12345.xyz

The Design Philosophy of the DARPA Internet Protocols 论文阅读报告

本文介绍了Internet中最基本的协议——TCP/IP协议的设计思想。由David.D Clack所著,他作为整个TCP/IP协议设计的参与者,见证了协议从1973年最初的版本,逐渐被改进,到最终成型的一系列尝试。在文章中,作者总结了设计中的一系列目标,以及存在的问题。让我们得以窥见这一段艰辛的摸索之路。

各种协议的设计,其基本目标无非两个: 1) 能够尽可能的兼容协议发布之前已存在的硬件设施; 2)能够尽可能的有利于协议发布之后的因特网发展。这些基本目标不仅为我们理解TCP/IP的后续发展有帮助,也能够让我们知道,为什么在计算机科学领域,很多的实现都是建立在“协议”上的。

在基本目标之上,人们基于对于因特网的畅想,设计出了一系列有严格层次关系的次级目标,如稳定性,经济性,可解释性等。由于Internet协议的设计流程过于复杂,常常会遇到两种不同实现方式的权衡取舍,由于实现确立了稳定性、经济性这一类不同目标的重要性排序,故可以基于此进行取舍。这种目标导向的协议构建,一方面确实省去了非常多的试错的时间,另一方面,却会缺少一些长远的考虑(例如由于对性能的要求并没有卸载目标中,导致性能在早期完全没有被重视等等,又例如一对多的信息传递没有在早期协议中支持,导致在长时间无法实现)。

互联网协议的稳定性作为最重要的目标,毫无疑问来自于互联网历史上的军方背景。也在之后的普及中发现其局限性,报文协议(UDP)的诞生或多或少的解决了这个尴尬的境地。不过UDP能够成功诞生还是算非常幸运的了,通常来说协议的修改总是耗时耗力,有时还会和原来的协议冲突,诸如一对多的广播就一直没有能够成功的引入。总之,协议设计者的远见与用心真的是非常的重要。而TCP/IP协议的设计流程,从文中提到的寥寥几句可以推测出,确实不是设计者拍脑袋的结果,而是无数次试验,并用一篇篇论文推动而来的。反观现在良莠不齐的学术论文,我觉得在协议设计中的这种,基于某个具体目标驱动的研究,才是能让学术界向前推进的正确方法。

由于网络的协议的更改需要兼容之前的功能,而论文写作至今又过去了接近30年,TCP/IP协议在30年前存在的缺陷,随着互联网体量的增大,在现在解决更需要花费巨大的人力财力。IPV6协议的创立可能会解决少量表层问题,但是很多结构上的缺陷仍然无从修复。可能这是计算机网络领域的一大难题。期待在未来,能有技术上的爆炸性突破,其力量,能够足以颠覆计算机领域坚守了三十余年的TCP/IP协议。也许量子通信就是这样的潜力吧。

原创文章地址:【The Design Philosophy of the DARPA Internet Protocols 论文阅读报告】转载时请注明出处mhy12345.xyz