云计算百科
云计算领域专业知识百科平台

初识PHP

一、PHP标准语言格式

PHP语言标记:

<?php // PHP 的开始标记,表示从此标记开始,进入PHP 模式。
phpinfo(); // PHP 代码
// PHP 的结束标记,表示从开始标记到结束标记,之间的内容是PHP 模式
// 之后的内容依然被PHP 解释引擎认为是纯文本字符串。
?>

PHP用通俗的话来讲它是比较随意的一个语言,在PHP文件中,里面可以掺杂着HTML和JavaScript进行混编。

PHP的开始标记为:<?php 结束标记为:?> ,分号;代表一句话代码结束。//代表注释,//后面的内容不会被执行。而在这标记之外的内容,它们会被当作字符输出到浏览器中。例如:

可以看到上面的sad sad sad内容我们并没有做任何处理,它们直接输出到浏览器中。

对于网络安全来讲,如果在一个在线网站上找到了phpinfo页面,说明存在敏感信息泄露漏洞。

二、字+字符串混搭运算

后面的内容我们用一些小练习来进行解释:

<?php

$a = 10;

$b = "20test";

echo $a + $b;

?>

在PHP中,+ 运算符是算术加法运算符,具有明确的数值上下文要求。当操作数类型不一致时,PHP会触发隐式类型转换,其底层遵循字符串向数值转换的既定规则。加法运算迫使字符串只认开头的数字部分,其余后缀被丢弃,然后两个纯数字完成求和。这是动态类型语言中常见的“弱类型”特性。

三、可变变量

<?php

$user = "hack123";

$admin = "密码123456";

echo $$user;

?>

先计算内层变量 $user,得到字符串值 "hack123"。将 "hack123" 作为新的变量名,去查找名为 $hack123 的变量。因为从未给 $hack123 赋值,查找失败,所以会报错。

可以用数学方面这样理解:∵$user=hack123 ∴$$user=$($user)=$hack123。但因为没有定义$hack123的值,所以它认不出谁是$hack123,然后导致报错。

<?php

$name = "flag";

$flag = "this_is_secret_flag";

$$name = "hacker_get_flag";

echo $flag;

?>

这是一个写入操作,直接修改了名为 flag 的变量。

因为有$name=flag 所以$$name=$flag,但最后写入的是$$name=”hacker_get_flag”;

所以$flag=hacker_get_flag

可变变量为什么会产生变量覆盖漏洞?

如果程序从用户输入(如 $_GET, $_POST, $_COOKIE)中获取数据,并直接或间接地用于创建可变变量,攻击者就可以通过控制输入来覆盖程序中的任意变量。简单的来说当用户可控的数据被不当地用作可变变量的名称时,攻击者可以覆盖任何已存在的变量,从而破坏程序的逻辑或数据。

四、布尔转换黑名单值

<?php

$arr = [false, 0, 0.0, "", "0", [], NULL, "abc", 99, 3.14];

foreach($arr as $v){

    if($v){

        echo var_dump($v)." → 真<br>";

    }else{

        echo var_dump($v)." → 假<br>";

    }

}

?>

10种不同数据类型的数组,然后用 foreach 遍历每个元素,在 if 判断中验证它们自动转换后的布尔值是真还是假,并通过 var_dump() 输出每个值的原始类型和内容,以此直观展示 PHP 的假值列表和类型自动转换规则。

在布尔上下文中,常见的以下 7 种情况会被转换为false:

类型

示例

布尔

false 本身

if(false) → 假

整数

0

if(0) → 假

浮点数

0.0 或 -0.0

if(0.0) → 假

字符串

""(空字符串)

if("") → 假

字符串

"0"(字符0)

if("0") → 假

数组

[](空数组)

if([]) → 假

NULL

NULL

if(NULL) → 假

除此以外的所有值,都是 true(真值)。

五、单引号/双引号/定界符

<?php

$name = "xiu";

$str1 = 'hello $name ,\\'test\\'';

echo $str1;

单引号字符串中的变量不会被解析,特殊字符只有反斜杠 \\ 用于转义单引号 \\' 和反斜杠本身 \\\\。

<?php

$name = "xiu";

$str2 = "hello {$name},\\"test\\"";

echo $str2;

?>

双引号字符串中的变量会被解析。{$name} 是一种明确告诉PHP解析器这是一个变量的写法,尤其在变量名紧挨着其他字符时很有用。

<?php

$title = "php渗透练习";

$html = <<<HTML

<h1>yemianbiaoti: {$title}</h1>

<script>

console.log("定界符无需转义单双引号!");

</script>

HTML;

echo $html;

?>

输出一段HTML,其中 <h1> 标签内显示了 "PHP渗透练习"。<<<HTML 表示开始一个名为 "HTML" 的多行字符串,HTML; 表示结束。这种语法让你能方便地输出大段 HTML/JavaScript 代码,且无需转义引号,{$title} 会被替换为变量的值。

1. 输出变量时`{$name}`大括号作用是什么?不加会出现什么问题?

作用:明确界定变量名的边界。

问题:如果不加大括号,PHP会尽可能多地吞并字符作为变量名。例如 "hello $name123",PHP会认为变量是 $name123 而不是 $name,导致找不到变量而报错。大括号可以避免这种歧义。

2. 大段混合HTML、JS代码时,优先用哪种字符串格式?

首选$变量 = <<<标记名

文本内容(可以包含变量 {$变量名})

标记名;

$变量 = <<<'标记名' 注意:标记名用单引号包裹

文本内容(变量不会被解析)

标记名;

<<<HTML的行为类似于双引号,可以解析变量,并且内部的双引号、单引号都无需转义,非常适合输出大段的、包含大量引号和换行符的HTML/JS代码,可读性最好。

单引号:性能最好,不解析变量,只转义 \\' 和 \\\\。

双引号:性能次之,解析变量,转义多种特殊字符(如 \\n, \\t, \\" 等)。

定界符:适合多行文本,行为类似双引号或单引号。

大括号语法: 明确变量边界,提高代码可读性和健壮性。

六、魔术常量

<?php

echo "当前文件完整路径:".__FILE__."<br>";

echo "当前代码行号:".__LINE__."<br>";

echo "PHP版本:".PHP_VERSION."<br>";

echo "操作系统:".PHP_OS."<br>";

echo "目录分隔符:".DIRECTORY_SEPARATOR;

?>

风险:暴露网站根目录,攻击者可以得知网站文件在服务器上的绝对路径(如 /var/www/html/project/app/controller/index.php)。

网站对外暴露 __FILE__ 路径会造成巨大的安全风险。这属于信息泄露漏洞。

辅助其他攻击:这个路径信息可以与其他漏洞(如SQL注入、文件包含、本地文件包含 LFI)结合使用,帮助攻击者更精确地定位和读取敏感文件(如配置文件config.php,包含数据库密码等)。

社会工程学:暴露了开发框架、目录结构,为后续更深入的攻击提供了信息基础。

魔术常量:__FILE__, __LINE__, __DIR__ 等,它们会随着代码位置的变化而变化,提供上下文信息。

预定义常量:PHP_VERSION, PHP_OS, DIRECTORY_SEPARATOR 等,提供了PHP环境和操作系统的信息。

安全风险:永远不要在线上环境直接输出这些敏感信息。phpinfo() 函数更是信息泄露的"重灾区",生产环境务必禁用。

七、== 与 ===

<?php

var_dump("123abc" == 123);

var_dump("123abc" === 123);

var_dump("0" == false);

var_dump("0" === false);

var_dump([] == false);

var_dump(NULL == "");

var_dump(NULL === "");

?>

==:松散比较。比较前会进行类型转换。

"123abc" 转数字是 123,所以 123 == 123 为 true。

"0" 转布尔是 false,所以 false == false 为 true。

空数组 [] 转布尔是 false,所以 false == false 为 true。

NULL 转字符串是空字符串 "",所以 "" == "" 为 true。

===:严格比较。要求值和类型都完全相同。

"123abc" 是字符串,123 是整数,类型不同,为 false。

"0" 是字符串,false 是布尔,类型不同,为 false。

NULL 和空字符串类型不同,为 false。

例如:if ($_POST['password'] == $db_password)。

如果数据库中的密码是纯数字,比如 0。

攻击者可以提交password=0,两者都为数字0,通过验证。

更经典的例子是,如果使用 == 比较,攻击者提交一个空数组 [],它可能被转换为 NULL 或 0,从而绕过。

== 与 ===: 在涉及用户输入比较(尤其是密码、权限判断)时,必须使用 ===,确保值和类型都匹配,防止因类型转换导致的安全绕过。

松散比较规则: 要熟悉常见类型在 == 下的转换结果,特别是字符串与数字、布尔值、NULL之间的比较。

八、反引号与@

<?php

$cmd = "ipconfig";

echo "<pre>".`$cmd`."</pre>";

?>

PHP 中的反引号(`)是一个执行运算符,用于执行系统命令并获取其输出,其功能等同于 shell_exec()。

<?php

echo $undefined_var;

?>

<?php

echo @$undefined_var;

?>

1. 直接将用户输入放入反引号``中会产生什么漏洞?

会产生命令注入漏洞。如果用户输入 ; rm -rf / 或 | whoami,PHP会执行这些恶意命令。

2. 开发者使用@屏蔽报错会给渗透测试带来什么阻碍?

隐藏调试信息:渗透测试人员通常依赖报错信息来了解数据库结构、文件路径、变量名称等。@ 屏蔽了这些信息,增加了信息收集的难度。

隐蔽漏洞:屏蔽错误可能会掩盖一些明显的错误,如SQL查询失败、文件包含失败等,使攻击者难以定位漏洞点。

阻碍判断:攻击者无法通过修改参数触发报错来判断代码执行流程,需要花费更多时间和精力进行"黑盒"测试。

反引号:是 shell_exec() 函数的别名,用于执行系统命令,是命令执行漏洞的直接载体。

错误抑制符 @:可以屏蔽该行代码可能产生的所有错误、警告、通知。在生产环境中过度使用会严重阻碍调试和漏洞排查。

安全编码:不要将用户输入直接传递给反引号或任何系统执行函数(如 system(), exec() 等)。必须使用 escapeshellarg() 或 escapeshellcmd() 进行严格过滤或采用白名单方式。

九、switch和break

<?php

$week = 7;

switch($week){

    case 1: echo "周一"; break;

    case 2: echo "周二"; break;

    case 7: echo "周日"; break;

    default: echo "非法星期";

}

?>

$week是1-7的随机数,程序会正常输出对应的星期几,如果不在范围内则输出"非法星期"。

<?php

$week = 7;

switch($week){

    case 1: echo "周一"; break;

    case 2: echo "周二"; break;

    case 7: echo "周日"; break;

    default: echo "非法星期";

}

?>

程序会执行 case 1 的代码块(输出"周一"),然后继续穿透执行case 2直到遇到 break或switch结束。

1.switch缺少break会产生什么逻辑漏洞?

逻辑错误:程序可能执行多个case分支,导致输出或操作不符合预期。

权限绕过:在权限判断的switch 中,如果忘记写break,可能导致普通用户"穿透"到管理员的case。

2.break的作用:终止switch语句的执行,防止"穿透"到下一个case。

3.穿透(Fall-through):在某些特殊场景下,可以利用穿透特性来简化代码(比如多个case执行相同的操作),但在大多数情况下,忘记加break是逻辑错误的来源。

十、循环

While循环:

<?php

$i = 1;

while ($i <= 100) {

    if ($i == 80) {

        break; // 到80终止循环

    }

    if ($i % 17 == 0) {

        $i++; // 注意:跳过时也要递增,防止死循环

        continue; // 跳过17的倍数

    }

    echo $i . " ";

    $i++;

}

?>

for循环:

<?php

for ($i = 1; $i <= 100; $i++) {

    if ($i == 80) {

        break;

    }

    if ($i % 17 == 0) {

        continue;

    }

    echo $i . " ";

}

?>

continue: 跳过当前循环的剩余代码,进入下一次循环。注意在while循环中,如果continue 前忘记递增 $i,会导致死循环。

break:完全终止并跳出当前的循环结构。

取模运算符%:用于判断是否能被17整除。

循环控制:break和continue可以精细控制循环的执行流程。

while与for:while 更灵活,但需要手动维护循环变量;for结构紧凑,适合知道循环次数的场景。

十一、索引数组、关联数组创建、增删改查

<?php

$class = ["a","b","c"];

$student = [

    "name" => "cxy",

"age" => 26,

"score" => 90.9,

"sex" => true

];

$student["score"] = 98.0;

$student["skill"] = "Web渗透";

echo "只取值:";

foreach ($student as $value) {

echo $value . " ";

}

echo "<br>";

echo "同时取键和值:";

foreach ($student as $key => $value){

echo "[$key] => $value";

}

echo "<br>";

?>

索引数组:键是自动递增的数字(从0开始)。

关联数组:键是自定义的字符串。

增/改:直接使用 $array[key] = value 即可,如果键不存在则新增,存在则修改。

删:使用 unset($array[key])。

foreach:是最常用的遍历数组方式。

foreach ($array as $value) 只获取值。

foreach ($array as $key => $value) 同时获取键和值。

数组类型:PHP 的数组实际上是一个有序映射,可以同时充当索引数组和关联数组。

增删改查:操作非常简单直接,是PHP灵活性的体现。

遍历:foreach 是处理数组的首选方式,它内部维护了指针,不需要手动控制。

十二、总结

PHP 标记与信息泄露:PHP 标记外的内容会直接输出到浏览器,phpinfo() 或输出魔术常量(__FILE__、__DIR__ 等)会暴露路径和配置信息,属于敏感信息泄露漏洞,生产环境务必关闭错误显示并禁用 phpinfo。

弱类型与类型转换:字+字符串运算、松散比较(==)等场景下的隐式类型转换,可能导致逻辑判断错误或绕过身份验证。始终使用严格比较(===),并避免在安全敏感处依赖隐式转换。

可变变量与变量覆盖:可变变量($$)若结合用户输入,可直接造成变量覆盖漏洞,攻击者可篡改程序状态。严禁将用户可控数据用于动态变量名。

布尔真假值:理解 PHP 的假值列表(false, 0, 0.0, "", "0", [], NULL),避免在条件判断中因类型转换产生意外结果。尤其注意 "0" 为假值,但 "0.0" 为真值。

字符串定界与注入:定界符(<<<)适合输出大段 HTML,但需注意变量注入的风险。输出到页面时应对变量进行 htmlspecialchars() 转义,防止 XSS。

魔术常量与预定义常量:__FILE__、PHP_VERSION 等可泄露服务器路径、PHP 版本等信息,生产环境禁止直接输出。可通过配置 php.ini 禁用相关输出。

反引号命令注入:反引号等同于 shell_exec(),直接执行系统命令,若拼接用户输入将导致命令注入,是最危险的安全漏洞之一。杜绝使用反引号执行外部输入,需要执行系统命令时应使用 escapeshellcmd() 或白名单。

错误抑制符 @:@ 会屏蔽错误信息,虽然可以隐藏调试信息,但也会掩盖真实漏洞,建议仅用于预期可控的错误场景,生产环境应配合日志记录。

switch 穿透与逻辑错误:忘记 break 会导致 case 穿透,可能造成权限绕过或逻辑漏洞,每个 case 末尾务必添加 break,除非有意利用穿透特性。

循环控制与死循环:while 循环中 continue 前必须递增计数器,否则导致死循环;break 要及时退出循环,防止无限执行。

数组与数据操作:PHP 数组灵活强大,是 Web 开发常用数据结构,但要注意在对数组进行增删改查时,避免因未验证数据来源导致的逻辑漏洞(如未过滤的键名可被利用)。

赞(0)
未经允许不得转载:网硕互联帮助中心 » 初识PHP
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!