如何让PHP的代码更安全

概述

攻击者通过构造恶意SQL命令发送到数据库,如果程序未对用户输入的 SQL命令执行判断过滤,那么生成的SQL语句可能会绕过安全性检查,插入其他用于修改后端数据库的语句,并可能执行系统命令,从而对系统造成危害

例如删除 id 为 1 的帖子,sql 如下:

$post_id = $_POST['post_id'];
$sql = "DELETE FROM posts WHERE user_id = 1 AND id = $post_id";
\DB::statement($sql);

如果有人在提交 post_id 时输入 1 OR 1 ,你的语句会组合成这样:

$sql = "DELETE FROM posts WHERE user_id = 1 AND id = 1 OR 1";

一般比较常出现在原生的 SQL 操作,框架一般会解决这方面的问题。通常采用参数控制或过滤特殊字符避免上述的问题。

越权漏洞

1. 水平越权

水平越权就是同等角色下的用户,不但能够访问和操作自己私有的数据,还能访问其他人私有的数据,其根本是基于数据的访问权限。

删除用户收款方式的场景如下:

graph LR

用户登录-->token

token-->获取收款方式列表

获取收款方式列表--token-->通过id删除

通过收款方式 {id} 执行 delete 请求的路由为:localhost/api/payments/{id}

假如用户A的收款方式有{1,2,3}    用户B的收款方式有{4,5}

如果没有做数据控制,A 登录后携带 A 的 token 执行删除的接口localhost/api/payments/4,则会删除 B 的,所以需要对destory方法做数据控制

# 1 删除前鉴权处理
public function destory($id){
    $payment = Payment::find($id);
    if ($payment->user_id != $this->currentUser->id) {
            return ...
    }
    $payment->delete();
}

# 2 参入id查询删除
public function destory($id){
    Payment::whereUserId($this->currentUser->id)->whereId($id)->delete();
}

# 3 模型关联查询
class User extends Model{
    public function payments()
    {
            return $this->hasMany('App\Payment');
    }
}

class PaymentController extends Controller{
    public function destory($id)
    {
            $this->currentUser->payments()->whereId($id)->delete();
    }
}

推荐使用第三种方式做数据控制,不然面向对象白学了。获取收款方式的列表同样需要数据权限控制,用户和收款方式存在一对多的关联关系,模型关联后,获取用户收款方式列表可以写成

class PaymentController extends Controller{
    public function index($id)
    {
        #带条件的查询
        $payments = $this->currentUser->payments()->where(function($query){
                ...
        })->get();

        #不带条件的查询
        $payments = $this->currentUser->payments;
    }
}

2. 垂直越权

低权限的角色通过一些途径,获得高权限的能力,就发生了越权访问。如普通用户 guest 修改 admin 用户的密码;guest 可直接进入后台取得域名管理、用户管理等所有权限

解决这个问题,需要把权限职责以最小颗粒细分,基于 RBAC 设计权限管理系统。分为以下关联模型

graph LR 

用户--多对多-->角色

用户--多对多-->权限

角色--多对多-->权限

每次执行请求时,在前置中间件判断这个用户是否永远该执行请求的权限,无权限则驳回。

3. 上下文越权

攻击者能够利用应用程序状态机中的漏洞获得关键资源的访问权限,这就存在上下文相关的越权。上下文相关的越权漏洞一般属于业务逻辑漏洞。 如在找回密码过程中,攻击者使用自己的账户信息通过验证,将他人的密码进行了修改。

graph LR

1.邮箱验证-->2.找回密码

在步骤1之后,执行找回密码,路由为 。如果此时没有校验当前找回密码的账户是否为进行邮箱校验后的账户,由可能产生越权漏洞.

路由 : 【PUT 】localhost/api/users/find-password,接收参数 email,new_password.

错误:校验和修改分成 2 步localhost/api/email/check->localhost/api/users/password

class UserController extends Controller{
    public function check($data)
    {
        if (checkEmail($data['email'], $data['code'])) {
                return true;
        }
        ...
    }

    public function findPassword()
    {
        $user = User::whereEmail($data['email'])->first();
        $user->password = bcrypt($data['new_password']);
        $user->save();
    }
}

正确:在 findPassword 里面再次验证完成邮箱校验的账户是否为当前找回密码的账号

class UserController extends Controller{

    public function check($data)
    {
        if (checkEmail($data['email'], $data['code'])) {
                return true;
        }
        ...
    }

    public function findPassword($data)
    {
        if (checkEmail($data['email'], $data['code'])) {
                $user = User::whereEmail($data['email'])->first();
                $user->password = $data['new_password'];
                $user->save();
        }
        ...
    }
}

限制分页条目范围,防止恶意请求

如获文章列表的接口localhost/api/articles

public function index($params){
    $pageId = $params['pageid'] ?? PAGE_ID;    //页码
    $pageSize = $params['pagesize'] ?? 15;  //条码

    $articles = Article::where(function ($query) use ($params) {
            ...
    })->take($pageSize)->skip($pageId * $pageSize)->orderby('id', 'desc')->get();
    ...
    ...
}

以上代码如果没有限制 pagesize 的范围,恶意请求者请求把 pagesize 输入 5000,10000 等甚至更大的数,会给数据库带来一定的压力,localhost/api/articles?pageid=0&pagesize=10000

//用框架自带的分页方法
public function index(){
    $builder = Article::with('category:id,name')->orderBy('id', 'desc')->paginate(8);
    return response()->json(['status' => true, 'count' => $builder->total(), 'articles' => $builder->items()]);
}

JWT 的 Token 需要二次加密

许多拓展包加密出的token并不十分安全,用base64_decode可以解密获取 加密主键、载荷等重要信息,所以通常需要对JWT的token进行二次加密

限制上传文件的类型

对于一个图片上传的接口,如果没有对上传文件的格式做限制,攻击者很有可能把 .php后缀的文件上传到public/images目录下,然后通过根目录执行这个文件。

需要设计安全的文件上传功能避免上述问题

1.文件上传的目录设置为不可执行

2.判断文件类型

3.使用随机数改写文件名和文件路径

4.单独设置文件服务器的域名

禁止或者避免写自动解压.zip 等压缩文件的代码

单纯地限制文件或压缩包大小并没有用,一个ZIP炸弹的.zip文件仅有 42 KB,但在解压后会占用 4718592 GB

避免登录密码被暴力破解

1.设定严格的速率限制,如登录次数限制,登录错误次数达 x 次时暂停登录 n 分钟

2.密码加上随机盐

public function reg(){
    $user = new User;
    $salt = radom(6);
    $user->password = bcrypt($data['password'] . $salt);
    ...
}

做好异常处理,避免在生产环境中不正确的错误报告暴露敏感数据

如果你不小心,可能会在生产环境中因为不正确的错误报告泄露了敏感信息,例如:文件夹结构、数据库结构、连接信息与用户信息。

1.在.env 文件中关闭调试模式

APP_DEBUG=true

2.php 错误控制 error_reporting、display_errors

<?php
// 关闭错误报告
error_reporting(0);

// 报告 runtime 错误
error_reporting(E_ERROR | E_WARNING | E_PARSE);

// 报告所有错误
error_reporting(E_ALL);

// 等同 error_reporting(E_ALL);
ini_set("error_reporting", E_ALL);

// 报告 E_NOTICE 之外的所有错误
error_reporting(E_ALL & ~E_NOTICE);
?>

display_errors = Off

php 弱语言的设计缺陷如:in_array

$array=[0,1,2,'3'];

var_dump(in_array('abc', $array)); //true

var_dump(in_array('1bc', $array)); //true

# 上面的情况返回的都是 true, 因为'abc'会转换为 0,'1bc'转换为 1

$a = null;
$b = false;
echo $a==$b;  //true

$c = "";
$d = 0;
echo $c==$d   //true

在一些重要的地方需要使用===来作数据判断。

LFI (本地文件包含)

LFI (本地文件包含) 是一个用户未经验证从磁盘读取文件的漏洞。

不验证过滤用户的输入 将它要渲染的模板文件用 GET 请求加载。

<body>
    <?php
      $page = $_GET['page'];
      if(!$page) {
        $page = 'main.php';
      }
      include($page);
    ?></body>

由于 Include 可以加载任何文件,不仅仅是 PHP,攻击者可以将系统上的任何文件作为包含目标传递。

index.php?page=../../etc/passwd

这将导致 /etc/passwd 文件被读取并展示在浏览器上。

要防御此类攻击,你必须仔细考虑允许用户输入的类型,并删除可能有害的字符,如输入字符中的 “.” “/” “\”。

XSS

XSS 又叫 CSS (Cross Site Script) ,跨站脚本攻击。它指的是恶意攻击者往 Web 页面里插入恶意 html 代码,当用户浏览该页之时,嵌入其中 Web 里面的 html 代码会被执行,从而达到恶意攻击用户的特殊目的。

<body>
    <?php
        $searchQuery = $_GET['q'];
        /* some search magic here */
    ?><h1>You searched for: <?php echo $searchQuery; ?></h1></body>

因为我们把用户的内容直接打印出来,不经过任何过滤,非法用户可以拼接 URL: search.php?q=%3Cscript%3Ealert(1)%3B%3C%2Fscript%3E

PHP 渲染出来的内容如下,可以看到 Javascript 代码会被直接执行:

<body>
<h1>You searched for: <script>alert(1);</script></h1>
<p>We found: Absolutely nothing because this is a demo</p>
</body>

Javascript 可以:

  • 偷走你用户浏览器里的 Cookie;
  • 通过浏览器的记住密码功能获取到你的站点登录账号和密码;
  • 盗取用户的机密信息;
  • 你的用户在站点上能做到的事情,有了 JS 权限执行权限就都能做,也就是说 A 用户可以模拟成为任何用户;
  • 在你的网页中嵌入恶意代码;

使用htmlentities()过滤特殊字符,防止大部分的 xss 攻击

CSRF (跨站请求伪造)

例如网站上有用户可以用来注销账户的链接。

<a href="http://your-website.com/delete-account" rel="external nofollow" >销毁账户</a>

如果某个用户评论:

<img src=”http://your-website.com/delete-account”> wow

用户将在查看此评论的时候删除他们的账号。

laravel 的 web 路由默认开启了 csrf 验证,原理是在客户端产生一个随机的 token,在表单校验时判断这个 token 是否是这个页面上的请求

以上就是如何让PHP的代码更安全的详细内容,更多关于让PHP的代码更安全的资料请关注我们其它相关文章!

(0)

相关推荐

  • PHP实现根据密码长度显示安全条

    本文给大家分享一段简单的代码,实现根据密码长度显示安全条功能,代码如下所示: //根据密码长度显示安全条 <ul class="clear"> <li>密 码:</li> <li> <input type="password" id="pwd" name="pwd" class="in" onKeyUp=pwStrength(this.value) on

  • 浅谈php(codeigniter)安全性注意事项

    1.httponly session一定要用httponly的否则可能被xxs攻击,利用js获取cookie的session_id. 要用框架的ci_session,更长的位数,httponly,这些默认都配好了. 不要用原生的phpsession,而要用ci_session.ci_session位数更长. 如果要用原生的session,应该这样设置(php.ini): session.sid_length //sid的长度,这里要加长,默认的太短了 session.cookie_httponl

  • PHP安全之register_globals的on和off的区别

    一.register_globals=Off和register_globals=On的区别 register_globals是php.ini里的一个配置,这个配置影响到php如何接收传递过来的参数. register_globals的值可以设置为:On或者Off,我们举一段代码来分别描述它们的不同. <form action='' method='get'> <input type='text' name='username' value='alex' > <input ty

  • PHP开发api接口安全验证的实例讲解

    php的api接口 在实际工作中,使用PHP写api接口是经常做的,PHP写好接口后,前台就可以通过链接获取接口提供的数据,而返回的数据一般分为两种情况,xml和json,在这个过程中,服务器并不知道,请求的来源是什么,有可能是别人非法调用我们的接口,获取数据,因此就要使用安全验证. 验证原理 示意图 原理 从图中可以看得很清楚,前台想要调用接口,需要使用几个参数生成签名. 时间戳:当前时间 随机数:随机生成的随机数 口令:前后台开发时,一个双方都知道的标识,相当于暗号 算法规则:商定好的运算规

  • php解决安全问题的方法实例

    PHP安全配置 (1) 打开php的安全模式 php的安全模式是个非常重要的内嵌的安全机制,能够控制一些php中的函数,比如system(),同时把很多文件操作函数进行了权限控制,也不允许对某些关键文件的文件,比如/etc/passwd,但是默认的php.ini是没有打开安全模式的,我们把它打开: safe_mode = on (2) 用户组安全 当safe_mode打开时,safe_mode_gid被关闭,那么php脚本能够对文件进行访问,而且相同组的用户也能够对文件进行访问. 建议设置为:

  • PHP更安全的密码加密机制Bcrypt详解

    前言 我们常常为了避免在服务器受到攻击,数据库被拖库时,用户的明文密码不被泄露,一般会对密码进行单向不可逆加密--哈希. 常见的方式是: 哈希方式 加密密码 md5('123456') e10adc3949ba59abbe56e057f20f883e md5('123456' . ($salt = 'salt')) 207acd61a3c1bd506d7e9a4535359f8a sha1('123456') 40位密文 hash('sha256', '123456') 64位密文 hash('

  • PHP网站常见安全漏洞,及相应防范措施总结

    目前,基于PHP的网站开发已经成为目前网站开发的主流,本文笔者重点从PHP网站攻击与安全防范方面进行探究,旨在减少网站漏洞,希望对大家有所帮助! 一.常见PHP网站安全漏洞 对于PHP的漏洞,目前常见的漏洞有五种.分别是Session文件漏洞.SQL注入漏洞.脚本命令执行漏洞.全局变量漏洞和文件漏洞.这里分别对这些漏洞进行简要的介绍. 1.session文件漏洞 Session攻击是黑客最常用到的攻击手段之一.当一个用户访问某一个网站时,为了免客户每进人一个页面都要输人账号和密码,PHP设置了S

  • PHP网页安全认证的实例详解

    PHP网页安全认证的实例详解 不基于数据库: <?php //unset($_SERVER['PHP_AUTH_USER']); $strAuthUser= $_SERVER['PHP_AUTH_USER']; $strAuthPass= $_SERVER['PHP_AUTH_PW']; if (! ($strAuthUser == "a" && $strAuthPass == "a")) { header('WWW-Authenticate:

  • PHP安全配置优化详解

    由于脚本语言和早期版本设计的诸多原因,php项目存在不少安全隐患.从配置选项来看,可以做如下的优化. 1.屏蔽PHP错误输出. 在/etc/php.ini(默认配置文件位置),将如下配置值改为Off display_errors=Off 不要将错误堆栈信息直接输出到网页上,防止黑客加以利用相关信息. 正确的做法是: 把错误日志写到日志文件中,方便排查问题. 2.屏蔽PHP版本. 默认情况下PHP版本会被显示在返回头里,如: Response Headers X-powered-by: PHP/7

  • 让Java代码更高效

    本文简单介绍一下在写代码过程中用到的一些让JAVA代码更高效的技巧. 1,将一些系统资源放在池中,如数据库连接,线程等.在standalone的应用中,数据库连接池可以使用一些开源的连接池实现,如C3P0,proxool和DBCP等,在运行在容器中的应用这可以使用服务器提供的DataSource.线程池可以使用JDK本身就提供的java.util.concurrent.ExecutorService. import java.util.concurrent.Executors; import j

  • 浅谈让你的代码更简短,更整洁,更易读的ES6小技巧

    写在文章前面 这篇文章翻译自ES6 tips and tricks to make your code cleaner, shorter, and easier to read!. 文章就代码整洁方面对es6进行了总结.如有错误欢迎指出. template literals 模板字符串 模板字符串使字符串的使用变得比以前更简单了,他们以反引号开始(`),并且能过使用${变量}来插入变量.我们来比较一下下面两行代码. var fName = 'Peter', sName = 'Smith', ag

  • 15个简单的JS编码标准让你的代码更整洁(小结)

    作者 | Daniel Anderson 本文最初发布于 Medium 网站,经原作者授权后翻译和分享. 编码标准可以帮助以下方面: 保持代码一致 易于阅读和理解 易于维护 下面的编码标准是我对上述几点有帮助的看法. 1. 比较时使用 === 代替 == 这很重要,因为JavaScript是一种动态语言,因此使用==可能会给您带来意想不到的结果,因为它允许类型不同. Fail: if (val == 2) Pass: if (val === 2) 2. 永远不要使用 var,使用 let 来代替

  • ES2020让代码更优美的运算符 (?.) (??)

    链判断运算符(?.) 非常好用.常用,搭配Null 判断运算符使用,效果更佳,完美! 来,上代码: 我们通常获取一个对象多层的属性值时,需要进行多次的判断.如不判断,一个为空则报错,导致后面无法继续下去. // error const firstName = message.body.user.firstName; // ok const firstName = (message && message.body && message.body.user &&

  • 如何让PHP的代码更安全

    概述 攻击者通过构造恶意SQL命令发送到数据库,如果程序未对用户输入的 SQL命令执行判断过滤,那么生成的SQL语句可能会绕过安全性检查,插入其他用于修改后端数据库的语句,并可能执行系统命令,从而对系统造成危害 例如删除 id 为 1 的帖子,sql 如下: $post_id = $_POST['post_id']; $sql = "DELETE FROM posts WHERE user_id = 1 AND id = $post_id"; \DB::statement($sql);

  • 使用Go重试机制代码更可靠

    目录 场景 设计原理 场景 服务依赖第三方服务或者其他服务,但有时第三方服务偶发性出现问题并且会快速的恢复,而我们的系统可能因为这些偶发性出现问题. 所以无法保证以来服务的可靠性,但是我们可以通过其他的方式来保证我们的服务是稳定的,可靠的. 重试机制可以协助我们构建健壮的服务.比如调用第三方服务或执行某操作时,出现执行失败,我们可以让其再重试几次再抛出错误. 设计原理 重试机制:某方法或者函数执行失败了重新执行,尝试重新执行后,尝试几次失败后结束.期间成功一次则表示成功. 重试次数 需要被重新执

  • 让Python代码更快运行的5种方法

    不论什么语言,我们都需要注意性能优化问题,提高执行效率.选择了脚本语言就要忍受其速度,这句话在某种程度上说明了Python作为脚本语言的不足之处,那就是执行效率和性能不够亮.尽管Python从未如C和Java一般快速,但是不少Python项目都处于开发语言领先位置. Python很简单易用,但大多数人使用Python都知道在处理密集型cpu工作时,它的数量级依然低于C.Java和JavaScript.但不少第三方不愿赘述Python的优点,而是决定自内而外提高其性能.如果你想让Python在同一

  • combox改进版 页面原型参考dojo的,比网上jQuery的那些combox功能强,代码更小

    简介 对于combox功能实现的最小化js代码,页面原型参考dojo的combox模样,支持键盘动作以及自动筛选,高亮等 API 初始化方法 编码方式 var c=new combox(document.getElementById('a')); 或者 var c=new combox(document.getElementById('a'),{ debug:true }); Css申明方式 <select class="combox" debug="true"

  • 如何让你的JS代码更好看易读

    作为JS程序员,自己写的代码如果好看易读,不只是自己看起来好看,在别的程序员接手以后,也会是交接工作异常顺利. 不要在代码中留大段注释掉的代码 留给git去管理,不然你要git干嘛 // bad // function add() { // const a = b + c // return a // } function add() { return a + 1000 } // good function add() { return a + 1000 } 适当地换行 // bad funct

  • 让代码变得更易维护的7个Python库

    随着软件项目进入"维护模式",对可读性和编码标准的要求很容易落空(甚至从一开始就没有建立过那些标准).然而,在代码库中保持一致的代码风格和测试标准能够显著减轻维护的压力,也能确保新的开发者能够快速了解项目的情况,同时能更好地全程保持应用程序的质量. 使用外部库来检查代码的质量不失为保护项目未来可维护性的一个好方法.以下会推荐一些我们最喜爱的 检查代码 (包括检查 PEP 8 和其它代码风格错误)的库,用它们来强制保持代码风格一致,并确保在项目成熟时有一个可接受的测试覆盖率. 检查你的代

随机推荐