1. 程式人生 > 遊戲 >推理AVG《流星世界演繹者:警徽和匕首》6月10日PC發售

推理AVG《流星世界演繹者:警徽和匕首》6月10日PC發售

PHP 安全與效能

摘要

我的系列文件

Netkiller Architect 手札

Netkiller Developer 手札

Netkiller PHP 手札

Netkiller Python 手札

Netkiller Testing 手札

Netkiller Cryptography 手札

Netkiller Linux 手札

Netkiller Debian 手札

Netkiller CentOS 手札

Netkiller FreeBSD 手札

Netkiller Shell 手札

Netkiller Security 手札

Netkiller Web 手札

Netkiller Monitoring 手札

Netkiller Storage 手札

Netkiller Mail 手札

Netkiller Docbook 手札

Netkiller Project 手札

Netkiller Database 手札

Netkiller PostgreSQL 手札

Netkiller MySQL 手札

Netkiller NoSQL 手札

Netkiller LDAP 手札

Netkiller Network 手札

Netkiller Cisco IOS 手札

Netkiller H3C 手札

Netkiller Multimedia 手札

Netkiller Perl 手札

Netkiller Amateur Radio 手札

Netkiller DevOps 手札

您可以使用iBook閱讀當前文件


目錄

  • 1. Apache mod_php / php-fpm
    • 1.1.1. Apache
    • 1.1.2. Nginx / lighttpd + fastcgi
    • 1.1. 使用者許可權
    • 1.2. web server 版本資訊
    • 1.3. php_flag / php_admin_flag
  • 2. php.ini
    • 2.2.1. chdir()函式安全演示
    • 2.1. Magic quotes
    • 2.2. 危險PHP函式
    • 2.3. 隱藏PHP版本資訊
    • 2.4. session名字可以洩露你的伺服器採用php技術
    • 2.5. 隱藏PHP出錯資訊
    • 2.6. open_basedir 防止操作web環境意外檔案目錄
  • 3. 開發於安全
    • 3.3.1. 禁止輸出除錯資訊
    • 3.3.2. 預防SQL注入攻擊
    • 3.3.3. SHELL 命令注入
    • 3.1. 徹底解決目錄於檔案的安全
    • 3.2. Session / Cookie安全
    • 3.3. 注入安全
  • 4. 執行效率
    • 4.1.1. mysql
    • 4.1. timeout
    • 4.2. 瀏覽器上傳檔案尺寸控制

1. Apache mod_php / php-fpm

目錄許可權安全

1.1. 使用者許可權

web server 啟動使用者不能於執行使用者為同一個使用者

web server 執行使用者與php程式不能為同一個使用者

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. 父程序 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 也需要作同樣的處理,上面程式碼僅供參考,未做過執行測試

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("iconv -f ".$_GET['from']." -t ".$_GET['from']." ".$_GET['file'])				

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			

設定不能過大,因為可以通過你的網站上傳功能,持續上傳實現攻擊。