多数大学生出来选择的工作和专业无关
首页 > 专业知识

PHP安全基础原则与方法

时间:2018-04-27 16:56:29 [来源]:郑州PHP培训学校

   PHP安全基础原则与方法

  原则:
  1.2.1. 深度防范
  深度防范原则是安全专业人员人人皆知的原则,它说明了冗余安全措施的价值,这是被历史所证明的。
  深度防范原则可以延伸到其它领域,不仅仅是局限于编程领域。使用过备份伞的跳伞队员可以证明有冗余安全措施是多么的有价值,尽管大家永远不希望主伞失效。一个冗余的安全措施可以在主安全措施失效的潜在的起到重大作用。
  回到编程领域,坚持深度防范原则要求您时刻有一个备份方案。如果一个安全措施失效了,必须有另外一个提供一些保护。例如,在用户进行重要操作前进行重新用户认证就是一个很好的习惯,尽管你的用户认证逻辑里面没有已知缺陷。如果一个未认证用户通过某种方法伪装成另一个用户,提示录入密码可以潜在地避免未认证(未验证)用户进行一些关键操作。
  尽管深度防范是一个合理的原则,但是过度地增加安全措施只能增加成本和降低价值。
  1.2.2. 最小权限
  我过去有一辆汽车有一个佣人钥匙。这个钥匙只能用来点火,所以它不能打开车门、控制台、后备箱,它只能用来启动汽车。我可以把它给泊车员(或把它留在点火器上),我确认这个钥匙不能用于其它目的。
  把一个不能打开控制台或后备箱的钥匙给泊车员是有道理的,毕竟,你可能想在这些地方保存贵重物品。但我觉得没有道理的是为什么它不能开车门。当然,这是因为我的观点是在于权限的收回。我是在想为什么泊车员被取消了开车门的权限。在编程中,这是一个很不好的观点。相反地,你应该考虑什么权限是必须的,只能给予每个人完成他本职工作所必须的尽量少的权限。
  一个为什么佣人钥匙不能打开车门的理由是这个钥匙可以被复制,而这个复制的钥匙在将来可能被用于偷车。这个情况听起来不太可能发生,但这个例子说明了不必要的授权会加大你的风险,即使是增加了很小权限也会如此。风险最小化是安全程序开发的主要组成部分。
  你无需去考虑一项权限被滥用的所有方法。事实上,你要预测每一个潜在攻击者的动作是几乎不可能的。
  1.2.3. 简单就是美
  复杂滋生错误,错误能导致安全漏洞。这个简单的事实说明了为什么简单对于一个安全的应用来说是多么重要。没有必要的复杂与没有必要的风险一样糟糕。
  例如,下面的代码摘自一个最近的安全漏洞通告:
  CODE:
  $search = (isset($_GET['search']) ? $_GET['search'] : '');?>
  这个流程会混淆$search变量受污染*的事实,特别是对于缺乏经验的开发者而言。上面语句等价于下面的程序:
  CODE:
  $search = '';
  if (isset($_GET['search'])){
  $search = $_GET['search'];
  }
  ?>
  上面的两个处理流程是完全相同的。现在请注意一下下面的语句:
  $search = $_GET['search'];
  使用这一语句,在不影响流程的情况下,保证了$search变量的状态维持原样,同时还可以看出它是否受污染。
  * 译注:受污染变量,即在程序执行过程中,该变量的值不是由赋值语句直接指定值,而是来自其它来源,如控制台录入、数据库等。
  1.2.4. 暴露最小化
  PHP应用程序需要在PHP与外部数据源间进行频繁通信。主要的外部数据源是客户端浏览器和数据库。如果你正确的跟踪数据,你可以确定哪些数据被暴露了。Internet是最主要的暴露源,这是因为它是一个非常公共的网络,您必须时刻小心防止数据被暴露在Internet上。
  数据暴露不一定就意味着安全风险。可是数据暴露必须尽量最小化。例如,一个用户进入支付系统,在向你的服务器传输他的信用卡数据时,你应该用SSL去保护它。如果你想要在一个确认页面上显示他的信用卡号时,由于该卡号信息是由服务器发向他的客户端的,你同样要用SSL去保护它。
  再谈谈上一小节的例子,显示信用卡号显然增加了暴露的机率。SSL确实可以降低风险,但是最佳的解决方案是通过只显示最后四位数,从而达到彻底杜绝风险的目的。
  为了降低对敏感数据的暴露率,你必须确认什么数据是敏感的,同时跟踪它,并消除所有不必要的数据暴露。在本书中,我会展示一些技巧,用以帮助你实现对很多常见敏感数据的保护。
  方法:
  1.3.1. 平衡风险与可用性
  用户操作的友好性与安全措施是一对矛盾,在提高安全性的同时,通常会降低可用性。在你为不合逻辑的使用者写代码时,必须要考虑到符合逻辑的正常使用者。要达到适当的平衡的确很难,但是你必须去做好它,没有人能替代你,因为这是你的软件。
  尽量使安全措施对用户透明,使他们感受不到它的存在。如果实在不可能,就尽量采用用户比较常见和熟悉的方式来进行。例如,在用户访问受控信息或服务前让他们输入用户名和密码就是一种比较好的方式。
  当你怀疑可能有非法操作时,必须意识到你可能会搞借。例如,在用户操作时如果系统对用户身份有疑问时,通常用让用户再次录入密码。这对于合法用户来说只是稍有不便,而对于攻击者来说则是铜墙铁壁。从技术上来说,这与提示用户进行重新登录基本是一样的,但是在用户感受上,则有天壤之别。
  没有必要将用户踢出系统并指责他们是所谓的攻击者。当你犯错时,这些流程会极大的降低系统的可用性,而错误是难免的。
  在本书中,我着重介绍透明和常用的安全措施,同时我建议大家对疑似攻击行为做出小心和明智的反应。
  1.3.2. 跟踪数据
  作为一个有安全意识的开发者,最重要的一件事就是随时跟踪数据。不只是要知道它是什么和它在哪里,还要知道它从哪里来,要到哪里去。有时候要做到这些是困难的,特别是当你对WEB的运做原理没有深入理解时。这也就是为什么尽管有些开发者在其它开发环境中很有经验,但他对WEB不是很有经验时,经常会犯错并制造安全漏洞。
  大多数人在读取EMAIL时,一般不会被题为"Re: Hello"之类的垃圾邮件所欺骗,因为他们知道,这个看起来像回复的主题是能被伪造的。因此,这封邮件不一定是对前一封主题为"Hello."的邮件的回复。简而言之,人们知道不能对这个主题不能太信任。但是很少有人意识到发件人地址也能被伪造,他们错误地认为它能可靠地显示这个EMAIL的来源。
  Web也非常类似,我想教给大家的其中一点是如何区分可信的和不可信的数据。做到这一点常常是不容易的,盲目的猜测并不是办法。
  PHP通过超级全局数组如$_GET, $_POST, 及$_COOKIE清楚地表示了用户数据的来源。一个严格的命名体系能保证你在程序代码的任何部分知道所有数据的来源,这也是我一直所示范和强调的。
  知道数据在哪里进入你的程序是极为重要的,同时知道数据在哪里离开你的程序也很重要。例如,当你使用echo指令时,你是在向客户端发送数据;当你使用mysql_query时,你是在向MySQL数据库发送数据(尽管你的目的可能是取数据)。
  在我审核PHP代码是否有安全漏洞时,我主要检查代码中与外部系统交互的部分。这部分代码很有可能包含安全漏洞,因此,在开发与代码检查时必须要加以特别仔细的注意。
  PHP编程的五个良好习惯
  根据具体的情况,一般的开发人员往往比优秀的开发人员的效率低 10%~20%。优秀的开发人员的效率更高,因为他们拥有丰富的经验和良好的编程习惯。不良的编程习惯将会影响到效率。本文通过展示一些良好的编程习惯,帮助您成为更优秀的程序员。
  这些良好的编程习惯不仅能提高效率,还能让您编写出在应用程序的整个生命周期中易于维护的代码。编写出来的代码可能需要大量的维护;应用程序的维护是一笔很 大的开支。养成良好的编程习惯能够提高设计质量(比如模块化),从而使代码更加容易理解,因此维护就更加容易,同时也降低维护成本。
  不良的编程习惯会造成代码缺陷,使其难以维护和修改,并且很可能在修改时又引入其他缺陷。以下是 5 个良好的编程习惯,能够帮助 PHP 代码避免这些缺陷:
  ◆使用良好的命名。
  ◆分成更小的部分。
  ◆为代码添加注释。
  ◆处理错误条件。
  ◆切忌使用复制粘贴。
  下面将详细介绍这些习惯:
  使用良好的命名
  使用良好的命名是最重要的编程习惯,因为描述性强的名称让代码更加容易阅读和理解。代码是否好理解取决于是否能在未来维护它。即便代码不带有注释,如果它很容易理解,将大大方便日后的更改。这个习惯的目标是让您编写的代码像书本一样容易阅读和理解。
  不良习惯:含糊的或无意义的名称
  清单 1 中的代码包含过短的变量名、难以辨认的缩写词,并且方法名不能反映该方法的功能。如果方法名给人的感觉是它应该做这件事情,而实际中它却做另外的事情,这将带来严重的问题,因为它会误导人。
  清单 1. 不良习惯:含糊的或无意义的名称
  <?php
  function getNBDay($d)
  {
  switch($d) {
  case 5:
  case 6:
  case 7:
  return 1;
  default:
  return ($d + 1);
  }
  }
  $day = 5;
  $nextDay = getNBDay($day);
  echo ("Next day is: " . $nextDay . "n");
  ?>
  良好习惯:说明性强并且简洁的名称
  清单 2 中的代码体现了良好的编程习惯。新的方法名具有很强的说明性,反映了方法的用途。同样,更改后的变量名也更具说明性。惟一的保持最短的变量是 $i,在本清单中,它是一个循环变量。尽管很多人不赞同使用过短的名称,但在循环变量中使用还是可以接受的(甚至有好处),因为它明确表明了代码的功能。
  清单 2. 良好习惯:说明性强并且简洁的名称
  <?php
  define ('MONDAY', 1);
  define ('TUESDAY', 2);
  define ('WEDNESDAY', 3);
  define ('THURSDAY', 4);
  define ('FRIDAY', 5);
  define ('SATURDAY', 6);
  define ('SUNDAY', 7);
  /*
  *
  * @param $dayOfWeek
  * @return int Day of week, with 1 being Monday and so on.
  */
  function findNextBusinessDay($dayOfWeek)
  {
  $nextBusinessDay = $dayOfWeek;
  switch($dayOfWeek) {
  case FRIDAY:
  case SATURDAY:
  case SUNDAY:
  $nextBusinessDay = MONDAY;
  break;
  default:
  $nextBusinessDay += 1;
  break;
  }
  return $nextBusinessDay;
  }
  $day = FRIDAY;
  $nextBusDay = findNextBusinessDay($day);
  echo ("Next day is:" . $nextBusDay . "n");?>
  我们鼓励您将大的条件拆分为一个方法,然后用能够描述该条件的名字命名方法。这个技巧能够提高代码的可读性,并且能够将条件具体化,使之能够被提取甚至重用。如果条件发生变化,更新方法也很容易。因为方法拥有一个有意义的名字,所以它能反映代码的用途,让代码更容易阅读。
  分成更小的部分
  专心解决一个问题之后再继续编程,这样会让您更轻松。在解决一个紧急的问题时,如果继续编程,会使函数越来越长。从长远来说,这并不是一个问题,但您要记得回过头来将它重构为更小的部分。
  重构是个不错的主意,但您应该养成编写更短、功能更集中的代码。短的方法能够在一个窗口中一次看完,并且容易理解。如果方法过长,不能在一个窗口中一次看完,那么它就变得不容易理解,因为您不能快速地从头到尾了解它的整个思路。
  构建方法时,您应该养成这样的习惯,让每个方法只完成一件事情。这个习惯很好,因为:首先,如果方法只完成一件事情,那么它就更容易被重用;其次,这样的方法容易测试;第三,这样的方法便于理解和更改。
  不良习惯:过长的方法(完成很多件事情)
  清单 3 展示了一个很长的函数,其中存在很多问题。它完成很多件事情,因此不够紧凑。它也不便于阅读、调试和测试。它要做的事情包括遍历一个文件、构建一个列表、为每个对象赋值、执行计算等等。
  清单 3. 不良习惯:过长的函数
  <?php
  function writeRssFeed($user)
  {
  // Get the DB connection information
  // look up the user's preferences...
  $link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')OR die(mysql_error());
  // Query
  $perfsQuery = sprintf("SELECT max_stories FROM user_perfs WHERE user= '%s'",mysql_real_escape_string($user));
  $result = mysql_query($query, $link);
  $max_stories = 25; // default it to 25;
  if ($row = mysql_fetch_assoc($result)) {
  $max_stories = $row['max_stories'];
  }
  // go get my data
  $perfsQuery = sprintf("SELECT * FROM stories WHERE post_date = '%s'",mysql_real_escape_string());
  $result = mysql_query($query, $link);
  $feed = "<rss version="2.0">" .
  "<channel>" .
  "<title>My Great Feed</title>" .
  "<link>http://www.example.com/feed.xml</link>" .
  "<description>The best feed in the world</description>" .
  "<language>en-us</language>" .
  "<pubDate>Tue, 20 Oct 2008 10:00:00 GMT</pubDate>" .
  "<lastBuildDate>Tue, 20 Oct 2008 10:00:00 GMT</lastBuildDate>" .
  "<docs>http://www.example.com/rss</docs>" .
  "<generator>MyFeed Generator</generator>" .
  "<managingEditor>editor@example.com</managingEditor>" .
  "<webMaster>webmaster@example.com</webMaster>" .
  "<ttl>5</ttl>";
  // build the feed...
  while ($row = mysql_fetch_assoc($result)) {$title = $row['title'];
  $link = $row['link'];
  $description = $row['description'];
  $date = $row['date'];
  $guid = $row['guid'];
  $feed .= "<item>";
  $feed .= "<title>" . $title . "</title>";$feed .= "<link>" . $link . "</link>";$feed .= "<description> " . $description . "</description>";$feed .= "<pubDate>" . $date . "</pubDate>";$feed .= "<guid>" . $guid . "</guid>";$feed .= "</item>";
  }
  $feed .= "</rss";
  // write the feed out to the server...
  echo($feed);
  }
  ?>
 

上一篇:PHP无限级分类方法及代码

下一篇:PHP构建语义WebCRUD操作