PHP是一門復雜的語言,讓你長年承受內心的迂回,彎曲,拉伸和打擊。它自相矛盾有時候充滿bug。每個版本都有其獨特的特性,缺點以及怪癖,而且你很難追蹤什么版本有什么問題。不難想見為什么有時候它會招致那么多怨恨。
盡管如此,它是時下web中最流行的語言。由于它的悠久歷史,你能找到許多教程,關于一些基本事情的做法,如密碼哈希(一次性加密)和數據庫訪問。 問題在于從五篇教程里,關于某件事情你很有可能找到五種完全不同的方法。哪個方法才是“正確”的方法呢?其它的方法有瑕疵或者意想不到的問題嗎?確實很難 弄清楚,而且你會在網絡里到處點擊,以試圖確定正確的答案。
那也是為什么新的PHP程序員經常會為其丑陋,過時或不安全代碼受到批評的原因之一。如果第一次Google搜索結果是一篇傳授5年前方法的4年前的文章,他們就不能幫助這些有所改觀。
這篇文章試圖做這些工作。它嘗試將一系列基本的操作提示匯集起來,這些可以被認為是PHP中處理普遍的令人困惑的難題時的最佳實踐。如果PHP中一個低等級的任務具有多個和令人困惑的方法,它就屬于這里。
它是什么它是在面對PHP程序員可能會遇到的普通的低級任務時,由于PHP提供了 許多選擇而不容易了解到的,最佳途徑的建議指導。例如:對許許多多可能的PHP解決方案,連接到數據庫是一個普通的任務,但這些方案并不都是好的——因此,這個問題包含在這篇文章中。
它是一系列短小,引導式的解決方案。你應該行動起來將例子運行于基礎的配置環境,而且你應該自己研究從中找到對自己有用的東西。
它指出了我們所理解的最先進的PHP。但是,這也意味著如果你正使用較老版本的PHP,可能你就沒有實現這些方案所需要的一些特性。
這是一篇動態文件,隨著PHP的繼續演進,我將努力保持相應更新。
它不是什么?這篇文章不是PHP教程。你應該在別的地方學習基礎和語法。
它不是普通web應用問題的指南,比如cookie存儲,緩存,代碼風格,文檔等等。
它不是安全向導。當觸及一些安全相關的問題時,你要自己研究怎么樣才能對你的PHP應用加固。特別的,在著手實施以前,你應該仔細回顧一下這里給出的任何建議方案。你的代碼責任在于你。
它不是某種代碼風格,模式或者框架的擁護者。
它不是關于如何去做高等級任務,如用戶注冊,登錄系統等等諸如此類任務的特定方法的支持者。這篇文章完全是為低等級任務,是因為PHP的長久歷史,可能會令人困惑或者不甚清楚。
它不是終極意義的解決方案,也不是唯一的方案。下面描述的一些方法可能對你實際的情況不是最優的,而且有許多能達到同樣目的的不同的方法。特別的,高負載的web應用可能會從更多的針對這些問題的秘密方案中獲益。
我們使用的是哪個 PHP 版本?帶有 Suhosin-補丁的PHP 5.3.10-1ubuntu3.6, 安裝于 Ubuntu 12.04 LTS.PHP如同是網絡世界的百年老龜。它的外殼上刻有一個豐富的,令人費解的,粗糙的歷史。在一個共享主機的環境下,它的配置可能會限制你能做什么事情。
為了保留一絲明智,我們需要只專注于一個版本的PHP。截至2013年4月30,該版本是 帶有Suhosin補丁的PHP5.3.10-1ubuntu3.6 。如果你使用apt-get從一個Ubuntu12.04 LTS服務器來安裝PHP的話,你所得到的版本就是這個。換句話說,許多人在默認情況下已經很明智地使用了它。
您可能會發現本文這些解決方案能工作于不同或更舊版本的PHP。如果是這樣的話,就要由你來研究在這些舊版本中的細微錯誤或安全問題的影響了。
保存密碼使用 phpass 庫計算密碼的哈希值進行比較。用 phpass 0.3 進行的測試。
散列化是在把用戶密碼保存進數據庫之前對其進行保護的標準方法。許多常見的散列算法,如MD5,乃至SHA1,用于存儲密碼都是不安全的,因為黑客可以使用這些散列算法輕松破解密碼。
要散列化密碼最安全的方法是使用bcrypt算法。開源的phpass 庫用一個易于使用的類來提供這個功能。
例子:<?php // 包含phpass庫 require_once('phpass-0.3/PasswordHash.php'); // 初始化散列器為不可移植(這樣更安全) $hasher = new PasswordHash(8, false); // 計算密碼哈希值。$hashedPassword 將會是一長為60個字符的字符串. $hashedPassword = $hasher->HashPassword('my super cool password'); // 你現在可以安全地保存$hashedPassword到數據庫中! // 通過比較用戶輸入內容(產生的哈希值)和我們之前計算出的哈希值,來判斷用戶是否輸入了正確的密碼 $hasher->CheckPassword('the wrong password', $hashedPassword); // 返回假 $hasher->CheckPassword('my super cool password', $hashedPassword); // 返回真 ?>陷阱很多來源會建議你在計算密碼的哈希值之前先給密碼加點“作料”。這是個好主意,phpass已經利用HashPassword() 函數中的一部分代碼來為你給密碼加了作料。 這就意味著你并不需要自己再親自做這個了。進一步閱讀
phpass Why hashing passwords with md5 or sha is unsafe
How to safely store a password
連接到并查詢html' target='_blank'>MySQL數據庫使用 PDO和它預定義的語句功能.在PHP中有很多方法連接到一個MySQL數據庫。 PDO (PHP Data Objects) 是其中最新最健壯的。對于許多不同類型的數據庫,PDO都使用一致性的接口,采用面向對象的方式,并支持較新的數據庫的提供的更多特性。
您應該使用PDO預定義語句的功能,以幫助防止SQL注入攻擊。使用 bindValue() 函數確保你的SQL對于第一階的SQL注入攻擊是安全的(但這不是100%萬無一失的,參考 進一步閱讀 獲得更詳細說明)。在過去,這只能用一些“魔術引號”函數的復雜結合來實現。PDO使所有這些黏糊糊的東西變得不再必要了。
示例 :<?php try{ // Create a new connection. // You'll probably want to replace hostname with localhost in the first parameter. // The PDO options we pass do the following: // /PDO::ATTR_ERRMODE enables exceptions for errors. This is optional but can be handy. // /PDO::ATTR_PERSISTENT disables persistent connections, which can cause concurrency issues in certain cases. See "Gotchas". // /PDO::MYSQL_ATTR_INIT_COMMAND alerts the connection that we'll be passing UTF-8 data. This may not be required depending on your configuration, but it'll save you headaches down the road if you're trying to store Unicode strings in your database. See "Gotchas". $link = new /PDO( 'mysql:host=your-hostname;dbname=your-db', 'your-username', 'your-password', array( /PDO::ATTR_ERRMODE => /PDO::ERRMODE_EXCEPTION, /PDO::ATTR_PERSISTENT => false, /PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8mb4' ) ); $handle = $link->prepare('select Username from Users where UserId = ? or Username = ? limit ?'); // PHP bug: if you don't specify PDO::PARAM_INT, PDO may enclose the argument in quotes. This can mess up some MySQL queries that don't expect integers to be quoted. // See: https://bugs.php.net/bug.php?id=44639 // If you're not sure whether the value you're passing is an integer, use the is_int() function. $handle->bindValue(1, 100, PDO::PARAM_INT); $handle->bindValue(2, 'Bilbo Baggins'); $handle->bindValue(3, 5, PDO::PARAM_INT); $handle->execute(); // Using the fetchAll() method might be too resource-heavy if you're selecting a truly massive amount of rows. // If that's the case, you can use the fetch() method and loop through each result row one by one. // You can also return arrays and other things instead of objects. See the PDO documentation for details. $result = $handle->fetchAll(/PDO::FETCH_OBJ); foreach($result as $row){ print($row->Username); } } catch(/PDOException $ex){ print($ex->getMessage()); } ?>
阱
當綁定整型變量時,如果不傳遞PDO::PARAM_INT參數有事可能會導致PDO對數據加引號。這會 搞壞特定的MySQL查詢。查看該bug報告。未使用 `set names utf8mb4` 作為首個查詢,可能會導致Unicode數據錯誤地存儲進數據庫,這依賴于你的配置。如果你 絕對有把握你的Unicode編碼數據不會出問題,那你可以不管這個。啟用持久連接可能會導致怪異的并發相關的問題。這不是一個PHP的問題,而是一個應用層面 的問題。只要你仔細考慮了后果,持久連接一般會是安全的。查看Stack Overfilow這個問題。即使你使用了 `set names utf8mb4` ,你也得確認實際的數據庫表使用的是utf8mb4字符集!可以在單個execute()調用中執行多條SQL語句。只需使用分號分隔語句,但注意這個bug,在該文檔所針對的PHP版本中還沒修復。鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。
新聞熱點
疑難解答