PHP开发(用于网络安全学习)
声明
该文章的代码都是学习一个思路,代码不能直接使用,需要根据自己实际情况进行修改;文章供安全研究人员学习交流。
PHP开发
PHP操作数据库
<?php
$res=mysqli_connect("127.0.0.1", 'root', 'root');
if($res){
echo "数据库连接成功";
}
//创建数据库
$sql = "create database demoPHP";
if(mysqli_query($res,$sql)){
echo "数据库创建成功";
}
//删除数据库
$sql = "drop database demoPHP";
mysqli_query($res, $sql);
mysqli_select_db($res, 数据库名);
//新增数据表
$sql = "create tables 表名 (字段1,字段1)";
mysqli_query($res,$sql);
//删除数据表
$sql = "drop table 表名";
mysqli_query($res, $sql);
//插入数据
$sql = "insert into 表名 对应字段和值";
//查询数据
$sql = "select 字段 from 表名";//后面where可以加查询的条件
?>
文件的上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>文件的上传</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<!-- multipart/form-data:不对字符编码。
在使用包含文件上传控件的表单时,必须使用该值。
enctype属性:规定将表单数据提交到服务器时应如何编码(仅供 method="post")-->
<h1>文件上传</h1>
<input type="file" name="file" id="file">
<input type="submit" name="submit_file">
</form>
</body>
</html>
<?php
$name = $_FILES["file"]["name"];//获取文件名字
$size = $_FILES["file"]["size"];//获取文件大小
$type = $_FILES["file"]["type"];//获取文件类型
$error = $_FILES["file"]["error"];//获取上传文件的错误代码
$tmpname = $_FILES["file"]["tmp_name"];//获取上传文件的临时文件名
echo $name . "<br>";
echo $size . "<br>";
echo $type . "<br>";
echo $error . "<br>";
echo $tmpname . "<br>";
if(move_uploaded_file($tmpname,"D:/H_desktop/".$name)){
echo "上传成功";
}else{
echo "上传失败";
}
?>
上面这种方法是自己敲代码,我们也可以直接采用编辑器。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>文件的上传</title>
<script src="./ueditor/ueditor.config.js">/*引入配置文件*/</script>
<script src="./ueditor/ueditor.all.js">/*引入源码文件*/</script>
</head>
<body>
<textarea id="content" rows="10" cols="70" style="border:1px solid #E5E5E5;">55222</textarea>
<script type="text/javascript">
UE.getEditor("content");//实例化编辑器 传参,id为将要被替换的容器。
</script>
</body>
</html>
我这里采用的是ueditor编辑器,可以直接从网上下载使用;由于这个核心代码是我们下载的,所以这个文件上传是否有漏洞就需要看该编辑器;使用框架同理。
文件的下载
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>文件的下载</title>
</head>
<body>
<h1>d</h1>
<?php get_filename();?>
<form action="" method="post" enctype="multipart/form-data">
请输入要下载的文件<input type="text" name="name">
<input type="submit" value="下载">
</form>
<?php
if ($_POST)
{
$name1 = $_POST["name"];
geturl($name1);
}
?>
</body>
</html>
<?php
function get_filename(){
$current_path = getcwd();//获取当前路径
$filename=scandir($current_path.'/soft/');//扫描当前路径下的文件
foreach($filename as $value){
if($value!="."&&$value!=".."){
echo $value."<br>";
}
}
}
function geturl($name){
$url = 'http://' . $_SERVER['HTTP_HOST'].'/xiaodi/soft/'.$name;
//echo $url;
header("location:$url");
}
?>
上面代码成功了,会直接访问header,这个问题还没有解决。
对于文件下载有两种,一种就是直接url下载(只能下载.exe、.zip等);另一种就是传参下载。相比来说,第一种更安全,因为传参下载可以下载任意类型,可以查看.php的源码;而在url中并不会下载.php文件。
文件的删除
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>文件的下载</title>
</head>
<body>
<h1>d</h1>
<?php get_filename();?>
<form action="" method="post" enctype="multipart/form-data">
请输入要删除的文件<input type="text" name="name">
<input type="submit" value="删除">
</form>
<?php
if ($_POST) {
$name1 = $_POST["name"];
del_file($name1);
}
?>
</body>
</html>
<?php
function get_filename(){
$current_path = getcwd();//获取当前路径
$filename=scandir($current_path);//扫描当前路径下的文件
foreach($filename as $value){
if($value!="."&&$value!=".."){
echo $value."<br>";
}
}
}
function del_file($name){
@unlink($name);//删除文件
}
function del_files($name){
rmdir($name); //删除文件夹
}
?>
上述代码实现对文件/文件夹的删除,主要用了两个函数。
注意:在post提交数据的时候千万加上if($_POST){}这个条件。
文件的读写

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=, initial-scale=1.0">
<title>文件的下载</title>
</head>
<body>
<h1>文件读取</h1>
<?php get_filename();?>
<form action="" method="post" enctype="multipart/form-data">
请输入要查看的文件<input type="text" name="name">
<input type="submit" value="确定">
</form>
<?php
if ($_POST) {
$name1 = $_POST["name"];
fileread($name1);
}
?>
<hr><br>
<h1>文件写入</h1>
<?php get_filename();?>
<form action="" method="post" enctype="multipart/form-data">
请输入要写入的文件<input type="text" name="name">
请输入要写入的内容<input type="text" name="write">
<input type="submit" value="确定">
</form>
<?php
if ($_POST) {
$name1 = $_POST["name"];
$write = $_POST["write"];
writefile($name1,$write);
}
?>
</body>
</html>
<?php
function get_filename(){
$current_path = getcwd();//获取当前路径
$filename=scandir($current_path);//扫描当前路径下的文件
foreach($filename as $value){
if($value!="."&&$value!=".."){
echo $value."<br>";
}
}
}
//读取文件函数
function fileread($name){
$file=fopen($name,"r");
echo fread($file, filesize($name));//两个参数都是必须的
fclose($file);
}
//写入文件的函数
function writefile($name,$txt){
$file = fopen($name, "a+");//a+如果文件存在,则会追加;不存在则会创建
fwrite($file, $txt);
fclose($file);
}
?>
文件包含
<?php
include("1.txt");
?>
文件包含就是在代码中利用include\require等函数包含一个文件,然后把该文件中的内容放到该文件中执行。
网站搜索功能
就是在页面进行搜索我们想要看的东西,后端代码就是在数据库中使用select * from 表名 where 字段名 like ;此时主要的问题就是会在页面输出我们查询的内容。如果我们输入的内容是一个script代码,则就会造成xss攻击。
留言板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板</title>
</head>
<body>
<form action="" method="post">
id:<input type="text" name="id"><br>
昵称:<input type="text" name="ni"><br>
QQ:<input type="text" name="qq"><br>
留言:<br><textarea rows="8" cols="500" name="content"></textarea>
<input type="submit" value="留言">
</form>
</body>
</html>
<?php
$id = $_POST["id"];
$ni = $_POST["ni"];
$q = $_POST["qq"];
$c = $_POST["content"];
header("Content-Type: text/html;charset=utf-8");
$con=mysqli_connect("127.0.0.1", "root", "root");
if($con){
echo "连接成功";
}
$sql = "create database liuyanban";
if(mysqli_query($con, $sql)){
echo "创建成功";
}
mysqli_select_db($con, "liuyanban");
// $sql="CREATE TABLE liuyanb ( id INT ( 12 ), ni INT (12), q INT(12), c INT(122))";
// if (mysqli_query($con, $sql)) {
// echo "数据表liuyanb创建成功";
// } else {
// echo "创建数据表错误: " . mysqli_error($conn);
// }
$sql = "insert into liuyanb (id,ni,q,c) values ($id,$ni,$q,$c)";
mysqli_query($con, $sql);
$sql="select * from liuyanb";
$result=mysqli_query($con, $sql);
//var_dump($result);
while($row=mysqli_fetch_array($result)){
echo '<br>';
echo 'id:'.$row['id'].'<br>';
echo '昵称:'.$row['ni'].'<br>';
echo 'QQ:'.$row['q'].'<br>';
echo '内容:'.$row['c'].'<br>';
}
?>
上述的网站搜索还是留言板都是在页面输出用户可控信息,留言板还有可能存储在数据库中,可能造成xss漏洞。
全局变量SERVER
<?php
echo $_SERVER['PHP_SELF'];
echo "<br>";
echo $_SERVER['SERVER_NAME'];
echo "<br>";
echo $_SERVER['HTTP_HOST'];
echo "<br>";
echo $_SERVER['HTTP_REFERER'];//查看referer,即访问该网站的来源IP
echo "<br>";
echo $_SERVER['HTTP_USER_AGENT'];
echo "<br>";
echo $_SERVER['SCRIPT_NAME'];
?>

获取IP地址:
<?php
function getIp()
{
if ($_SERVER["HTTP_CLIENT_IP"] && strcasecmp($_SERVER["HTTP_CLIENT_IP"], "unknown")) {
$ip = $_SERVER["HTTP_CLIENT_IP"];
} else {
if ($_SERVER["HTTP_X_FORWARDED_FOR"] && strcasecmp($_SERVER["HTTP_X_FORWARDED_FOR"], "unknown")) {
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
} else {
if ($_SERVER["REMOTE_ADDR"] && strcasecmp($_SERVER["REMOTE_ADDR"], "unknown")) {
$ip = $_SERVER["REMOTE_ADDR"];
} else {
if (isset ($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'],
"unknown")
) {
$ip = $_SERVER['REMOTE_ADDR'];
} else {
$ip = "unknown";
}
}
}
}
return ($ip);
}
echo getIp()."<br>";
IP地址伪造来源:就是抓包修改X_FORWARDED_FOR的值;X_FORWARDED_FOR用来表示 HTTP 请求端真实 IP。
来源页伪造:就是抓包修改REFERER的值,$_SERVER["HTTP_REFERER"]可以获取其来源IP,直接访问网址referer就是空。
后台验证
服务器后台有多个文件页面,但我们通常都是只需要在第一个登陆页面登陆就行,而其他页面并不需要重复登陆,因此为了方便验证,一般使用cookie和session进行验证。
cookie:身份验证,存储到客户端浏览器
session:身份验证,存储到服务端服务器内
安全问题:cookie修改、伪造、盗取
session:会话劫持(session劫持)
cookie验证
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>账号登陆</title>
</head>
<body>
<form action="" method="post">
账号:<input type="text" name="id"><br>
密码:<input type="password" name="pass"><br>
<input type="submit" value="登陆">
</form>
</body>
</html>
<?php
header("Content-Type:text/html;charset=utf-8");
$con=mysqli_connect("127.0.0.1", "root", "root");
$username=$_POST['id'];
$password=md5($_POST['pass']);//因为数据库中存放密码是会加密
$sql="select * from 存放登录信息的表名 where username='$username' and password='$password'";
$result=mysqli_query($con,$sql);
if (mysqli_num_rows($result)){
setcookie('user',$username);//登陆成功的话就会生成一个cookie;第一个参数是cookie的名称,第二个参数是cookie的值
header('Location: index.php');//登陆成功进行跳转
}
//后台的每个页面都会进行cookie的验证,使用$_COOKIE进行验证
?>
cookie就是检验我们身份的标志,而且他又是保存在我们浏览器端,所以会造成cookie伪造、修改等等安全漏洞。
session验证
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>账号登陆</title>
</head>
<body>
<form action="" method="post">
账号:<input type="text" name="id"><br>
密码:<input type="password" name="pass"><br>
<input type="submit" value="登陆">
</form>
</body>
</html>
<?php
header("Content-Type:text/html;charset=utf-8");
$con=mysqli_connect("127.0.0.1", "root", "root");
$username=$_POST['id'];
$password=md5($_POST['pass']);//因为数据库中存放密码是会加密
$sql="select * from 存放登录信息的表名 where username='$username' and password='$password'";
$result=mysqli_query($con,$sql);
while($row=mysqli_fetch_array($result)){
session_start();
$_SESSION['username'] = $row['username'];
//echo $_SESSION['username'];
header('Location: index.php');
}
?>
session验证就比较严格,因为session每次的值都是随机变化,并不是确定的,很难造成攻击。
验证码
验证码本身是为了防止爆破的,但是有些验证码是可以通过抓包进行爆破的;抓到包之后验证码就是一成不变的。
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 简单的表单提交代码 -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>简单验证码的实现</title>
</head>
<body>
<form method="post" action="">
帐号:<input type="text" name="user">
密码:<input type="password" name="pass">
<p>验证码图片:<img src="code.php" onClick="this.src='code.php?nocache='+Math.random()" style="cursor:hand" alt="点击换一张"/>点击图片可更换验证码</p>
<p>请输入图片中的内容:<input type="text" name="authcode" value=""/></p>
<p><input type="submit" width="20px" height=19px value="提交"></input></p>
</form>
</body>
</html>
<?php
include('config/conn.php');
header("Content-type: text/html; charset=utf-8");
// session 存值并匹配用户输入值
if (isset($_REQUEST['authcode'])) {
session_start();
if (strtolower($_REQUEST['authcode'])==$_SESSION['authcode']) {//strtolower转化为小写的函数
echo"输入正确!";
$username=$_POST['user'];
$password=md5($_POST['pass']);
$sql="select * from sy_adminuser where username='$username' and password='$password'";
$result=mysql_query($sql,$conn);
if(mysql_num_rows($result)){
$row=mysql_fetch_array($result);
echo '成功!';
session_start();
$_SESSION['user']=$row['username'];//讲查询结果的数据进行赋值
header("Location: admin/add_news.php");
}else{
echo '失败!';
//header("Location: login.php");
}
# code...
}
else{
echo"输入错误!";
}
exit();
}
?>
code.php
<?php
/**
* 字母+数字的验证码生成
*/
// 开启session
session_start();
//1.创建黑色画布
$image = imagecreatetruecolor(100, 30);
//2.为画布定义(背景)颜色
$bgcolor = imagecolorallocate($image, 255, 255, 255);
//3.填充颜色
imagefill($image, 0, 0, $bgcolor);
// 4.设置验证码内容
//4.1 定义验证码的内容
$content = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
//4.1 创建一个变量存储产生的验证码数据,便于用户提交核对
$captcha = "";
for ($i = 0; $i < 4; $i++) {
// 字体大小
$fontsize = 10;
// 字体颜色
$fontcolor = imagecolorallocate($image, mt_rand(0, 120), mt_rand(0, 120), mt_rand(0, 120));
// 设置字体内容
$fontcontent = substr($content, mt_rand(0, strlen($content)), 1);
$captcha .= $fontcontent;
// 显示的坐标
$x = ($i * 100 / 4) + mt_rand(5, 10);
$y = mt_rand(5, 10);
// 填充内容到画布中
imagestring($image, $fontsize, $x, $y, $fontcontent, $fontcolor);
}
$_SESSION["captcha"] = $captcha;
//4.3 设置背景干扰元素
for ($$i = 0; $i < 200; $i++) {
$pointcolor = imagecolorallocate($image, mt_rand(50, 200), mt_rand(50, 200), mt_rand(50, 200));
imagesetpixel($image, mt_rand(1, 99), mt_rand(1, 29), $pointcolor);
}
//4.4 设置干扰线
for ($i = 0; $i < 3; $i++) {
$linecolor = imagecolorallocate($image, mt_rand(50, 200), mt_rand(50, 200), mt_rand(50, 200));
imageline($image, mt_rand(1, 99), mt_rand(1, 29), mt_rand(1, 99), mt_rand(1, 29), $linecolor);
}
//5.向浏览器输出图片头信息
header('content-type:image/png');
//6.输出图片到浏览器
imagepng($image);
//7.销毁图片
imagedestroy($image);
?>
检验上传文件的扩展名
对上传文件的扩展名检验有两种方法,一种就是js代码,在前端对其验证;另一种就是php在后端对其进行验证;js验证我们是可以绕过的,在浏览器我们可以设置禁用javascript绕过,而后端验证就不能通过该方法验证了。
js检验代码:
<script>
function checkFileExt(filename)
{
var flag = false; //状态
var arr = ["jpg","png","gif"];
//取出上传文件的扩展名
var index = filename.lastIndexOf(".");
var ext = filename.substr(index+1);
//比较
for(var i=0;i<arr.length;i++)
{
if(ext == arr[i])
{
flag = true; //一旦找到合适的,立即退出循环
alert("上传的文件符合要求!");
break;
}
}
//条件判断
if(!flag)
{
alert("上传的文件不符合要求,请重新选择!");
location.reload(true);
}
}
</script>
Ajax
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。
框架
现在有很多CMS都是采用框架开发,框架为我们封装了很多功能,方便我们使用;对于框架的使用,我们直接从网上找手册即可学习。面对框架开发的程序,漏洞的出现有两种可能,一是开发者没有利用官方要求使用某些函数或功能进行开发,二是框架爆出漏洞。以后的渗透我们就从这两个方向入手。
框架的文件和url之间关系,通常就是域名端口+index.php+模块名+控制器下的文件(该文件必须是首字母大写,不需要加扩展名)+函数+参数
使用-路由访问-控制器&对象&函数&参数 理解:URL <=> 文件
总结
漏洞就是在代码解决实际问题中产生的,所以在学习漏洞之前我们应先了解一下基础的开发;有了开发的基础,我们才能知道漏洞产生的原因,才能更好的防护。
无论是js还是利用ajax等等处理数据,只要是在前端页面处理数据而后端没有验证,都可以对其进行抓包修改数据,造成一定的破坏。
技巧:如果我们看到某个页面的返回结果是json格式,且其中有code等字段,那么大概率就是前端处理数据,很有可能存在上述漏洞。
web漏洞核心:
- 可控变量
- 特定功能的函数

浙公网安备 33010602011771号