编程基础 | PHP代码审记(上 )
字数 2759 2025-08-18 11:39:26
PHP代码审计与安全编程教学文档
1. PHP基础知识
1.1 全局变量
1.1.1 $GLOBALS
- PHP内置变量,自动获取当前页面中所有变量的内容
- 包含所有全局变量的关联数组
1.1.2 $_SERVER
- 包含头信息、路径、脚本位置等信息的数组
- 由Web服务器创建,不同服务器提供的信息可能不同
示例代码:
echo '<table>';
foreach($_SERVER as $key => $value) {
echo '<tr>';
echo '<td>'.$key.'</td><td>'.$value.'</td>';
echo '</tr>';
}
echo '</table>';
1.1.3 $_FILES
- 获取上传文件的信息
示例代码:
if ($_FILES["file"]["error"] > 0) {
echo "错误: ".$_FILES["file"]["error"];
} else {
echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . "kB<br>";
echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"];
if (file_exists("upload/" . $_FILES["file"]["name"])) {
echo $_FILES["file"]["name"] . " 文件已经存在。";
} else {
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
echo "文件存储在: " . "upload/" . $_FILES["file"]["name"];
}
}
1.1.4 $_GET
- 通过URL方式传递数据
- 传递方式:URL地址?参数1=值1&参数2=值2
示例代码:
<form action="#" method="get">
USERNAME:<input type="text" name="username"><br>
PASSWORD:<input type="password" name="password"><br>
<input type="submit" name="submit">
</form>
1.1.5 $_POST
- 通过HTTP协议的POST方式传递数据
- 地址栏不可见传输内容
示例代码:
<form action="#" method="post">
USERNAME:<input type="text" name="username"><br>
PASSWORD:<input type="password" name="password"><br>
<input type="submit" name="submit">
</form>
1.1.6 $_REQUEST
- 默认可以接受GET、POST和COOKIE三种方式提交的数据
- 如果设置只接受GET方式,POST提交会找不到值
1.2 正则表达式
1.2.1 正则表达式简介
- 用于描述字符串匹配规则的代码
- 比通配符更精确,但更复杂
1.2.2 正则表达式相关函数
preg_match- 筛选到一个结果即停止preg_match_all- 筛选所有结果保存到数组preg_replace- 筛选并替换内容preg_split- 根据规则拆分字符串
1.2.3 正则表达式应用场景
- 用户注册验证
- 内容采集
- 数据过滤
1.2.4 表达式语法
-
定界符:表示规则的边界,通常使用
/ -
元字符:
\w:字母、数字、下划线\W:非字母、数字、下划线\d:数字(0-9)\D:非数字\s:空格\S:非空格[a-z]:a-z任意字符[^hel]:非h、e、l的任意字符.:除换行符外的任意字符|:或
-
量词:
{m}:固定m个{n,m}:最少n个,最多m个{n,}:最少n个*:0个或多个+:至少1个?:0个或1个
-
模式修正符:
i:忽略大小写s:万能点模式U:贪婪模式改为懒惰模式
1.2.5 正则实例
- 用户名验证(6-30位,字母开头,字母数字下划线组合)
$reg = '/^[a-zA-Z]\w{5,29}$/';
$res = 'ehllo1123';
preg_match($reg, $res, $match);
if($match) {
echo "success";
} else {
echo "fail";
}
- 密码验证(6-20位,字母、数字或符号)
- 纯字母/数字:提示密码太简单
- 字母+数字:提示比较安全
- 字母+数字+特殊符号:提示非常安全
- 正则漏洞示例
$ip = '1.1.1.1 abcd'; // 可以绕过
if(!preg_match("/(\d+)\.(\d+)\.(\d+)\.(\d+)/", $ip)) {
die('error');
} else {
echo('key...');
}
问题:没有限制开始和结束,导致输入危险字符绕过
1.3 Socket函数
1.3.1 Socket介绍
- 应用层与TCP/IP协议族通信的中间抽象层
- 两种方案:
- 基于内核的socket:
fsockopen,pfsockopen - PHP扩展模块:
socket_create,socket_bind,socket_connect,socket_accept
- 基于内核的socket:
1.3.2 关键函数
socket_create($net, $stream, $protocol)- 创建socket套接字socket_connect($socket, $ip, $port)- 连接套接字socket_bind($socket, $ip, $port)- 绑定套接字socket_listen($socket, $backlog)- 监听套接字socket_accept($socket)- 接收套接字资源信息socket_read($socket, $length)- 读取套接字资源socket_write($socket, $msg, $strlen)- 写入数据到套接字socket_close($socket)- 关闭套接字
1.3.3 服务端示例代码
// Server_socket.php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '127.0.0.1', 8888);
socket_listen($socket);
$accept = socket_accept($socket);
$string = socket_read($accept, 1024);
echo $string;
socket_write($accept, 'Server has received your information');
socket_close($accept);
socket_close($socket);
1.3.4 客户端示例代码
// Client_server.php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>1, "usec"=>0));
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>6, "usec"=>0));
if(socket_connect($socket, '127.0.0.1', 8888) == false) {
echo 'connect fail massege:'.socket_strerror(socket_last_error());
} else {
$message = 'l love you 我爱你 socket';
$message = mb_convert_encoding($message, 'GBK', 'UTF-8');
if(socket_write($socket, $message, strlen($message)) == false) {
echo 'fail to write'.socket_strerror(socket_last_error());
} else {
echo 'client write success'.PHP_EOL;
while($callback = socket_read($socket, 1024)) {
echo 'server return message is:'.PHP_EOL.$callback;
}
}
}
socket_close($socket);
1.4 fsockopen函数
1.4.1 介绍
- 打开一个网络连接或Unix套接字连接
- 语法:
resource fsockopen (string $hostname [, int $port = -1 [, int &$errno [, string &$errstr [, float $timeout = ini_get("default_socket_timeout")]]]])
1.4.2 参数
hostname- 主机名port- 端口号(-1表示不使用端口)errno- 错误号errstr- 错误信息timeout- 连接时限(秒)
1.4.3 示例代码
$fp = fsockopen("127.0.0.1", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
fputs($fp, "GET / HTTP/1.0\r\nHost: 127.0.0.1\r\n\r\n");
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
1.4.4 端口扫描器实现
$ip = $_POST['ip'];
if (ip2long($ip)) {
$_ip = explode(".", $ip);
foreach ($_ip as $key => $value) {
if ($value < 0 || $value > 255) {
die("Invalid IP");
}
}
}
$port = array(21, 23, 25, 79, 80, 110, 135, 137, 138, 139, 143, 443, 445, 1433, 3306);
$msg = array('Ftp', 'Telnet', 'Smtp', 'Finger', 'Http', 'Pop3', 'Location Service',
'Netbios-NS', 'Netbios-DGM', 'Netbios-SSN', 'IMAP', 'Https', 'Microsoft-DS',
'MSSQL', 'MYSQL', 'Terminal Services');
foreach ($port as $key => $value) {
echo '<tr>';
echo '<td>'.$key.'</td>';
echo '<td>'.$value.'</td>';
echo '<td>'.$msg[$key].'</td>';
$fp = @fsockopen($ip, $value, $errno, $errstr, 1);
$res = $fp ? '<span>Open</span>' : '<span>Close</span>';
echo '<td>'.$res.'</td>';
}
1.4.5 常见安全端口
- 21: FTP
- 22: SSH
- 23: Telnet
- 25: SMTP
- 80: HTTP
- 110: POP3
- 135-139: NetBIOS
- 143: IMAP
- 443: HTTPS
- 445: Microsoft-DS
- 1433: MSSQL
- 3306: MySQL
- 3389: 远程桌面
- 6379: Redis
- 8080: Tomcat
- 27017: MongoDB
2. 安全编程实践
2.1 输入验证
- 对所有用户输入进行严格验证
- 使用白名单而非黑名单策略
- 对特殊字符进行转义或过滤
2.2 文件上传安全
- 检查文件类型(不要依赖客户端提供的信息)
- 限制文件大小
- 重命名上传文件
- 存储在非web可访问目录
- 设置适当权限
2.3 SQL注入防护
- 使用预处理语句(PDO或MySQLi)
- 避免直接拼接SQL语句
- 对特殊字符进行转义
2.4 XSS防护
- 对所有输出进行HTML实体编码
- 使用Content Security Policy (CSP)
- 设置HttpOnly标志的cookie
2.5 CSRF防护
- 使用CSRF令牌
- 检查Referer头
- 敏感操作使用POST而非GET
2.6 会话安全
- 使用安全的会话配置
- 会话ID足够随机
- 设置适当的会话超时
- 用户登出时销毁会话
2.7 错误处理
- 生产环境关闭错误显示
- 记录错误到日志文件
- 不向用户暴露敏感信息
3. 代码审计要点
3.1 常见漏洞点
- 未过滤的用户输入
- 直接拼接的SQL查询
- 不安全的文件操作
- 不正确的权限设置
- 硬编码的敏感信息
- 不安全的反序列化
- 不正确的加密实现
- 不安全的第三方组件
3.2 审计工具
- 静态代码分析工具
- 动态分析工具
- 交互式应用安全测试(IAST)
- 手动代码审查
3.3 审计流程
- 了解应用架构
- 识别入口点
- 跟踪数据流
- 识别信任边界
- 验证安全控制
- 报告发现的问题
4. 总结
本教学文档涵盖了PHP安全编程的基础知识,包括全局变量、正则表达式、Socket编程和fsockopen函数的使用。同时提供了安全编程的最佳实践和代码审计的关键要点。通过掌握这些知识,开发者可以编写更安全的PHP代码,审计人员可以更有效地发现潜在的安全漏洞。