大約2023年的當代PHP加密圖書館。
概要:
該庫是PHP鈉庫和PHP OpenSSL庫圍繞的包裝器。
該庫中的鈉代碼基於PHP Sodium_crypto_secretbox()函數的文檔中給出的示例。
此庫中的OPENSL代碼基於PHP OPENSSL_ENCRYPT()函數的文檔中給出的示例。
該庫旨在確保其加密數據與用於加密的秘密密鑰一樣安全。還採取了措施,以確保加密數據具有防篡改。
該庫無法解決鑰匙管理的嚴重問題。

這個庫是一項正在進行的工作。
我正在與朋友和同事分享此代碼,並儘可能多地批評和反饋。當我覺得這個庫盡我所能做到的好時,我將更新此狀態註釋。同時,打破變化幾乎是確定的,並且加密弱點是非常有可能的。如果您發現我認為我應該知道的東西,請告訴我!
我想和您清楚地說,這個庫很複雜,而且沒有太多用處。毫無疑問,尚未發現的微妙蟲子。我認為,此代碼基礎有可能成熟成一個可靠的可靠工具,但是我們需要進行整個過程。
請閱讀本節。
您的加密代碼有很多方法可以出錯。該圖書館是為了減少加密富槍的嘗試。希望它沒有介紹!
關於加密貨幣的第一件事是,您的數據僅與密鑰一樣安全。關於關鍵管理的知識比我在這裡告訴您的要多得多(無論如何我都不是專家),但是這裡有幾件事要考慮:
其他一些要注意的事情:
get_error()以確保其無錯誤表示錯誤)。當我學到的時候,這讓我感到驚訝的另一件事,儘管一旦知道,這很明顯,但它是在加密數據之前不應壓縮數據的。這並不總是一個問題,但是在某些情況下可能是,所以最好不要這樣做。
壓縮的問題是,如果攻擊者可以控制某些輸入數據,則可以包括特定值,然後如果輸出尺寸降低,則可以知道特定值中的其他輸入也包括。哎喲。
此代碼庫不是成熟或測試良好的,在使用它之前,您應該閱讀所有代碼,以確保它符合您的質量標準。如果您這樣做,我很高興收到您的來信。
如果您能想到每個人都應該知道的其他任何東西,請小心,請告訴我!
不想rtfm ..?我在這裡,寫所有這些東西...噓。至少閱讀上面列出的警告。
#!/bin/bash
set -euo pipefail;
mkdir -p kickass-demo/lib
cd kickass-demo
git clone https://github.com/jj5/kickass-crypto.git lib/kickass-crypto 2>/dev/null
php lib/kickass-crypto/bin/gen-demo-config.php > config.php
cat > demo.php <<'EOF'
<?php
require_once __DIR__ . '/lib/kickass-crypto/inc/sodium.php';
require_once __DIR__ . '/config.php';
$ciphertext = kickass_round_trip()->encrypt( 'secret text' );
$plaintext = kickass_round_trip()->decrypt( $ciphertext );
echo "the secret data is: $plaintext.n";
EOF
php demo.php
有關稍微詳細說明,也可以查看示例代碼。
或者,如果您想了解該庫的工作方式的底線,請在庫框架或其他代碼中讀取代碼。
哎呀,它開始得足夠簡單,但最終變得很複雜。
我想以相對安全的方式,保密和防篡改的方式將一些相對敏感的數據(用於樂觀並發控制的行版本編號(用於樂觀並發控制)之間。
我聽說OpenSSL庫在PHP中可用,因此我搜索了有關如何使用該庫的信息。我在openssl_encrypt()函數的PHP文檔中找到了示例代碼。
最初,我不清楚如何使用此代碼。特別是很難弄清楚如何處理這三個部分:身份驗證標籤,初始化向量和密碼文本。最終,我發現我可以加入它們。但是,如果我要這樣做,我需要對它們的長度和放置進行標準化,以便以後可以找回它們...
...然後我認為最好通過將其填充到某些邊界的固定長度來掩蓋我的實際數據大小,所以我做到了...
...然後我想支持需要某種序列化的豐富數據。最初,我使用的是php serialize()函數,但後來已更改為json_encode()。
示例代碼沒有任何關於如何以支持方式旋轉鍵的任何信息。因此,我想出了該庫支持的兩個用例,採用不同的方法來管理往返和精加工方案的鑰匙管理方法。該庫使您可以在新的鑰匙中旋轉,同時保持對舊密鑰的支持,因為您可能不會做。
然後,我以仔細的方法進行了層次,以進行異常處理和錯誤報告,某些單元測試和驗證,時機攻擊緩解,服務定位器,使用情況演示,數據大小限制,密碼初始化,密鑰生成腳本,遙測等。
基本上,整個庫只是我必須做的一切,以便我實際上可以使用內置的PHP OpenSSL庫實現。
然後...人們開始告訴我有關鈉庫的信息,並建議我使用它。由於我已經為密鑰管理和輸入序列化以及消息格式化和編碼做了很多工作,因此,我認為我可以重複使用所有這些,並在鈉周圍提供包裝。這就是我所做的。
現在,如果您使用此庫,則可以決定是要使用鈉實現還是OPENSL實現。因為這兩個實現可以愉快地共存,因此,如果您需要的話,也可以編寫代碼從一個移動到另一個。實現從未共享關鍵配置或數據格式,它們是完全分開的。 (也就是說,切換加密算法並不是完全微不足道的,您可能必須離線遷移所有數據,如果您不能這樣做,那麼您將有一個糟糕的時間,因此如果不確定您不確定要開始使用鈉並堅持下去,請不要打算切換算法。
我認為這個庫不滾動我自己的加密貨幣,而是我認為這是弄清楚如何實際使用鈉和openssl的。如果我犯了任何明顯的錯誤,我真的很高興聽到有關它的消息。
假設我記得不時更新它,這裡有一個演示系統:
演示設施僅顯示瞭如何使用HTML和HTTP在客戶端和服務器之間進行加密數據。
如果您想自己託管,則該庫中的演示代碼可在此庫中可用。
假設我記得不時更新它們,則PHP文檔在這裡:
如上所述,您可以使用這樣的命令查看Git中的代碼:
git clone https://github.com/jj5/kickass-crypto.git
此代碼未發行,沒有穩定的版本。
如果要將客戶庫包括在應用程序中使用的客戶庫,包括Inc/Sodium.php或Inc/openssl.php文件,它將負責加載其他所有內容;使用這樣的東西:
require_once __DIR__ . '/lib/kickass-crypto/inc/sodium.php';
加載此庫後,您通常將通過kickass_round_trip()或kickass_at_rest()服務定位器訪問,如下所示:
$ciphertext = kickass_round_trip()->encrypt( 'secret text' );
$plaintext = kickass_round_trip()->decrypt( $ciphertext );
echo "the secret data is: $plaintext.n";
使事情變得簡單,花了很多工作!
如果要託管演示代碼,則需要在src/ emo/中託管文件,並在項目基礎目錄中包含有效的config.php文件(這是包括此readme.md文件的目錄)。 config.php演示CONFIG_SODIUM_SECRET_CURR
php bin/gen-key.php
或者,您可以使用以下方式生成整個演示config.php文件
php bin/gen-demo-config.php > config.php
以下是有關軟件文件和代碼行的一些註釋。
Total Number of Files = 128
Total Number of Source Code Files = 128
| 目錄 | 文件 | 通過語言 |
|---|---|---|
| 測試 | 63 | PHP = 59,SH = 4 |
| 代碼 | 35 | PHP = 35 |
| 垃圾桶 | 22 | PHP = 13,SH = 9 |
| Inc | 7 | PHP = 7 |
| 演示 | 1 | PHP = 1 |
| 語言 | 文件 | 百分比 |
|---|---|---|
| php | 115 | (89.84%) |
| sh | 13 | (10.16%) |
Total Physical Source Lines of Code (SLOC) = 9,210
Development Effort Estimate, Person-Years (Person-Months) = 2.06 (24.70)
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
Schedule Estimate, Years (Months) = 0.70 (8.46)
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
Estimated Average Number of Developers (Effort/Schedule) = 2.92
Total Estimated Cost to Develop = $ 278,044
(average salary = $56,286/year, overhead = 2.40).
| 目錄 | sloc | 通過語言 |
|---|---|---|
| 代碼 | 5,136 | PHP = 5136 |
| 測試 | 3,363 | PHP = 3193,SH = 170 |
| 垃圾桶 | 603 | PHP = 423,SH = 180 |
| 演示 | 71 | PHP = 71 |
| Inc | 37 | PHP = 37 |
| 語言 | sloc | 百分比 |
|---|---|---|
| php | 8,860 | (96.20%) |
| sh | 350 | (3.80%) |
該代碼應適用於PHP 7.4或更高的。如果您嘗試在舊版本的PHP上運行此代碼,它將嘗試記錄錯誤消息,然後退出您的過程。
該代碼將檢查以確保其在64位平台上運行。如果不是,它將抱怨和退出。
如果加載鈉模塊,庫將確保鈉庫實際上可用。如果不是這樣,該過程將抱怨和退出。
如果加載OPENSL模塊,庫將確保實際上可用的OpenSSL庫。如果不是這樣,該過程將抱怨和退出。
我相信該代碼應該在任何操作系統上運行,但是我只在Linux上對其進行了測試。如果您在MacOS或Windows上取得了成功,我很高興聽到它。
外殼腳本是為bash編寫的。如果您沒有bash,則可能需要端口。
該代碼支持兩個特定用例:
對於每個用例,鍵分別管理和不同。
下面記錄瞭如何支持每個用例的詳細信息。
將此庫進行精加工加密通常是更大的風險和更大的承諾,而不是僅僅用於往返加密。如果您丟失了往返加密密鑰或被迫緊急旋轉它們,那可能會比您的當下鍵發生類似的事情要小。
開發該庫的主要用例是支持幾千字節的數據,其中包含輕度敏感但沒有任務的關鍵行版本編號,以進行樂觀並發控制。與替代方案(不加密或篡改樂觀的並發控制數據)相比,該庫的使用是一種改進。我不確定,它是否真的適合其他應用程序是一個懸而未決的問題。當然,如果該庫不提供所需的安全級別,則不應使用該庫。
在配置文件中提名秘密的首选和支持的方法是使用PHP Define()函數作為常數。使用類/實例字段或全局變量的問題在於,這些值可以很容易地洩漏到調試和記錄代碼中,對於常數而言,這不太可能(儘管仍然可能)。同樣,如果您需要緩存全局/靜態數據(例如從配置文件中讀取),則最好的方法是使用函數中的局部靜態變量(如果可能的話)使用實例字段,類字段或全球範圍,則可以更輕鬆地導致秘密洩漏。
為了給您一個示例,讓我們創建一個稱為double-define.php的測試文件:
<?php
define( 'TEST', 123 );
define( 'TEST', 456 );
然後,當我們運行代碼時,會發生類似的事情:
$ php double-define.php
PHP Warning: Constant TEST already defined in ./double-define.php on line 4
PHP Stack trace:
PHP 1. {main}() ./double-define.php:0
PHP 2. define($constant_name = 'TEST', $value = 456) ./double-define.php:4
如果該常數值包含您的秘密鍵,那麼您的一天就非常糟糕。
定義PHP中常數的最安全方法是檢查它尚未首先定義,因為嘗試定義已經定義的常數將導致錯誤。如果您找到已經定義的常數,則可以使用錯誤消息中止(如果您沒有提供太多的細節,因為公共網絡可能會看到它),或者只是保留現有的值,並且不要嘗試重新定義它。 bin/gen-demo-config.php配置文件生成器採用第一種方法,並在檢測到重複時調用php die()函數。您可以通過兩次包括生成的config.php文件來查看會發生什麼,as:
require __DIR__ . '/config.php';
require __DIR__ . '/config.php';
您可以找到一個示例,說明如果將config.php包含在config-die.php中。
因此,與大多數PHP源文件一樣,在包含config.php文件時最好使用require_once :
require_once __DIR__ . '/config.php';
當我命名秘密的東西時,我確保名稱包含字符串“通行證”(如“密碼”,“ passwd”和“ passphrase”,甚至在某種程度上,“ passport”,“ passport”)或“秘密”。在我的通用日誌記錄設施中(本庫中不包含),我在記錄診斷數據之前擦洗並用匹配的名稱(不敏感)的名稱。我鼓勵您採用這種做法。
在此庫中,如果變量或常數可能包含敏感數據,它將以“通行證”或“秘密”為名命名。
不要將敏感數據寫入日誌中。
請以敏感變量,字段或常數的名義將“通過”或“秘密”放置。
在這裡,我解釋了這些類似的聲音術語在此庫的背景下實際上是什麼意思。
如果使用默認模塊,則數據格式為OPENSL模塊的“ KA0”,或者是鈉模塊的“ KAS0”。
如果您繼承了基本框架並定義了自己的加密模塊,則基於OpenSL實現的模塊的默認數據格式為“ XKA0”,或基於sodium實現的模塊的模塊或“ XKAS0”,否則您的實現了您的do_get_const_data_format() the do not do do do not do note note n.官方實施。
您需要將正確的模塊用於數據格式,以便成功解密密文。
數據編碼是JSON,PHP序列化或文本。假設您具有適合數據格式的正確模塊(上圖),並且在下面討論的警告中,無論使用該數據編碼如何,您都可以解密任何內容。將使用配置的數據編碼進行加密,請參見Config_encryption_data_encoding,它可以是:
請注意,除非您還定義了config_encryption_phps_enable,否則您將無法使用PHP編碼,這是因為PHP避難所可能是不安全的,因此默認情況下它被禁用。老實說,這有點手浪。我剛剛聽到有傳言稱PHP unserialize()可能會導致代碼注入,但我不確定這是真的還是它的含義。我實施了PHP序列化和避免序列化,並進行了一些測試,但我不知道它是否真的沒有安全感。我很確定JSON和文本數據編碼應該是安全的。
除了從KickassCrypto繼承和覆蓋特定功能外,還可以通過配置常數獲得很多配置。搜索CONFIG_SODIUM以查找可用於鈉和CONFIG_OPENSSL的可用內容,以找到可用於OpenSSL的東西。
請注意,目前此代碼直接在config.php文件中配置。
將來, config.php文件將包含單獨管理的配置文件,為:
這些文件將有用於自動旋轉和配置密鑰的管理腳本。
經驗豐富的Linux用戶知道您不直接編輯/etc/sudoers ,而是使用visudo進行編輯,以便您可以驗證您沒有意外地引入語法錯誤並使用系統。
我打算提供類似的腳本來編輯和管理config.php和其他配置文件。因此,這些更新。同時...請非常小心。
您應該非常小心的一件事是不要在使用“ .php”文件擴展名的PHP文件中管理密鑰。如果您將鍵放入“ .ini”文件或類似文件中,則它們很可能會由Web服務器用作純文本。所以不要那樣做。另外,請注意不要將語法錯誤引入您的配置文件或生產中運行的其他源文件,因為詳細信息可能會隨著潛在的錯誤消息而洩漏。
如前一節所述,支持命名配置常數提供了相當多的可配置性。
除了配置常數外,如果您從KickassCrypto基類繼承並覆蓋其方法,您還可以做很多事情。
作為配置常數的替代方案(只能每個過程只能定義一次,然後無法更改),對於配置選項,有實例方法為get_config_...() get_const_...()用於常量評估。最重要的常數和配置選項是通過這些登錄器間接讀取的,因此您應該能夠可靠地覆蓋它們。
大多數對PHP內置功能的調用都是由薄包裝器通過KickassCrypto上的受保護功能完成的。這些定義在KICKASS_WRAPPER_PHP特徵中。這種間接允許某些PHP函數調用被攔截並可能修改。這樣做的主要目的是支持單位測試期間的故障注入,但是您可以用於其他目的來更改實施細節。
在KickassCrypto中被認為敏感的事物被定義為私人或最終。如果它不是私人的,並且不是最終的,那是公平的比賽(除非我犯了一個錯誤)。特別是以do_開頭的實例方法是專門為實施者替換或攔截的。
該庫提供了兩個服務定位器函數,該功能管理每個加密庫庫的實例,它們是:
kickass_round_trip()kickass_at_rest()您可以通過調用函數並將新實例傳遞為唯一參數,替換服務定位器函數提供的服務實例,例如:
class MyKickassCrypto extends KickassCryptoKickassCrypto {
protected function do_is_valid_config( &$problem = null ) { return TODO; }
protected function do_get_passphrase_list() { return TODO; }
// ... other function overrides ...
}
kickass_round_trip( new MyKickassCrypto );
理想情況下,此庫將滿足您的要求(或具有某些配置),並且默認情況下您無需替換服務定位者提供的實例。
如果服務定位器尚未有實例,則在第一個呼叫服務定位器的第一個調用中,將為您創建一個新的默認實例。默認實現是鈉模塊還是OPENSL模塊取決於您包括inc/sodium.php和inc/openssl.php文件的順序;如果您將整個庫與inc/library.php一起包含,則鈉模塊將具有優先級。
無論您是為鈉模塊加載服務定位器還是OPENSSL模塊,您都可以通過以新實例為參數來調用服務定位器來覆蓋默認實例。
加密過程大致:
請注意,鈉庫使用非CE代替初始化向量(相似的效果),鈉可以處理其自己的身份驗證標籤。
當此庫編碼其密文時,它包含用於鈉實現的“ Kas0/”的數據形式前綴,以及用於OpenSSL實現的“ KA0/”。
Data-Format前綴中的零(“ 0”)用於零版本,這旨在暗示接口不穩定並且可能會更改。
該庫的未來版本可能會為穩定的數據格式實現新的數據形式前綴。
當此庫解碼其密文時,它會驗證數據格式前綴。目前僅支持“ KAS0/”或“ KA0/”。
上述版本零數據格式目前表示以下內容:
在數據編碼(默認情況下,在下一節中討論)進行填充後,將完成數據長度。在加密之前,郵件的格式是這樣:
$message = $encoded_data_length . '|json|' . $encoded_data . $this->get_padding( $pad_length );
JSON數據長度格式為8個字符十六進制值。 8個字符的大小是恆定的,並且根據JSON數據長度的大小而變化。
填充的原因是要掩蓋實際的數據大小。填充最多可在4 KIB邊界(2 12個字節)中完成,我們稱之為塊。塊大小是可配置的,默認值可能會在將來發生變化。
然後,如果我們要使用鈉加密,則消息將使用sodium_crypto_secretbox()加密,然後將nonce和Ciphertext加入在一起,如下:
$nonce . $ciphertext
否則,如果我們使用openssl加密,則消息將使用AES-256-GCM進行加密,並且初始化向量,密文和身份驗證標籤被串聯在一起,如下所示:
$iv . $ciphertext . $tag
然後,所有內容都是用php base64_encode()函數編碼的base64,並添加了data-Format前綴。
對於這樣做的鈉:
"KAS0/" . base64_encode( $nonce . $ciphertext )
對於這樣做的openssl:
"KA0/" . base64_encode( $iv . $ciphertext . $tag )
解密過程希望找到“ KAS0”數據格式的24個字節NONCE和CIPHERTEXT,以及12個字節初始化向量,Ciphertext和KA0數據格式的16個字節身份驗證標籤。
在解密密碼後,圖書館希望找到JSON數據的大小為ASCII字符串,代表8個字符的六角形編碼值,其次是單個管道字符,然後是一個四個字符數據編碼指示燈('json'或'phps'),然後是單個管道字符,然後是單個管道字符,然後是JSON(或php serialized serialized數據),然後是PADDADIAD DADA),然後使用PADDing。然後,庫可以從其填充物中提取JSON/序列化數據,並照顧其餘的解碼。
使用PHP JSON_ENCODE()函數將加密輸入數據編碼為JSON。最初,該庫使用php serialize()函數,但顯然可以導致某些代碼執行方案(我不確定詳細信息),因此決定JSON編碼更安全。因此,現在,我們使用JSON編碼。
將JSON用作數據編碼格式的使用對我們可以支持的值有一些較小的含義。特別是我們無法編碼以後可以將其解碼為對象實例的對象實例(如果對象實現了可序列化的界面,則可以將它們序列化為數據,但是僅將其解碼回到PHP數組,而不是它們來自的PHP對象);無法表示一些奇怪的浮點值(即nan,pos inf,neg Info和neg Zero);二進製字符串在JSON中不能表示。
默認情況下,這些選項用於JSON編碼:
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
但是這些選項不會影響實現解碼JSON的能力。實現可以通過覆蓋data_encode()和data_decode()方法來微調JSON編碼和解碼。另外,您可以使用CONFIG_ENCRYPTION_JSON_ENCODE_OPTIONS和CONFIG_ENCRYPTION_JSON_DECODE_OPTIONS常數提名config.php文件中的JSON編碼和解碼選項。
define( 'CONFIG_ENCRYPTION_JSON_ENCODE_OPTIONS', JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
define( 'CONFIG_ENCRYPTION_JSON_ENCODE_OPTIONS', JSON_THROW_ON_ERROR );
無論是否指定JSON_THROW_ON_ERROR ,該庫都應工作。
如果您在JSON編碼選項中指定JSON_PARTIAL_OUTPUT_ON_ERROR您的數據可能會默默無效,因此請自行冒險。也許違反直覺,我發現啟用JSON_PARTIAL_OUTPUT_ON_ERROR是最糟糕的策略,因為至少在這種情況下,您會得到一些東西。如果您不啟用JSON_PARTIAL_OUTPUT_ON_ERROR則如果無法編碼輸入的任何部分(例如,當您沒有有效編碼的二進製字符串,例如UTF-8),則將刪除整個數據。使用JSON_PARTIAL_OUTPUT_ON_ERROR僅省略了無法實現的部分。目前, JSON_PARTIAL_OUTPUT_ON_ERROR尚未自動指定,但這是我將來可能會重新訪問的。
如果您使用這些JSON編碼/解碼選項中的任何一種,您很可能會遇到糟糕的時光:
JSON_NUMERIC_CHECKJSON_INVALID_UTF8_IGNOREJSON_INVALID_UTF8_SUBSTITUTE 當此庫加密數據時,它將其輸出填充到可配置的塊尺寸。
塊大小的配置常量為CONFIG_ENCRYPTION_CHUNK_SIZE 。
默認塊尺寸為4,096(2 12 )。
如果您想將塊大小提高到8,192,則可以在您的config.php文件中執行此操作:
define( 'CONFIG_ENCRYPTION_CHUNK_SIZE', 8912 );
您可以更改定義的塊大小,它將開始應用於新數據,並且舊數據以不同的塊大小加密仍然可以解密。
只要觀察到數據尺寸限制(下一個將討論這些限制),該庫可以加密可以通過PHP編碼為JSON的任何內容。
這包括各種事情,例如:
JSON無法支持的事情:
請注意,布爾值false不能加密。這不是因為我們無法對其進行加密,而是因為當解密失敗時我們將其返回。因此,我們拒絕加密false,以使其與解密時的錯誤混淆。
如果您需要對布爾值進行加密,則假設將其放在數組中,這樣:
$input = [ 'value' => false ];
或像JSON一樣編碼:這樣:
$input = json_encode( false );
如果您做這兩個事情中的任何一件事,您都可以加密價值。
值得指出的是,在PHP中,“字符串”本質上是字節陣列,這意味著它們可以包含“二進制”數據。但是,這種二進制數據不能表示為JSON。如果您需要處理二進制數據,則最好的方法可能是用base64_encode()()將其編碼為base64,或使用bin2hex()將其編碼為base64,然後對其進行加密。
將來,可能會添加並不總是將JSON編碼的數據工作的能力添加到此庫中。讓我知道這是否是您願意擁有的功能。
注意:現在使用PHP序列化而不是JSON編碼是一個選項;需要更新此文檔,以解釋其工作原理以及如何使用它。 PHP序列化的優點是,它比JSON支持更多的數據類型和格式。
將數據編碼為JSON後,它僅限於可配置的最大長度。
最大JSON編碼長度的配置常量為CONFIG_ENCRYPTION_DATA_LENGTH_MAX 。
默認數據編碼限制為67,108,864(2^ 26 )字節,大約為67 MB或64 MIB。
如果您需要使其更大或更小,則可以配置此數據編碼限制。請注意,如果您使極限太大,則最終會遇到內存問題,並且您的過程可能會終止。
如果要降低數據編碼限制,則可以在您的config.php文件中執行此操作:
define( 'CONFIG_ENCRYPTION_DATA_LENGTH_MAX', pow( 2, 25 ) );
該庫不會壓縮輸入數據,因為壓縮可以引入加密弱點,例如在犯罪SSL/TLS攻擊中。
問題是,如果攻擊者可以修改一些純文本,他們可以確定他們輸入的數據是否存在於純文本的其他部分,因為如果他們提出值並且結果較小,那是因為它存在於他們不知道的純文本部分中,但是現在!
非常重要的是,不要壓縮攻擊者可以提供其他秘密數據的數據。最好是完全不壓縮。
如果在加密或解密期間遇到誤差,則引入1毫秒(1 ms)至10秒(10 s)之間的延遲。這是針對潛在的計時攻擊的緩解措施。有關討論,請參見S2N和Lucky 13。
請注意,避免正時攻擊很難。 VPS主機上的惡意嘉賓(或聽取服務器粉絲的惡意人員!?)可以弄清楚您的過程正在睡覺而不是實際工作。
該庫包括一個稱為delay()的方法,此方法在錯誤的第一個實例上自動稱為。 delay()方法執行錫上所說的內容:它將隨機延遲注入過程中。 delay()方法是公開的,如果您覺得需要,您可以自己調用。每個時間delay()都會稱呼它會在1毫秒至10秒之間的隨機時間內睡覺。
使用此庫的程序員有機會覆蓋do_delay()方法並提供自己的延遲邏輯。
如果do_delay()覆蓋會拋出異常,將處理並註入緊急延遲。
如果您執行Override do_delay() ,但實際上不會至少延遲最小持續時間(即1 ms),則該庫將注入緊急延遲。
允許實現者自定義延遲邏輯的主要原因是,單位測試可以延遲最短的時間。通常,應該沒有任何理由干預延遲邏輯,這樣做可能不太安全。
當創建以下一個實例之一時,將驗證配置設置。
KickassSodiumRoundTripKickassSodiumAtRestKickassOpenSSLRoundTripKickassOpenSSLAtRest如果配置設置無效,則構造函數將引發異常。如果構造函數成功,則稍後再加密和解密也應(通常)成功。如果有任何配置問題,這意味著加密或解密將無法成功(例如未提供的秘密密鑰),則應拋出。
該庫定義了自己的異常類,稱為KickassException 。這與正常例外一樣起作用,除了它添加了一個方法getData()該方法可以返回與異常相關的任何數據。 KickassException並不總是具有關聯的數據。
當然,並非所有問題都可以事先診斷出來。如果庫在成功結構後無法完成加密或解密操作,它將通過返回布爾值false來指示錯誤。返回錯誤的錯誤是PHP成語,我們使用此習慣,而不是提出例外來限制在呼叫堆棧上加密秘密或密碼時拋出異常的可能性。
提出異常時,在呼叫堆棧上擁有敏感數據的問題在於,可以將數據複製到堆棧跟踪中,這些數據可以保存,序列化,向用戶顯示,已記錄等。我們不希望這樣做,因此我們不願意不努力提出異常,而敏感數據可能會在堆棧中。
如果錯誤返回錯誤,則將將一個或多個錯誤消息添加到內部錯誤列表中。呼叫者可以通過調用方法get_error獲取最新錯誤。如果您想要錯誤的完整列表,請致電get_error_list 。
如果OpenSSL庫函數登記了任何錯誤(OpenSSL模塊調用以進行繁重的舉重),則如果您調用get_openssl_error() ,則可以使用最後一個錯誤。您可以通過調用方法clear_error()來清除當前錯誤列表(和OPENSL錯誤消息)。
對於PHP鈉實現,我們使用的功能是Sodium_crypto_secretbox()。那就是XSALSA20流密碼加密,具有Poly1305 MAC身份驗證和完整性檢查。
對於PHP OPENSL實現,我們使用的密碼套件是AES-256-GCM。這是通過GALOIS/Counter Mode身份驗證和完整性檢查的高級加密標準加密。
秘密鍵是您在config.php文件中保留的秘密值,該秘密值將處理並將其轉換為密碼,供鈉和OpenSSL庫功能使用。該庫會自動處理將秘密鍵轉換為密碼,因此您唯一的責任是提名秘密密鑰。
秘密密鑰根據用例和模塊而變化。有兩個默認用例,稱為往返和精加工。
AES-256-GCM中的“ 256”意味著該密碼套件的期望為256位(32個字節)密碼。鈉庫sodium_crypto_secretbox()功能還期望256位(32個字節)密碼。
我們使用哈希算法將我們的秘密鍵轉換為256位二進製字符串,這些串聯可以用作密碼算法的密碼。
所需的最小密鑰長度為88個字節。當這些庫生成這些鍵時,它們將使用66個字節的隨機數據生成,然後將其編碼為基礎64。
我們使用的秘密鍵哈希算法是SHA512/256。這是秘密鑰匙的SHA512哈希的價值256位。將此哈希代碼與來自88個字節基本64編碼輸入的原始二進制輸出一起應用時,您應該獲得約32個字節的隨機性。
鈉庫預計將提供一個非CE,以代替初始化向量。
要了解Nonce減輕什麼問題,請考慮一下如果您要加密人們的生日會發生什麼。如果您有兩個用戶擁有同一個生日,並且使用相同的鑰匙對那些生日進行了加密,那麼兩個用戶的生日都將具有相同的密文。發生這種情況時,即使您可能不確切知道何時是生日,您也可以看到誰的生日。初始化矢量避免了這個潛在的問題。
我們的AES-256-GCM密碼套件支持我們提供的12個字節初始化向量。初始化向量確保即使您使用相同的密碼加密相同的值,結果密碼仍然會有所不同。
這減輕了與鈉鈉相同的問題。
我們的AES-256-GCM密碼套件支持16個字節身份驗證標籤的驗證。
AES-256-GCM中的“ GCM”代表GALOIS/計數器模式。 GCM是與基於哈希的消息身份驗證代碼(HMAC)相似的消息身份驗證代碼(MAC)。 GCM身份驗證標籤的目標是使您的加密數據篡改。
鈉庫還使用身份驗證標籤,但它本身就可以解決這個問題,這不是我們必須管理的。當您在鈉模塊中parse_binary()時,標籤設置為false。
該庫需要用於各種目的的安全隨機數據輸入:
在PHP中生成合適的隨機數據有兩個主要選項,它們是:
兩者都是合理的選擇,但此庫使用Random_bytes()。
如果random_bytes()函數無法生成安全的隨機數據,則會引發異常。有關詳細信息,請參見文檔。
我們還使用PHP Random_Int()函數來生成隨機延遲,以用於緩解計時攻擊。
往返用例適用於您要以隱藏的html表單<輸入>元素將數據發送給客戶端,並稍後將其發布回。
使用兩種類型的秘密鍵支持此用例。
第一個鍵稱為當前密鑰,是必需的。
第二個鍵稱為上一個鍵,它是可選的。
數據始終使用當前密鑰加密。
數據用當前密鑰解密,如果失敗,則與先前的密鑰解密。如果使用上一個鍵的解密也失敗,則無法解密數據,在這種情況下,將返回布爾值false以發出錯誤。
當您旋轉往返秘密密鑰時,您會將當前密鑰複製到上一個鍵,更換舊的先前鍵,然後生成新的當前鍵。
鈉模塊的當前密鑰的配置設置為: CONFIG_SODIUM_SECRET_CURR 。
OpenSSL模塊的當前密鑰的配置設置為: CONFIG_OPENSSL_SECRET_CURR 。
鈉模塊上一個鍵的配置設置為: CONFIG_SODIUM_SECRET_PREV 。
OpenSSL模塊的上一個鍵的配置設置為: CONFIG_OPENSSL_SECRET_PREV 。
加密往返數據:
$ciphertext = kickass_round_trip()->encrypt( 'secret data' );
解密往返數據:
$plaintext = kickass_round_trip()->decrypt( $ciphertext );
如果要在數據庫中或其他地方加密數據以存儲何時,則應使用。
該用例由任意長的秘密鍵列表支持。
該列表必須至少包括一個值。列表中的第一個值用於加密。為了解密,要在列表中的每個秘密密鑰都嘗試,直到找到一個有效的鍵為止。如果沒有工作,則無法解密數據,並且返回布爾值false以發出錯誤。
當您旋轉固定密鑰時,您會添加一個新的主密鑰作為列表中的第一項。您需要至少保留一個額外的鑰匙,並且除了適合您的目的外,還可以保留盡可能多的鑰匙。
旋轉出色的秘密密鑰後,您應該考慮重新加密所有現有的精加工數據,以便它使用最新的密鑰。重新授予您的優質數據後,您可以刪除較舊的密鑰。
鈉模塊的鍵列表的配置設置為: CONFIG_SODIUM_SECRET_LIST 。
OpenSSL模塊的鍵列表的配置設置為: CONFIG_OPENSSL_SECRET_LIST 。
請注意:如果您還原數據庫的舊備份,則還需要還原舊鍵。
請非常小心,不要失去您的精加工秘密鑰匙。如果您丟失了這些密鑰,則將無法解密您的優質數據。
要加密精確數據:
$ciphertext = kickass_at_rest()->encrypt( 'secret data' );
解密測試數據:
$plaintext = kickass_at_rest()->decrypt( $ciphertext );
已經註意到,主要管理是網絡安全中最難的部分。該圖書館無法為您提供幫助。
您的加密數據僅與秘密密鑰一樣安全。
如果有人獲得您的秘密密鑰的副本,他們將能夠解密您的數據。
如果有人立即獲取您的加密數據副本,則可以將其保留並解密,如果他們將來獲得了您的秘密鍵的副本。因此,您的鑰匙現在不一定只是秘密,但是它們必須一直是秘密。
如果您失去了秘密鍵,則無法解密數據。
您的往返數據可能不如您的精確數據重要。
確保您擁有基本往返或精加工數據的秘密鍵的備份是一個很好的主意。您可以考慮:
進行密鑰管理時,重要的是要確保以安全的方式編輯您的配置文件。配置文件中的語法錯誤可能會導致一個秘密鍵暴露於公共網絡。如果發生這種情況,您將不得不立即旋轉所有鑰匙,然後銷毀舊的損壞的鑰匙,即使那樣,也可能為時已晚。
在緊急情況下,準備立即以自動和測試的方式進行鑰匙旋轉是個好主意。
當您旋轉往返鑰匙時,您需要確保它們在所有網絡服務器上都同步。
我打算實施一些設施來幫助密鑰部署和配置文件編輯,但這些設施尚未完成。
該庫支持加密的數據,並加密的數據往返。另一個考慮因素是運動中的數據。運動中的數據有時也稱為運輸中的數據。
當數據在您的Web服務器和數據庫服務器之間移動時,數據就在運動。當數據在您的Web服務器和訪問它們的客戶端之間移動時,數據也會在運動。您應該使用非對稱加密進行運動。連接到數據庫時,請使用SSL加密支持,並為您的Web客戶端使用HTTPS。
該庫是服務器端組件。我們不支持Web瀏覽器中的數據客戶端加密。
該庫收集了一些基本的遙測:
致電KickassCrypto::GetTelemetry()獲取遙測和KickassCrypto::ReportTelemetry()進行報告。
單位測試在SRC/ Test/ Directory中,依次編號。
如您所見,bin/dev/中有一些測試跑者。閱讀有關血腥詳細信息的腳本,但簡而言之:
也有一些愚蠢的測試,但我們不會談論這些測試。他們通常不是運行。他們很傻。
如果要添加正常/快速測試,將單元測試目錄創建為src/test/test-XXX ,然後添加fast.php或fast.sh如果您同時創建兩個,則fast.sh將具有優先級,而fast.php將被忽略。
如果要添加慢速測試,將單元測試目錄創建為src/test/test-XXX ,請添加slow.php或slow.sh如果您同時創建兩個,則slow.sh將具有優先級,而slow.php將被忽略。
通常,如果您的設備測試需要多個過程才能工作,通常只需要提供Shell腳本。當您需要測試不同的常數定義時,這可能會發生。由於無法重新定義PHP中的常數,如果您想以不同的值運行,則必須重新啟動過程。
有關如何使用簡單單元測試主機的示例,請參見現有的單元測試。
我聽說並使用了phpunit(儘管很長一段時間沒有使用它)。我在這個項目中不使用它,因為我覺得我不需要它或增加了很多價值。測試是一個Shell腳本,如果缺少它們是PHP腳本。如果我需要做出斷言,請稱為essert()。簡單的。
以下是有關此庫中各種習語和方法的一些註釋。
在代碼中,您會看到這樣的東西:
protected final function is_valid_settings( int $setting_a, string $setting_b ) : bool {
if ( strlen( $setting_b ) > 20 ) { return false; }
return $this->do_is_valid_settings( $setting_a, $setting_b );
}
protected function do_is_valid_settings( $setting_a, $setting_b ) {
if ( $setting_a < 100 ) { return false; }
if ( strlen( $setting_b ) > 10 ) { return false; }
return 1;
}
關於這個習語有幾件事要注意。
在談論上述代碼時,我們將將第一個函數is_valid_settings()稱為“最終包裝器”(或有時為“主函數”),我們將第二個函數do_is_valid_settings()稱為“默認實現”。
要注意的第一件事是,最終的包裝器is_valid_settings()被聲明最終,因此無法被實現所覆蓋。要注意的第二件事是,最終包裝器在其接口上聲明了數據類型。
相比之下,默認實現do_is_valid_settings()未標記為最終,並且不會在其接口上聲明類型。
這是Postel定律的一個例子,也稱為魯棒性原則。最終的包裝器在接受的內容上是自由的,例如默認實施中的返回值一( 1 );保守的工作,例如總是返回正確鍵入的布爾值,並始終為默認實現提供正確類型的值。
不需要寫出並聲明默認實現界面上的類型也使實現和調試更加容易,因為編寫代碼較少。 (我也發現返回類型的語法有點醜陋,並且有可能在可能的情況下避免這種語法,但這是一個瑣碎的事情。)
通常,此代碼的用戶只會調用主函數is_valid_settings() ,任何實現新代碼的人只需要覆蓋do_is_valid_settings() 。
通常,您應該始終根據本習慣將任何非最終方法(除私人方法除外)包裹,以便您可以像他們想做的那樣具有呼叫者覆蓋功能,但可以保留保持標準的能力。
如果您要重構一種使其公開或受保護的私人方法,請務必引入相關的最終包裝器。
最後一件事:如果您的組件具有公共功能,則可能是最終的包裝器,只是延期默認實現。
默認實現幾乎應該始終受到保護,當然不是公共的,如果您還沒有準備好揭示實施情況,也許應該私下。
在最終方法的接口上具有類型is_valid_settings()賦予三個主要優點。
首先是界面強烈鍵入,這意味著您的呼叫者可以知道期望什麼,並且PHP可以為我們修復一些較小的細節。
這種方法的第二個優點是我們的最終包裝器功能被標記為最終功能。這意味著實施者可以在圖書館內維護特定的標準,並可以確保這些標準沒有被偶然或其他方式闡明。
擁有所依賴為最終的代碼可以幫助您推理組件的可能狀態。在上面給出的示例中,要求$setting_b小於或等於20字節的長度是不能通過實現更改的要求;實現只能使要求更強大,例如在示例中給出的默認實現中完成的,其中最大長度進一步縮小到10個字節。
鍵入接口的另一個優點是它提供了可以自動添加到文檔中的額外信息。鍵入的接口將意圖傳達給PHP運行時,也可以與其他程序員讀取,使用或維護代碼。
在默認實現do_is_valid_settings()接口上沒有類型的類型賦予了四個主要優點。
首先是,由於您不必擔心寫出類型,因此更容易輸入和維護壓倒功能。
同樣,將來, is_valid_settings()可能會聲明新的接口並更改其類型。如果發生這種情況,它可以維持對新舊do_is_valid_settings()實現的支持,而無需實現者需要更新其代碼。
do_is_valid_settings()函數的未型接口的第三個優點是它允許注入“不可能”的值。這些值將永遠無法超越主函數上聲明的類型is_valid_settings()以及do_is_valid_settings()函數,並且能夠注入這種“不可能”的值可以使特定情況的單元測試變得更加容易,因為您可以通過在問題中發出一些信號的價值來實現某種價值。
默認實現方法的第四個也許是最重要的含義是,它沒有被標記為最終,這意味著從您的班級繼承的程序員可以提供新的實現,從而更換或增強默認實現。
程序員可能會出錯的一種方式是無限地重複出現。例如這樣:
class InfiniteRecursion extends KickassCryptoOpenSslKickassOpenSslRoundTrip {
protected function do_encrypt( $input ) {
return $this->encrypt( $input );
}
}
如果do_encrypt()函數調用encrypt()函數,則encrypt()函數將調用do_encrypt()函數,然後關閉,我們轉到Infinity。
如果您執行此操作,並且已安裝並啟用了Xdebug,則默認情況下將呼叫深度限制為256。如果您沒有安裝Xdebug並啟用了PHP,則只會開始重複出現,並且將繼續這樣做,直到它達到其內存限製或耗盡RAM為止。
由於該圖書館幾乎沒有什麼可以阻止程序員像上面做的那樣意外編寫代碼的事情,就是通過跟踪使用Enter/Well Missipline嵌套的深度來檢測何時發生的情況,例如:
try {
$this->enter( __FUNCTION__ );
// 2023-04-07 jj5 - do work...
return $result;
}
catch ( AssertionError $ex ) {
throw $ex;
}
catch ( Throwable $ex ) {
try {
$this->handle( $ex, __FILE__, __LINE__, __FUNCTION__ );
}
catch ( Throwable $ignore ) {
try {
$this->ignore( $ignore, __FILE__, __LINE__, __FUNCTION__ );
}
catch ( Throwable $ignore ) { ; }
}
}
finally {
try { $this->leave( __FUNCTION__ ); } catch ( Throwable $ignore ) { ; }
}
leave()函數沒有業務拋出例外,但是我們將其包裝在一個trycatch塊中,以防萬一。
上面顯示了示例代碼,其中包含典型的捕獲塊,但是關鍵點是,我們要做的第一件事是將功能條目註冊為“呼叫” enter() ,然後在我們的最後塊中,我們在呼叫to to leave()中註冊函數退出。
如果一個功能輸入的次數超過kickass_crypto_recursion_limit允許的次數,而沒有離開,則會引發異常以打破遞歸。在編寫時,KICKASS_CRYPTO_RECURSION_LIMIT定義為100,少於Xdebug限制256的限制,這意味著我們應該始終能夠打破自己的遞歸循環。
而且,如果繼承者自稱並直接遞歸,我們遇到的所有麻煩都沒有什麼可做的:
class EpicFail extends KickassCryptoOpenSslKickassOpenSslRoundTrip {
protected function do_encrypt( $input ) {
return $this->do_encrypt( $input );
}
}
如上所述,並在以下一節中進行了詳細說明,該庫通常不會從其公共界面上的方法中引發例外,因為如果有問題,我們不想從呼叫堆棧中洩漏秘密。
而不是拋出異常,本庫中的類中的方法通常會返回false ,或者其他一些無效的值,例如null或[] 。
避免例外只是敏感功能調用的牢固規則,該呼叫處理秘密密鑰,密碼,未加密內容或任何其他敏感數據。在撰寫本文時,如果實現者已將do_get_error_list()返回了無效的值,則可以讓public get_error_list()函數拋出異常,除了在這種特定且希望不太可能的情況下其他所有情況都應該安全的情況,並且使用布爾值(或另一個適當的哨兵值)以傳達錯誤的誤差。
有時由於鍵入接口的性質,無法返回布爾值false,在某些情況下,空字符串( '' ),一個空數組( [] ),null( null ),浮點值零( 0.0 )或整數零(0)或零一個( 0 )或負一個( -1 )可以返回;但是,如果可能的話,返回錯誤絕對是首選。
除了:在某些情況下,減一( -1 )可以用作前哨值以發出錯誤的信號,例如,當您想指出無效的數組索引或無效計數時,但是與PHP中的某些其他語言中,不一定不一定是無效的陣列索引,並且返回false仍然是首選。如果在某些情況下,如果管理遙測計數器有問題,則該庫確實會使用減去。
發生錯誤的事實可以通過調用error()在您的組件中註冊,因此,如果呼叫者獲得錯誤的返回值,他們可以通過調用呼叫to get_error()或get_error_list()詢問您的組件,以獲取最近的錯誤(呼叫者可以使用clear_error()也可以)清除這些錯誤(也可以清除這些錯誤)。
在我們的庫中,註冊發生錯誤的函數是kickasscrypto類中定義的error()函數。
在某些錯誤情況下,最好和最安全的事情是吞噬錯誤,並將明智,安全和無爭議的默認值作為後備。
這是一個快速分解:
get_error_list()您會得到一個異常而沒有錯誤get_error()您會遇到空和錯誤clear_error()那是無效的,但是有錯誤handle()您會收到日誌條目,則沒有錯誤notify()它將被處理,然後忽略,沒有錯誤ignore()您會收到日誌條目,沒有錯誤throw()error()您的錯誤可能無法正確註冊,則始終返回falsecount_*()計數器,則會得到-1,沒有錯誤increment_counter()則會得到-1,沒有錯誤get_const_data_format()您會得到一個空字符串,沒有錯誤get_const_*()常數登錄器,則獲得由默認常數定義的值,沒有錯誤get_config_*() config訪問者,則獲得由默認常數定義的值(或者如果沒有這樣的東西,則false),並且沒有錯誤get_const()則會獲得默認值,沒有錯誤get_passphrase_list()則會得到一個空數組和一個錯誤get_encryption_passphrase()則會遇到無錯誤is_*()方法get_data_encoding()您將獲得一個空字符串,沒有錯誤get_data_format()您將得到false且無錯誤convert_secret_to_passphrase()您將會得到false,沒有錯誤get_padding()您會遇到false,沒有錯誤get_delay()您將遇到錯誤且不會出錯(注射緊急延遲)delay()您將遇到無效,也不會出錯(注射緊急延遲)log_error()您會遇到錯誤且沒有錯誤(但我們試圖寬恕)該庫對於異常處理和錯誤報告非常特別。
如果您對呼叫堆棧有敏感的數據,則不得拋出異常。敏感數據包括:
如果您遇到的情況無法繼續處理典型和預期的程序邏輯註冊此問題的方法是通過調用error()函數的字符串識別和描述問題,然後返回false以表示失敗。
由於error()函數始終返回布爾值false,因此通常可以註冊錯誤並在相同的同一中返回false,因此:
return $this->error( __FUNCTION__, 'something bad happened.' );
當我提名錯誤字符串時,我通常會以小寫字母開始,然後以一段時間結束它們。
請注意,可以攔截和重新釋放PHP php OssertionError例外。這些只能在開發過程中而不是在生產中發生。如果您調用代碼不信任的代碼,您可能不希望重新構成OssertionError例外,但是如果您調用代碼,您不相信自己的生活可能會有更大的問題。
如果您對斷言異常有強烈的看法,並且認為我不應該再試它們,我很樂意收到您的意見,以了解您的擔憂並可能解決這個問題。
以下是一些示例代碼,顯示如何處理異常和管理錯誤。
protected final function do_work_with_secret( $secret ) {
try {
$result = str_repeat( $secret, 2 );
$this->call_some_function_you_might_not_control( $result );
return $result;
}
catch ( AssertionError $ex ) {
throw $ex;
}
catch ( Throwable $ex ) {
try {
$this->handle( $ex, __FILE__, __LINE__, __FUNCTION__ );
}
catch ( Throwable $ignore ) {
try {
$this->ignore( $ignore, __FILE__, __LINE__, __FUNCTION__ );
}
catch ( Throwable $ignore ) { ; }
}
}
try {
return $this->error( __FUNCTION__, 'error working with string.' );
}
catch ( Throwable $ignore ) {
try {
$this->ignore( $ignore, __FILE__, __LINE__, __FUNCTION__ );
}
catch ( Throwable $ignore ) { ; }
}
return false;
}
在實際代碼中,您將定義一個錯誤常數供使用,而不是字符串字面的'error working with string.' 。在此庫中,錯誤常數的名稱以“ KICKASS_CRYPTO_ERROR_”開頭,它們在SRC/Code/gobal/global/constant/constant/Framework.php文件中定義。
請注意,我們甚至都不認為可以安全地調用handle() , ignore()或error() ;我們也將所有此類電話包裝在TryCatch處理程序中。在某些邊緣情況情況下,即使是應該是線程安全的這些功能也可能導致例外,例如,當無限遞歸被運行時流產時。如果您是此類事務的專家,則代碼可能會對您進行評論。
現在,我同意上述代碼有點瘋狂,只是在我看來,如果我們想安全,就不會避免它。我們必須在每種方法中每次都明確允許essertionError例外,以使斷言對我們作為開發工具有用,然後當我們要處理其他例外時,我們要對它們發出一些噪音,以便我們調用handle() ,這樣就是handle() ()都會拖延到do_handle() ,那麼我們就可以拋出(我們都可以拋出(我們),我們可以拋出handle() ,我們可以thor thor()),)最後有機會了解它們的錯誤代碼,因此我們通知我們將忽略ignore()呼籲,但這會延遲到do_ignore() ,程序員可以覆蓋這些例外,並從...拋棄...但是如果發生這種情況,我們會默默地忽略這樣一個問題。
然後,如果我們通過所有這些,並且我們的功能尚未返回,那麼這是一個錯誤情況,因此我們要通知錯誤,但是error() defers to do_error()可以覆蓋並拋棄,因此我們將其包裝在一個try-catch塊中,然後做例外,然後再次忽略舞蹈。
我的意思是,它遍布頂部,過多,但至少應該是安全的,它符合兩個要求:
在通常的快樂代碼路徑中,沒有例外處理代碼甚至運行。
有許多用於測試布爾條件的功能,它們以“ is_”開頭,然後返回布爾值。這些功能只能進行測試並返回真或錯誤,它們不應使用error()函數登記錯誤,如果是必要的,則呼叫者將執行此操作。
IS_()函數可以使用上面記錄的鍵入最終包裝習慣實現。
以下是代碼中的一個很好的例子。
protected final function is_valid_secret( $secret ) : bool {
try {
$is_valid = $this->do_is_valid_secret( $secret );
// ...
assert( is_bool( $is_valid ) );
return $is_valid;
}
catch ( AssertionError $ex ) {
throw $ex;
}
catch ( Throwable $ex ) {
try {
$this->handle( $ex, __FILE__, __LINE__, __FUNCTION__ );
}
catch ( Throwable $ignore ) {
$this->ignore( $ignore, __FILE__, __LINE__, __FUNCTION__ );
}
}
return false;
}
請注意, do_is_valid_secret()在呼叫堆棧上也有一個秘密,因此應以相同的方式將其實現為例外安全(如果直接從代碼的其他部分調用)。
也請注意,只能重新違反違規行為,這些都不應在生產中發生,並且使測試代碼更加容易。
該庫採取的單位測試方法是簡單而強大的。每個單元測試都可以定義三種類型的測試:
每個腳本將是具有相同名稱的Shell腳本,例如fast.sh ,或者如果缺少具有相同名稱的PHP腳本,例如fast.php 。測試跑者只是找到這些腳本並運行它們。 This is easy to do and provides all the power we need to run our tests, including support for the various situations where each test instance needs to run in its own process and be isolated from other testing environments.
If you have flakey and unreliable tests you can stick them in as silly tests. The fast and slow tests are the important ones, and you shouldn't put slow tests in the fast test scripts. The fast tests are for day to day programming and testing and the slow scripts are for running prior to a version release.
Here are some notes regarding notable components:
config.php file for the demoSome countries have banned the import or use of strong cryptography, such as 256 bit AES.
Please be advised that this library does not contain cryptographic functions, they are provided by your PHP implementation.
Copyright (c) 2023 John Elliot V.
This code is licensed under the MIT License.
See the contributors file.
I should probably be more disciplined with my commit messages... if this library matures and gets widely used I will try to be more careful with my commits.
The Kickass Crypto ASCII banner is in the Graffiti font courtesy of TAAG.
The string "kickass" appears in the source code 1,506 times (including the ASCII banners).
SLOC and file count reports generated using David A. Wheeler's 'SLOCCount'.
I'd love to hear from you! Hit me up at [email protected]. Put "Kickass Crypto" in the subject line to make it past my mail filters.