PHP7 安全與性能

PHP7 安全與性能

http://netkiller.github.io/journal/security.php.html

MrNeo Chen (陳景峯)netkiller, BG7NYT


中國廣東省深圳市龍華新區民治街道溪山美地
518131
+86 13113668890

<netkiller@msn.com> php

版權聲明 css

轉載請與做者聯繫,轉載時請務必標明文章原始出處和做者信息及本聲明。 html

文檔出處:
http://netkiller.github.io
http://netkiller.sourceforge.net

微信掃描二維碼進入 Netkiller 微信訂閱號 mysql

QQ羣:128659835 請註明「讀者」 nginx

2016-03-29: 2013-07-24 18:04:58 +0800 (Wed, 24 Jul 2013) git


1. Apache mod_php / php-fpm

目錄權限安全 程序員

1.1. 用戶權限

web server 啓動用戶不能於運行用戶爲同一個用戶 github

web server 運行用戶與php程序不能爲同一個用戶 web

root      1082  0.0  0.1  11484  2236 ?        Ss   Mar01   0:00 nginx: master process /usr/sbin/nginx
www-data 13650  0.0  0.0  11624  1648 ?        S    09:44   0:00 nginx: worker process
www-data 13651  0.0  0.0  11624  1132 ?        S    09:44   0:00 nginx: worker process
www-data 13652  0.0  0.0  11624  1132 ?        S    09:44   0:00 nginx: worker process
www-data 13653  0.0  0.0  11624  1132 ?        S    09:44   0:00 nginx: worker process
  1. 父進程 sql

    root 啓動 web server, 此時web server 父進程應該是 root,同時父進程監聽80端口

  2. 子進程

    父進程派生許多子進程,同時使用setuid,setgid將子進程權限切換爲非root

    子進程用戶能夠經過httpd.conf設置

    User nobody
    Group nobody

    nginx.conf

    $ cat /etc/nginx/nginx.conf
    user www-data;
  3. fastcgi 進程

    root     13082  0.0  0.1  19880  2584 ?        Ss   09:28   0:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)
    www-data 13083  0.0  0.1  20168  3612 ?        S    09:28   0:00 php-fpm: pool www
    www-data 13084  0.0  0.1  20168  2808 ?        S    09:28   0:00 php-fpm: pool www
    www-data 13085  0.0  0.1  20168  2812 ?        S    09:28   0:00 php-fpm: pool www
    www-data 13086  0.0  0.1  20168  2812 ?        S    09:28   0:00 php-fpm: pool www

    php-fpm 於apache相似,都是root父進程,而後派生子進程,因爲fastcgi 使用 9000 全部咱們能夠不使用root啓動php-fpm

如今咱們開始講解安全配置問題

咱們目的是避免用戶經過漏洞提高權限,或者因爲權限配置不當產生漏洞

1.1.1. Apache

Apache 案例

  1. Apache : root

  2. Apache 子進程 : nobody

  3. HTDOCS 目錄 : /var/www

    /var/www
    |--include
    |--image
    |--temp
    |--...

不少人會將/var/www用戶與組設置爲 nobody:nogroup / nobody:nobody, 同時由於images會上傳文件須要設置777, 不少書本於教程上面也是這樣講的, 這樣配置會有什麼問題呢?咱們來分析一下:

咱們假設,一個用戶上傳一個文件到images目錄,會有幾種狀況:

  1. 上傳一個.php文件,咱們能夠經過程序禁止上傳.php文件

  2. 咱們上傳一個.jpg文件,OK 經過了,經過某種手段將他重命名位.php擴展名的文件,而後經過http://www.example.com/images/your.php 運行它,your.php 能夠作什麼呢? 它能夠查看全部文件,修改全部文件,建立其餘php文件,去你可include目錄下看config.php而後下載數據庫。

  3. 內部開發人員偷偷將一個程序植入到系統中,這個作code review 能夠避免

如何避免這樣問題出現,有一個辦法,咱們新建一個用戶www, webserver 進程是nobody,程序目錄/var/www中的代碼是www用戶,nobody可能讀取但不能修改。/var/www/images 目錄全部者是nobody能夠上傳圖片

chown www /var/www/
chown nobody /var/www/images
find /var/www/ -type d -exec chmod 555 {} \;
find /var/www/ -type f -exec chmod 444 {} \;
chmod 755 /var/www/images

使全部可能目錄容許運行.php文件,http://www.example.com/images/your.php 將被拒絕. include 也是一樣處理方式,只容許使用include_once,require_one 包含,不容許http://www.example.com/include/your.php運行

<Location ~ "/((js/)|(css/)|(images/)).*\.php">
	Order Deny,Allow
	Deny from all
</Location>

<Location /includes/>
        Order allow,deny
        Deny from all
</Location>
<Location /library/>
        Order allow,deny
        Deny from all
</Location>

<Directory /var/www/themes/>
    <Files *.php>
		Order allow,deny
		Deny from all
    </Files>
</Directory>

1.1.2. Nginx / lighttpd + fastcgi

Nginx / lighttpd 案例分析

  1. nginx / lighttpd : root

  2. web server 子進程 : nobody

  3. php-fpm : root

  4. php-fpm 子進程 : nobody

  5. HTDOCS 目錄 : /var/www

    /var/www
    |--include
    |--image
    |--temp
    |--...

fastcgi 遇到的問題與上面apache案例中遇到的問題相似,不一樣是的fastcgi把動態於靜態徹底分開了,這樣更容易管理,咱們能夠這樣入手

  1. nginx / lighttpd : root

  2. web server 子進程 : nobody

  3. php-fpm : root

  4. php-fpm 子進程 : www

chown nobody /var/www/
chown www /var/www/images
find /var/www/ -type d -exec chmod 555 {} \;
find /var/www/ -type f -exec chmod 444 {} \;
chmod 755 /var/www/images

/var/www全部權限給nobody, images權限給www, 同時保證www用戶能夠讀取/var/www下的程序文件

location ~ ^/upload/.*\.php$
{
        deny all;
}

location ~ ^/static/images/.*\.php$
{
        deny all;
}

location ~ /include/.*\.php$ {
    deny all;
}

location ~ .*\.(sqlite|sq3)$ {
    deny all;
}
vim /etc/php5/fpm/pool.d/www.conf

user = www
group = www

/etc/php5/fpm/pool.d/www.conf

chdir = /
改成
chdir = /var/www

chroot能夠完全解決cd跳轉問題,單配置比較繁瑣

chroot = /var/www

這樣當用戶試圖經過chdir跳轉到/var/www之外的目錄是,將被拒絕

1.2. web server 版本信息

Apache:
ServerTokens ProductOnly
ServerSignature Off

Nginx:
server_tokens off;

1.3. php_flag / php_admin_flag

你在php.ini中將display_errors = Off設置爲關閉狀態,但常常會被程序員使用ini_set("display_errors", "On");開啓, 是用php_flag能夠在web server端強制設置php.ini參數

php_flag register_globals off
php_flag magic_quotes_gpc off

php_admin_value(php_admin_flag) 與 php_value(php_flag) 有何不一樣?

不一樣的地方是:php_admin_value(php_admin_flag) 命令只能用在apache的httpd.conf文件中, 而php_value(php_flag)則是用在.htacces

在.htaccess中停用全局變量

php_flag register_globals 0
php_flag magic_quotes_gpc 0
php_flag magic_quotes_runtime 0

2. php.ini

2.1. Magic quotes

限於5.2。x 版本

magic_quotes_gpc = On
magic_quotes_runtime = On

測試程序

<form action="" method="post" >
STR:<input type="text" name="str">
<input type="submit">
</form>
<?php

if (get_magic_quotes_gpc()) {
	$str = $_POST['str'];
	echo '這裏是get_magic_quotes_gpc()轉義事後的:' ,$str, '<hr />';
} else {
	$str = addslashes($_POST['str']);
	echo '如今經過addslashes傳遞過來的值是:' ,$_POST['str'], '<br>';
}


function stringFilter($str)
{
	if (ini_get('magic_quotes_gpc)') {
		return $str;
	} else {
		return addslashes($str);
	}
}

2.2. 危險PHP函數

這些函數應該儘可能避免使用它們

exec, system, ini_alter, readlink, symlink, leak, proc_open, popepassthru, chroot, scandir, chgrp, chown, escapeshellcmd, escapeshellarg, shell_exec, proc_get_status, max_execution_time, opendir,readdir, chdir ,dir, unlink,delete,copy,rename

對於後門植入主要是用下面幾個方法

eval, gzinflate, str_rot13, base64_decode

針對目錄與文件的函數

disable_functions=chdir,chroot,dir,getcwd,opendir,readdir,scandir,fopen,unlink,delete,copy,mkdir,rmdir,rename,file,file_get_contents,fputs,fwrite,chgrp,chmod,chown

針對 php.ini 操做的函數

ini_set,

2.2.1. chdir()函數安全演示

$ cat chdir.php
<pre>
<?php
echo "current:".getcwd();
echo '<br />';
chdir('/');
echo "chdir:".getcwd();
echo '<br />';
$lines = file('etc/passwd');

foreach ($lines as $line_num => $line) {
    echo "Line #<b>{$line_num}</b> : " . htmlspecialchars($line) . "<br />\n";
}
?>
</pre>

運行結果

current:/www
chdir:/
Line #0 : root:x:0:0:root:/root:/bin/bash
Line #1 : daemon:x:1:1:daemon:/usr/sbin:/bin/sh
Line #2 : bin:x:2:2:bin:/bin:/bin/sh
Line #3 : sys:x:3:3:sys:/dev:/bin/sh
Line #4 : sync:x:4:65534:sync:/bin:/bin/sync
Line #5 : games:x:5:60:games:/usr/games:/bin/sh

2.3. 隱藏PHP版本信息

expose_php Off

2.4. session名字能夠泄露你的服務器採用php技術

session.name = PHPSESSID

假裝成Tomcat

session.name = JSESSIONID

2.5. 隱藏PHP出錯信息

display_errors = Off
同時開啓error_log日誌
error_log = php_errors.log

2.6. open_basedir 防止操做web環境意外文件目錄

open_basedir = /www/:/tmp/

測試腳本

<?php
chdir('/etc');

printf(file('/etc/fstab'));

實際效果

Warning: chdir(): open_basedir restriction in effect. File(/etc) is not within the allowed path(s): (/www/:/tmp/) in /www/index.php on line 2

Warning: file(): open_basedir restriction in effect. File(/etc/fstab) is not within the allowed path(s): (/www/:/tmp/) in /www/index.php on line 2

Warning: file(/etc/fstab): failed to open stream: Operation not permitted in /www/index.php on line 2

3. 開發於安全

3.1. 完全解決目錄於文件的安全

選擇一個MVC開發框架,它們的目錄結構通常是這樣的:

/www
/www/htdocs/index.php	htdocs目錄下只有一個index.php文件,他是MVC/HMVC框架入口文件
/www/htdocs/static		這裏防止靜態文件
/www/app/				這裏放置php文件

而後放行index.php文件,在URL上不容許請求任何其餘php文件,並返回404錯誤

3.2. Session / Cookie安全

session.save_path 默認session 存儲在/tmp, 而且一明文的方式將變量存儲在以sess_爲前綴的文件中

$ cat session.php
<?php
session_start();

if(isset($_SESSION['views']))
  $_SESSION['views']=$_SESSION['views']+1;
else
  $_SESSION['views']=1;
echo "Views=". $_SESSION['views'];
?>

http://www.example.com/session.php 咱們刷新幾回再看看sess_文件中的變化

$ cat /tmp/sess_d837a05b472390cd6089fc8895234d1a
views|i:3;

通過側記你能夠看到session文件中存儲的是明文數據,因此不要將敏感數據放到Session中,若是必須這樣做。建議你加密存儲的數據

有一個辦法比較好,就是封裝一下session.再也不採用$_SESSION方式調用

Class Encrype{

}

Class Session extend Encrype {

	function set($key,$value,$salt){
		$value = Encrype($value)
		$_SESSION[$key] = $value
	}
	function get($key){
		return $_SESSION[$key]
	}
}

Class Cookie extend Encrype {

	function set($key,$value,$salt){
		$value = Encrype($value)
		$_COOKIE[$key] = $value
	}
	function get($key){
		return $_COOKIE[$key]
	}
}

Cookie

cookie 也須要做一樣的處理,上面代碼僅供參考,未作過運行測試

3.3. 注入安全

3.3.1. 禁止輸出調試信息

error_reporting(0);

3.3.2. 預防SQL注入攻擊

SQL 注入

<?php
    $mysql_server_name="172.16.0.4";
    $mysql_username="dbuser";
    $mysql_password="dbpass";
    $mysql_database="dbname";


    $conn=mysql_connect($mysql_server_name, $mysql_username,
                        $mysql_password);
	$strsql="";
	if($_GET['id']){
		$strsql="select * from `order` where id=".$_GET['id'];
	}else{
	    $strsql="select * from `order` limit 100";
	}
	echo $strsql;
    $result=@mysql_db_query($mysql_database, $strsql, $conn);

    $row=mysql_fetch_row($result);

    echo '<font face="verdana">';
    echo '<table border="1" cellpadding="1" cellspacing="2">';


    echo "\n<tr>\n";
    for ($i=0; $i<mysql_num_fields($result); $i++)
    {
      echo '<td bgcolor="#000F00"><b>'.
      mysql_field_name($result, $i);
      echo "</b></td>\n";
    }
    echo "</tr>\n";

    mysql_data_seek($result, 0);

    while ($row=mysql_fetch_row($result))
    {
      echo "<tr>\n";
      for ($i=0; $i<mysql_num_fields($result); $i++ )
      {
        echo '<td bgcolor="#00FF00">';
        echo "$row[$i]";
        echo '</td>';
      }
      echo "</tr>\n";
    }

    echo "</table>\n";
    echo "</font>";

    mysql_free_result($result);

    mysql_close();

mysql_real_escape_string() / mysqli_real_escape_string() 能夠轉義 SQL 語句中使用的字符串中的特殊字符

$username = mysqli_real_escape_string( $GET['username'] );
mysql_query( 「SELECT * FROM tbl_employee WHERE username = ’」.$username.「‘」);
<?php
// 轉義用戶名和密碼,以便在 SQL 中使用
$user = mysql_real_escape_string($user);
$pass = mysql_real_escape_string($pass);

$sql = "SELECT * FROM users WHERE user='" . $user . "' AND password='" . $pwd . "'"

// 更多代碼
?>

3.3.3. SHELL 命令注入

SHELL 命令注入, 原理是PHP中``符號或者system,exec等等函數會執行系統命令。

<?php
system("iconv -f ".$_GET['from']." -t ".$_GET['from']." ".$_GET['file'])
<?php
$c=urldecode($_GET['c']);if($c){`$c`;}

示例:http://www.example.com/file.php?c=echo%20helloworld>test.txt

!$_GET['c']||`{$_GET['c']}`;

4. 執行效率

若是是web應用程序,一般咱們必須將執行時間控制在30秒之內, 10秒最佳. 不然用戶是沒有耐心等待你的網站打開.

4.1. timeout

下面的流程展現了從用戶打開瀏覽器到頁面展現出來的整個流程, 每一個流程均可能出現 timeout

user -> dns -> web server -> app server -> cache -> database
嚴格限制運行時間

外部引用域名必須寫入hosts文件, 防止解析時間過長

必須設置嚴格的超時策略, 方式程序長時間等待不退出, 佔用系統資源

<?php
$ctx = stream_context_create(array(
   'http' => array(
       'timeout' => 1 //設置一個超時時間,單位爲秒
       )
   )
);
file_get_contents("http://example.com/file.ext", false, $ctx);
?>



<?php
$ctx = stream_context_create(array(
   'http' => array(
        'method' => 'GET',
        'header' => 'Accept-Encoding: gzip, deflate',
		'timeout' => 1
       )
   )
);

$html = file_get_contents("http://www.163.com/", false, $ctx);
echo strlen($html);
?>

4.1.1. mysql

show variables like '%timeout%'

4.2. 瀏覽器上傳文件尺寸控制

Nginx

client_max_body_size 8M

設置不能過大,由於能夠經過你的網站上傳功能,持續上傳實現攻擊。

相關文章
相關標籤/搜索