IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 「第十一章」加密算法与随机数 -> 正文阅读

[PHP知识库]「第十一章」加密算法与随机数

「第三篇」服务器端应用安全

    批注
    	[……]	表示他人、自己、网络批注
    		参考资料来源于
    			* 书中批注
    			* CSDN
    			* GitHub
    			* Google
    			* 维基百科
    			* YouTube
    			* MDN Web Docs
    		由于编写过程中无法记录所有的URL
    		所以如需原文,请自行查询
    	{……}	重点内容
    	*……*	表示先前提到的内容,不赘述

「第三篇」服务器端应用安全

	「0.6本书结构」
		就常见的服务器端应用安全问题进行了阐述
		这些问题往往能引起非常严重的后果,在网站的安全建设之初需要优先解决这些问题,避免留下任何隐患


	「第十一章」加密算法与随机数

		「11.1概述」
			加密算法与伪随机数算法是开发中经常会用到的东西,但加密算法的专业性非常强,在Web开发中,如果对加密算法和伪随机数算法缺乏一定的了解,则很可能会错误地使用它们,最终导致应用出现安全问题
			密码学有着悠久的历史,它满足了人们对安全的最基本需求————保密性
			密码学可以说是安全领域发展的基础
			在Web应用中,常常可以见到加密算法的身影,最常见的就是网站在将敏感信息保存到Cookie时使用的加密算法
			加密算法的运用是否正确,与网站的安全息息相关
			常见的加密算法通常分为分组加密算法与流密码加密算法,两者的实现原理不同
			分组加密算法基于分组(block)进行操作,根据算法的不同,每个分组的长度可能不同
			分组加密算法的代表
				DES
				3-DES
				Blowfish
				IDEA
				AES
				...
			流密码加密算法,则每次只处理一个字节,密钥独立于消息之外,两者通过异或实现加密与解密
			流密码加密算法的代表
				RC4
				ORYX
				SEAL
				...
			针对加密算法的攻击,一般根据攻击者能获得的信息,可以分为
				* 唯密文攻击
					攻击者有一些密文,它们是使用同一加密算法和同一密钥加密的
					这种攻击是最难的
				* 已知明文攻击
					攻击者除了能得到一些密文外,还能得到这些密文对应的明文
				* 选择明文攻击
					攻击者不仅能得到一些密文和明文,还能选择用于加密的明文
				* 选择密文攻击
					攻击者可以选择不同的密文来解密
					Padding Oracle Attack就是一种选择密文攻击
			密码学在整个安全领域中是非常大的一个课题

		「11.2Stream Cipher Attack」
			流密码是常用的一种加密算法,与分组加密算法不同,流密码的加密是基于异或(XOR)操作进行的,每次都只操作一个字节
			但流密码加密算法的性能非常好,因此也是非常受开发者欢迎的一种加密算法
			常见的流密码加密算法
				RC4
				ORYX
				SEAL
				...

			11.2.1Reused Key Attack
				在流密码的使用中,最常见的错误便是使用同一个密钥进行多次加/解密,这将使得破解流密码变得非常简单
				这种攻击被称为Reused Key Attack,在这种攻击下,攻击者不需要知道密钥,即可还原出明文
				假设有密钥C、明文A、明文B,那么XOR可以表示为
				--------------
				E(A) = A xor C
				E(B) = B xor C
				--------------
				这种密文是公开于众的,因此很容易计算
				-------------
				E(A) xor E(B)
				-------------
				因为两个相同的数进行XOR运算的结果是0
				-----------------------------------------------------------------------
				E(A) xor E(B) = (A xor C) xor (B xor C) = A xor B xor C xor C = A xor B
				-----------------------------------------------------------------------
				这意味着4个数据中,只需要知道3个,就可以推导出剩下的一个
				如果存在初始化向量,则相同明文每次加密的结果均不同,将增加破解的难度,即不受此攻击影响,因此当$ckey_length = 4;时,authcode()将产生随机密钥,算法的强度也就增加了
				如果IV不够随机,攻击者有可能找到相同的IV,则在相同IV的情况下仍然可以实施Reused Key Attack

			11.2.2Bit-flipping Attack
				再次回到这个公式
				-----------------------
				E(A) xor E(B) = A xor B
				-----------------------
				由此可以得出
				-----------------------
				A xor E(A) xor B = E(B)
				-----------------------
				这意味着当知道A的明文、B的明文、A的密文时,可以推导出B的密文,这在实际应用中非常有用
				比如一个网站应用,使用Cookie作为用户身份的认证凭证,而Cookie的值是通过XOR加密而得的
				认证的过程就是服务器端解密Cookie后,检查明文是否合法
				假设明文是username + role
				那么当攻击者注册了一个普通用户A时,获取了A的Cookie为Cookie(A),就有可能构造出管理员的Cookie
				从而获得管理员权限
				-------------------------------------------------------------------------------
				(accountA + member) xor Coolie(A) xor (admin_account + manager) = Coolie(admin)
				-------------------------------------------------------------------------------
				在密码学中,攻击者在不知道明文的情况下,通过改变密文,使得明文按其需要的方式发生改变的攻击方式,被称为Bit-flipping Attack
					http://en.wikipedia.org/wili/Bit-flipping_attack
				解决Bit-flipping攻击的方法是验证密文的完整性,最常见的方法是增加带有KEY的MAC(消息验证码,Message Authentication Code),通过MAC验证密文是否被篡改
				通过哈希算法来实现的MAC,称为HMAC,HMAC由于其性能较好,而被广泛使用
				在authcode()中,其实已经实现了HMAC,所以攻击者在不知晓加密KEY的情况下,是无法完成Bit-flipping攻击的
				其中,密文的前10个字节用于验证时间是否有效,10~26个字节即为HMAC,用于验证密文是否被篡改,26个字节之后才是真正的密文
				这个值与两个因素有关,一个是真正的密文:substr($result,26),一个是$keyb,而$keyb?又是由加密密钥KEY变化得到的,因此在不知晓KEY的情况下,这个HMAC的值是无法伪造出来的,因此HMAC有效地保证了密文不会被篡改

			11.2.3弱随机IV问题
				在authcode()函数中,它默认使用了4字节的IV(就是函数中的keyc),使得破解难度增大
				但其实4字节的IV是很脆弱的,它不够随机,我们完全可以通过暴破的方式找到重复的IV
				为了验证这一点,调整一下破解程序
				在大约16秒后,共遍历了19295个不同的XOR KEY,找到了相同的IV,顺利破解出明文
		
		「11.3WEP破解」
			流密码加密算法存在Reused Key Attack和Bit-flipping Attack等攻击方式
			而在现实中,一种最著名的针对流密码的攻击可能就是WEP密钥的破解
			WEP是一种常用的无线加密传输协议,破解了WEP的密钥,就可以以此密钥连接无线的Access Point
			WEP采用RC4算法,也存在这两种攻击方式
			WEP在加密过程中,有两个关键因素,一个是初始化向量IV,一个是对消息的CRC-32校验,而这两者都可以通过一些方法克服
			IV以明文的形式发送,在WEP中采用24bit的IV,但这其实不是很大的一个值
			假设一个繁忙的AP,以11Mbps的速度发送大小为1500bytes的包,则1500*8/(11*10^6)*2^24 =~18000秒,约为5个小时
			因此最多5个小时,IV就将耗光,不得不开始出现重复的IV
			在实际情况中,并非每个包都有1500bytes大小,因此时间会更短
			IV一旦开始重复,就会使得Reused Key Attack成为可能
			同时通过收集大量的数据包,找到相同的IV,构造出相同的CRC-32校验值,也可以成功实施Bit-flipping Attack
			破解WEP的理论变得可行了
			Berkly的Nikita Borisov,Ian Goldberg以及David Wagner共同完成了一篇很好的论文Security of the WEP algorithm,其中深入阐述了WEP破解的理论基础
				http://www.isaac.cs.berkeley.edu/isaac/wep-faq.html
			实际破解WEP的步骤要稍微复杂一些,Aircrack实现了这一过程
				* 加载目标
				* 与目标网络进行协商
				* 生成密钥流
				* 构造ARP包
				* 生成自己的ARP包
				* 开始暴破
				最终成功破解出WEP的KEY,可以免费蹭网了

		「11.4ECB模式的缺陷」
			前面讲到了流密码加密算法中的几种常见的攻击方法,在分组加密算法中,也有一些可能被攻击者利用的地方
			如果开发者不熟悉这些问题,就有可能错误地使用加密算法,导致安全隐患
			对于分组加密算法来说,除去算法本身,还有一些通用的加密模式,不同的加密算法会支持同样的几种加密模式
			常见的加密模式
				ECB
				CBC
				CFB
				OFB
				CTR
				...
			如果加密模式被攻击,那么不论加密算法的密钥有多长,都可能不再安全
			ECB模式(电码簿模式)是最简单的一种加密模式,它的每个分组之间相对独立
			但ECB模式最大的问题也是出在这种分组的独立性上
			攻击者只需要对调任意分组的密文,在经过解密后,所得明文的顺序也是经过对调的3-DES每个分组为8个字节
			对比plain加密后的密文,可以看到,仅仅block 1的密文不同,而block 2的密文是完全一样的
			也就是说,block 1并未影响到block 2的结果
			这与链式加密模式(CBC)等是完全不同的,链式加密模式的分组前后之间会互相关联,一个字节的变化,会导致整个密文发生变化
			这一特点也可以用于判断密文是否是用ECB模式加密的
			对于ECB模式来说,改变分组密文的顺序,将改变解密后的明文顺序,替换某个分组密文,解密后该对应分组的明文也会被替换,而其他分组不受影响
			ECB模式并未完全混淆分组间的关系,因此当分组足够多时,仍然会暴露一些私密信息,而链式模式则避免了此问题
			当需要加密的明文多于一个分组的长度时,应该避免使用ECB模式,而使用其他更加安全的加密模式

		「11.5Padding Oracle Attack」
			在Eurocrypt 2002 大会上,Vaudenay介绍了针对CBC模式的Padding Oracle Attack
			它可以在不知道密钥的情况下,通过对padding bytes的尝试,还原明文,或者构造出任意明文的密文
			在2010 年的BlackHat欧洲大会上,Juliano Rizzo与Thai Duong介绍了Padding Oracle在实际中的攻击案例
				http://net.ifera.com/research/
			并公布了ASP.NET存在的Padding Oracle问题
				http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-3332
			在2011年的Pwnie Rewards中,ASP.NET的这个漏洞被评为最具价值的服务器端漏洞
				http://pwnies.com/winners
			分组加密算法在实现加/解密时,需要把消息进行分组(block),block的大小常见的有
				64bit
				128bit
				256bit
			在解密完成后,如果最后的padding值不正确,解密程序往往会抛出异常(padding error)
			而利用应用的错误回显,攻击者往往可以判断出padding是否正确
			所以Padding Oracle实际上是一种边信道攻击,攻击者只需要知道密文的解密结果是否正确即可,而这往往有许多途径
			比如在Web应用中,如果是padding不正确,则应用程序很可能会返回500的错误,如果padding正确,但解密出来的内容不正确,则可能会返回200的自定义错误
			正确的padding值只可能为
				1个字节的padding为0x01
				2个字节的padding为0x02,0x02
				3个字节的padding为0x03,0x03,0x03
				4个字节的padding为0x04,0x04,0x04,0x04
			因此慢慢调整IV的值,以希望解密后,最后一个字节的值为正确的padding byte,比如一个0x01
			因为Intermediary Value是固定的(我们此时不知道Intermediary Value的值是多少),因此从0x00到0xFF之间,只可能有一个值与Intermediary Value的最后一个字节进行XOR后,结果是0x01
			通过遍历这255个值,可以找出IV需要的最后一个字节
			通过XOR运算,可以马上推导出此Intermediary Byte的值
			在正确匹配了padding为0x01后,需要做的是继续推导出剩下的Intermediary Byte
			根据padding的标准,当需要padding两个字节时,其值应该为0x02,0x02
			而我们已经知道了最后一个Intermediary Byte为0x3D,因此可以更新IV的第8个字节为0x3D ^ 0x02 = 0x3F,此时可以开始遍历IV的第7个字节(0x00~0xFF)
			获得Intermediary Value后,通过与原来的IV进行XOR运算,即可得到明文
			在这个过程中仅仅用到了密文和IV,通过对padding的推导,即可还原出明文,而不需要知道密钥是什么
			而IV并不需要保密,它往往是以明文形式发送的
			如何通过Padding Oracle使得密文能够解密为任意明文呢
			实际上通过前面的解密过程可以看出,通过改变IV,可以控制整个解密过程
			因此在已经获得了Intermediary Value的情况下,很快就可以通过XOR运算得到可以生成任意明文的IV
			而对于多个分组的密文来说,从最后一组密文开始往前推
			以两个分组为例,第二个分组使用的IV是第一个分组的密文(cipher text),因此当推导出第二个分组使用的IV时,将此IV值当做第一个分组的密文,再次进行推导
			Brian Holyfield实现了一个叫padbuster的工具,可以自动实施Padding Oracle攻击
				http://bole.gdssecurity.com/labs/2010/9/14/automated-padding-oracle-attacks-with-padbuster.html
				http://github.com/GDSSecurity/PadBuster
				http://hi.baidu.com/aullik5/blog/item/7e769d2ec68b2d241f3089ce.html
			Padding Oracle Attack的关键在于攻击者能够获知解密的结果是否符合padding
			在头现和使用CBC模式的分组加密算法时,注意这一点即可

		「11.6密钥管理」
			密码学基本原则
				密码系统的安全性应该依赖于密钥的复杂性,而不应该依赖于算法的保密性
			在安全领域里,选择一个足够安全的加密算法不是困难的事情,难的是密钥管理
			在一些实际的攻击案例中,直接攻击加密算法本身的案例很少,而因为密钥没有妥善管理导致的安全事件却很多
			对于攻击者来说,他们不需要正面破解加密算法,如果能够通过一些方法获得密钥,则是件事半功倍的事情
			密钥管理中最常见的错误,就是将密钥硬编码在代码里
			同样的,将加密密钥、签名的salt等key硬编码在代码中,是非常不好的习惯
			硬编码的密钥,在以下几种情况下可能被泄露
				* 代码被广泛传播
					这种泄露途径常见于一些开源软件
					有的商业软件并不开源,但编译后的二进制文件被用户下载,也可能被逆向工程反编译后,泄露硬编码的密钥
				* 软件开发团队的成员都能查看代码,从而获知硬编码的密钥
					开发团队的成员如果流动性较大,则可能会由此泄露代码
			对于第一种情况,如果一定要将密钥硬编码在代码中,我们尚可通过Diffie-Hellman交换密钥体系,生成公私钥来完成密钥的分发
			对于第二种情况,则只能通过改善密钥管理来保护密钥
			对于Web应用来说,常见的做法是将密钥(包括密码)保存在配置文件或者数据库中,在使用时由程序读出密钥并加载进内存
			密钥所在的配置文件或数据库需要严格的控制访问权限,同时也要确保运维或DBA中具有访问权限的人越少越好
			在应用发布到生产环境时,需要重新生成新的密钥或密码,以免与测试环境中使用的密钥相同
			当黑客已经入侵之后,密钥管理系统也难以保证密钥的安全性
			比如攻击者获取了一个webshell,那么攻击者也就具备了应用程序的一切权限
			由于正常的应用程序也需要使用密钥,因此对密钥的控制不可能限制住webshell的正常请求
			密钥管理的主要目的,还是为了防止密钥从非正常的渠道泄露
			定期更换密钥也是一种有效的做法
			一个比较安全的密钥管理系统,可以将所有的密钥(包括一些敏感配置文件)都集中保存在一个服务器(集群)上,并通过Web Service的方式提供获取密钥的API
			每个Web应用在需要使用密钥时,通过带认证信息的API请求密钥管理系统,动态获取密钥
			Web应用不能把密钥写入本地文件中,只加载到内存,这样动态获取密钥最大程度地保护了密钥的私密性
			密钥集中管理,降低了系统对于密钥的耦合性,也有利于定期更换密钥

		「11.7伪随机数问题」
			伪随机数(pseudo random number)问题——伪随机数不够随机,是程序开发中会出现的一个问题
			一方面,大多数开发者对此方面的安全知识有所欠缺,很容易写出不安全的代码,另一方面,伪随机数问题的攻击方式在多数情况下都只存在于理论中,难以证明,因此在说服程序员修补代码时也显得有点理由不够充分
			但伪随机数问题是真实存在的、不可忽视的一个安全问题
			伪随机数,是通过一些数学算法生成的随机数,并非真正的随机数
			密码学上的安全伪随机数应该是不可压缩的
			真随机数,则是通过一些物理系统生成的随机数,比如电压的波动、硬盘磁头读/写时的寻道时间、空中电磁波的噪声

			11.7.1弱伪随机数的麻烦
				Luciano Bello发现了Debian上的OpenSSL包中存在弱伪随机数算法
				产生这个问题的原因,是由于编译时会产生警告(warning)信息,因此下面的代码被移除了
				这直接导致的后果是,在OpenSSL的伪随机数生成算法中,唯一的随机因子是pid
				而在Linux系统中,pid的最大值也是32768
				这是一个很小的范围,因此可以很快地遍历出所有的随机数
				受到影响的有,从2006.9到2008.5.13的debian平台上生成的所有ssh key的个数是有限的,都是可以遍历出来的,这是一个非常严重的漏洞
				同时受到影响的还有OpenSSL生成的key以及OpenVPN生成的key
				Debian随后公布了这些可以被遍历的key的名单
				这次事件的影响很大,也让更多的开发者开始关注伪随机数的安全问题
				在Web应用中,使用伪随机数的地方非常广泛
				密码、key、SessionID、token等许多非常关键的secret往往都是通过伪随机数算法生成的
				如果使用了弱伪随机数算法,则可能会导致非常严重的安全问题

			11.7.2时间真的随机吗
				很多伪随机数算法与系统时间有关,而有的程序员甚至就直接使用系统时间代替随机数的生成,这样生成的随机数,是根据时间顺序增长的,可以从时间上进行预测,从而存在安全隐患
				比如下面这段代码,其逻辑是用户取回密码时,会由系统随机生成一个新的密码,并发送到用户邮箱
				----------------------------------------------------------------------------------------
				function sendPSW()
				{
					......
					$messenger = &$this->system->loadModel('system/messenger');echo microtime()."<br/>";
					$passwd = substr(md5(print_r(microtime(),true)),0,6);
					......
				}
				----------------------------------------------------------------------------------------
				这个新生成的$passwd,是直接调用了microtime()后,取其MD5值的前6位
				由于MD5算法是单向的哈希函数,因此只需要遍历microtime()的值,再按照同样的算法,即可猜解出$passwd的值
				PHP中的microtime()由两个值合并而成,一个是微秒数,一个是系统当前秒数
				因此只需要获取到服务器的系统时间,就可以以此时间为基数,按次序递增,即可猜解出新生成的密码
				因此这个算法是存在非常严重的设计缺陷的,程序员预想的随机生成密码,其实并未随机
				{在开发程序时,要切记不要把时间函数当成随机数使用}

			11.7.3破解伪伪随机数算法的种子
				在PHP中,常用的随机数生成算法有mnd()、mt_rand()
				可见,rand()的范围其实是非常小的,如果使用rand()生成的随机数用于一些重要的地方,则会非常危险
				其实PHP中的mt_rand()也不是很安全,Stefan Esser在他著名的papermt_srand and not so random numbers中提出了PHP的伪随机函数mt_rand()在实现上的一些缺陷
					http://www.suspekt.org/2008/8/17/mt_srand-and-not-so-ranfom-numbers/
				伪随机数是由数学算法实现的,它真正随机的地方在于种子(seed)
				种子一旦确定后,再通过同一伪随机数算法计算出来的随机数,其值是固定的,多次计算所得值的顺序也是固定的
				在PHP4.2.0之前的版本中,是需要通过srand()或mt_srand()给rand()、mt_rand()播种的
				在PHP 4.2.0之后的版本中不再需要事先通过srand()、mt_srand()播种
				比如直接调用mt_rand(),系统会自动播种
				在PHP 4.2.0之后的版本中,如果没有通过播种函数指定seed,而直接调用mt_rand(),则系统会分配一个默认的种子
				在32位系统上默认的播种的种子最大值是2^32,因此最多只需要尝试2^32次就可以破解seed
				可以看出,当seed确定时,第一次到第n次通过mt_rand()产生的值都没有发生变化
				建立在这个基础上,就可以得到一种可行的攻击方式
					* 通过一些方法猜解出种子的值
					* 通过mt_srand()对猜解出的种子值进行播种
					* 通过还原程序逻辑,计算出对应的mt_rand()产生的伪随机数的值
				需要注意的是,在PHP 5.2.1及其之后的版本中调整了随机数的生成算法,但强度未变,因此在实施猜解种子时,需要在对应的PHP版本中运行猜解程序
				在Stefan Esser的文中还提到了一个小技巧,可以通过发送Keep-Alive HTTP头,迫使服务器端使用同一PHP进程响应请求,而在该PHP进程中,随机数在使用时只会在一开始播种一次
				在一个Web应用中,有很多地方都可以获取到随机数,从而提供猜解种子的可能
				Stefan Esser提供了一种Cross Application Attacks的思路,即通过前一个应用在页面上返回的随机数值,猜解出其他应用生成的随机数值
				如果服务器端将$search_id返回到页面上,则攻击者就可能猜解出当前的种子
				这种攻击确实可行,比如一个服务器上同时安装了WordPress与phpBB,可以通过phpBB猜解出种子,然后利用WordPress的密码取回功能猜解出新生成的密码
				Stefan Esser描述这个攻击过程如下
					* 使用Keep-Alive HTTP请求在phpBB2论坛中搜索字符串'a'
					* 搜索必然会出来很多结果,同时也泄露了search_id
					* 很容易通过该值猜解出随机数的种子
					* 攻击者仍然使用Keep-Alive HTTP头发送一个重置admin密码的请求给WordPress blog
					* WordPress mt_rand()生成确认链接,并发送到管理员邮箱
					* 攻击者根据已算出的种子,可以构造出此确认链接
					* 攻击者确认此链接(仍然使用Keep-Alive头),WordPress将向管理员邮箱发送新生成的密码
					* 因为新密码也是由mt_rand()生成的,攻击者仍然可以计算出来
					* 从而攻击者最终获取了新的管理员密码

			11.7.4使用安全的随机数
				我们需要谨记,在重要或敏感的系统中,一定要使用足够强壮的随机数生成算法
				在Java中,可以使用java.security.SecureRandom
				而在Linux中,可以使用/dev/random或者/dev/urandom来生成随机数,只需要读取即可
				而在PHP 5.3.0及其之后的版本中,若是支持openSSL扩展,也可以直接使用函数来生成随机数
				除了以上方法外,从算法上还可以通过多个随机数的组合,以增加随机数的复杂性
				比如通过给随机数使用MD5算法后,再连接一个随机字符,然后再使用MD5算法一次
				这些方法,也将极大地增加攻击的难度

		「11.8总结」
			在加密算法的选择和使用上,有以下最佳实践
				* 不要使用ECB模式
				* 不要使用流密码(比如RC4)
				* 使用HMAC-SHA1代替MD5(甚至是代替SHA1)
				* 不要使用相同的key做不同的事情
				* salts与IV需要随机产生
				* 不要自己实现加密算法,尽量使用安全专家已经实现好的库
				* 不要依赖系统的保密性
			当你不知道该如何选择时,有以下建议
				* 使用CBC模式的AES256用于加密
				* 使用HMAC-SHA512用于完整性检查
				* 使用带salt的SHA-256 或SHA-512 用于Hashing

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-08-11 11:55:25  更:2021-08-11 11:56:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/13 4:09:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码