正確地使用加密與認證技術
在密碼學專家之中,“加密並不是認證”是一個簡單的共識。但很多不瞭解密碼學的開發者,並不知道這句話的意義。如果這個知識更廣為人知和深入理解,那麼將會避免很多的設計錯誤。
這一概念本身並不困難,但在表面之下,還有更多豐富的細節和玄妙之處有待發現。本文就是講述開發者對於加密和認證二者的混淆與誤用,並附上了優秀的解決方案。
0x01 加密與認證之間有哪些區別?
加密是呈現資訊,使其在沒有正確的金鑰情況下,變得難以卒讀的過程。在簡單的對稱加密中,同一個金鑰被用於加密和解密。在非對稱加密中,可以使用使用者的公鑰對資訊加密,使得只有對應私鑰的擁有者才能讀取它。
認證是呈現資訊,使其抗篡改(通常在某一非常低的機率之內,小於1除以已知宇宙中粒子的數量),同時也證明它起源於預期傳送者的過程。
注意:當本文提及真實性時,是專門指的資訊真實性,而不是身份真實性。這是一個PKI和金鑰管理問題,我們可能在未來的部落格中詳細說明。
就CIA triad而言:加密提供機密性,認證提供完整性。
加密不提供完整性;被篡改的資訊(通常)還能解密,但結果通常會是垃圾。單獨加密也不抑制惡意第三方傳送加密資訊。
認證不提供機密性;可以為明文資訊提供抗篡改。
在程式設計師中,常見的錯誤是混淆這兩個概念。你能很容易找到這樣的一個庫或者框架:加密cookie資料,然後在僅僅解密它之後就無條件地信任與使用之。
0x02 加密
我們之前定義了加密,並且詳細說明了它是提供機密性,但不提供完整性和真實性的。你可以篡改加密資訊,並將產生的垃圾給予接收者。而且你甚至可以利用這種垃圾產生機制,來繞過安全控制。
考慮在加密cookie的情況下,有如下程式碼:
#!php
function setUnsafeCookie($name, $cookieData, $key)
{
$iv = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
return setcookie(
$name,
base64_encode(
$iv.
mcrypt_encrypt(
MCRYPT_RIJNDAEL_128,
$key,
json_encode($cookieData),
MCRYPT_MODE_CBC,
$iv
)
)
);
}
function getUnsafeCookie($name, $key)
{
if (!isset($_COOKIE[$name])) {
return null;
}
$decoded = base64_decode($_COOKIE[$name]);
$iv = mb_substr($decoded, 0, 16, '8bit');
$ciphertext = mb_substr($decoded, 16, null, '8bit');
$decrypted = rtrim(
mcrypt_decrypt(
MCRYPT_RIJNDAEL_128,
$key,
$ciphertext,
MCRYPT_MODE_CBC,
$iv
),
"\0"
);
return json_decode($decrypted, true);
}
上面的程式碼提供了在密碼段連結模組的AES加密,如果你傳入32位元組的字串作為$key,你甚至可以聲稱,為你的cookie提供了256位的AES加密,然後人們可能被誤導相信它是安全的。
0x03 如何攻擊未經認證的加密
比方說,在登入到這個應用程式之後,你會發現你收到一個會話cookie,看起來就像
kHv9PAlStPZaZJHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==
讓我們改變一個位元組的第一塊(初始化向量),並反覆傳送我們的新的cookie,直到出現一些變化。應該採取共4096次HTTP請求,以嘗試變數IV所有可能的單位元組變化。在上面的例子中,經過2405次請求後,我們得到一個看起來像這樣的字串:
kHv9PAlStPZaZZHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==
相比之下,在base64編碼的cookie中只有一個字元不同(kHv9PAlStPZaZ J vs kHv9PAlStPZaZ Z):
- kHv9PAlStPZaZJHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==
+ kHv9PAlStPZaZZHIYXzyCnuAhWdRRK7H0cNVUCwzCZ4M8fxH79xIIIbznxmiOxGQ7td8LwTzHFgwBmbqWuB+sQ==
我們儲存在這個cookie裡的原始資料,是看起來像這樣的陣列:
#!php
array(2) {
["admin"]=>
int(0)
["user"]=>
"aaaaaaaaaaaaa"
}
但在僅僅改變初始化向量的一個位元組之後,我們就能夠改寫我們的閱讀資訊:
#!php
array(2) {
["admin"]=>
int(1)
["user"]=>
"aaaaaaaaaaaaa"
}
根據底層應用程式的設定方法,你或許可以翻轉一位進而提升成為一名管理員。即使你的cookie是加密的。 如果你想再現我們的結果,我們的加密金鑰是十六進位制下的:000102030405060708090a0b0c0d0e0f
0x04 認證
如上所述,認證旨在提供資訊的完整性(我們指顯著抗篡改能力),而這證明它來自預期的源(真實性)。這樣做的典型方法是,為資訊計算一個金鑰雜湊訊息認證碼(HMAC的簡稱),並將資訊與它連結。
#!php
function hmac_sign($message, $key)
{
return hash_hmac('sha256', $message, $key) . $message;
}
function hmac_verify($bundle, $key)
{
$msgMAC = mb_substr($bundle, 0, 64, '8bit');
$message = mb_substr($bundle, 64, null, '8bit');
return hash_equals(
hash_hmac('sha256', $message, $key),
$msgMAC
);
}
重要的是,這裡使用一個適當的雜湊工具,如HMAC,而不僅僅是一個簡單的雜湊函式。
#!php
function unsafe_hash_sign($message, $key)
{
return md5($key.$message) . $message;
}
function unsafe_hash_verify($bundle, $key)
{
$msgHash = mb_substr($bundle, 0, 64, '8bit');
$message = mb_substr($bundle, 64, null, '8bit');
return md5($key.$message) == $msgHash;
}
我在這兩個函式名前面加了unsafe,是因為它們還是易受到一些缺點的危害:
Timing Attacks
Chosen Prefix Attacks on MD5 (PDF)
Non-strict equality operator bugs (largely specific to PHP)
現在,我們有點接近我們強大的對稱加密認證的目標。目前仍有幾個問題,如:
如果我們的原始資訊以空位元組結尾會發生什麼?
有沒有一個比mcrypt擴充套件庫預設使用的更好的填充策略?
由於使用AES,有哪些通訊方面是易受攻擊的?
幸運的是,這些問題在現有的加密函式庫中已有了解答。我們強烈推薦你使用現有的庫,而不是寫自己的加密功能。對於PHP開發人員來說,你應該使用defuse/php-encryption(或者libsodium)。
0x05 用Libsodium安全加密Cookies
#!php
/*
// At some point, we run this command:
$key = Sodium::randombytes_buf(Sodium::CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES);
*/
/**
* Store ciphertext in a cookie
*
* @param string $name - cookie name
* @param mixed $cookieData - cookie data
* @param string $key - crypto key
*/
function setSafeCookie($name, $cookieData, $key)
{
$nonce = Sodium::randombytes_buf(Sodium::CRYPTO_SECRETBOX_NONCEBYTES);
return setcookie(
$name,
base64_encode(
$nonce.
Sodium::crypto_secretbox(
json_encode($cookieData),
$nonce,
$key
)
)
);
}
/**
* Decrypt a cookie, expand to array
*
* @param string $name - cookie name
* @param string $key - crypto key
*/
function getSafeCookie($name, $key)
{
$hexSize = 2 * Sodium::Sodium::CRYPTO_SECRETBOX_NONCEBYTES;
if (!isset($_COOKIE[$name])) {
return array();
}
$decoded = base64_decode($_COOKIE[$name]);
$nonce = mb_substr($decoded, 0, $hexSize, '8bit');
$ciphertext = mb_substr($decoded, $hexSize, null, '8bit');
$decrypted = Sodium::crypto_secretbox_open(
$ciphertext,
$nonce,
$key
);
if (empty($decrypted)) {
return array();
}
return json_decode($decrypted, true);
}
對於沒有libsodium庫的開發人員,我們的一個部落格讀者,提供了一個安全cookie實現的例子,其使用了defuse/php-encryption(我們推薦的PHP庫)。
0x06 使用關聯資料的認證加密
在我們前面的示例中,我們集中精力於,同時使用加密和認證,使其作為必須小心使用的單獨元件,以避免加密的悲劇。具體而言,我們專注於密碼段連結模組的AES加密。
然而,密碼學家已經開發出更新,更具有彈性的加密模型,其加密和認證資訊在同一操作。這些模型被稱為AEAD模型(Authenticated Encryption with Associated Data)。關聯資料意味著,無論你的應用程式需要什麼進行認證,都不加密。
AEAD模型通常用於有狀態的目的,如網路通訊中,其中一個隨機數可以很容易地管理。
AEAD兩個可靠的實現是AES-GCM和ChaCha20-Poly1305。
AES-GCM是Galois/Counter模式中的高階加密標準(又名Rijndael演算法加密)。這種模式在OpenSSL的最新版本中加入,但它目前在PHP中還不被支援。
ChaCha20-Poly1305結合了ChaCha20流密碼與Poly1305訊息認證碼。這種模式在libsodium PHP擴充套件可用。
Sodium::crypto_aead_chacha20poly1305_encrypt() Sodium::crypto_aead_chacha20poly1305_decrypt()
總結一下,你該記住的
加密不是認證
加密提供機密性
認證提供完整性
將兩者混為一談你就得自擔風險
為了完成CIA triad,你需要單獨解決可用性。這通常不是一個加密問題。
更重要的是:在密碼學專家的監督下,使用具有韌性被證實記錄的庫,而不是自己在那裡閉門造車,你會好得多。
原文:https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly
相關文章
- 教你正確認識WIFI無線聯網技術2016-06-21WiFi
- 使用帶有SQL Server加密技術的認證2009-10-26SQLServer加密
- SQL Server正確刪除Windows認證使用者的方法教程2019-10-10SQLServerWindows
- Android-如何正確地使用Handler2018-01-03Android
- HTTPS 加密與認證機制2022-02-09HTTP加密
- Java加密技術(十一)——雙向認證2015-03-12Java加密
- golang使用JWX進行認證和加密2023-02-22Golang加密
- 如何正確認識代理伺服器的使用2021-09-11伺服器
- 使用行業標準網線測試解決方案和銅纜應用程式正確地認證銅纜網路2023-02-28行業
- LINUX認證推出 掀新一輪技術認證狂潮(轉)2007-08-15Linux
- ThreadLocal的正確使用與原理2022-11-02thread
- Python3 MD5加密正確使用方式2018-06-26Python加密
- 【譯】如何通過 INUIAddVoiceShortcutButtonDelegate 正確地使用 INUIAddVoiceShortcutButton2019-02-27UI
- 靈雀雲ACP與浪潮資訊KOS完成澎湃技術認證2023-04-20
- Java 人員正確使用 IntelliJ IDEA 的方式 | 掘金技術徵文2017-04-26JavaIntelliJIdea
- Swift 里正確地 addTarget(_:action:for:)2017-08-23Swift
- Laravel + dingoapi + jwt 使用者認證無法正確指定 guard 的解決辦法2018-07-11LaravelGoAPIJWT
- 從技術思維角度聊一聊『程式設計師』擺地攤的正確姿勢2020-06-05程式設計師
- 寬頻認證計費系統的認證技術主要有哪些2019-10-18
- Java 如何正確地輸出日誌2019-03-04Java
- 讀《原則》(一):“正確地失敗”2018-12-25
- 如何正確地寫出單例模式2017-05-16單例模式
- Java程式設計師如何正確地學習新的知識,擴充自己的技術棧2018-06-19Java程式設計師
- 如何正確地使用Python的屬性和描述符2016-08-22Python
- 自定義Django認證系統的技術方案2020-12-12Django
- 【技術向】SSH加密隧道流量攻擊與檢測技術2021-04-01加密
- 加密貨幣轉正!多米尼克立法確認波場系代幣為國家法幣2022-10-20加密
- 正確高效使用 Google2018-07-27Go
- 正確使用rman crosscheck2015-04-13ROS
- 正規表示式驗證Ip地址(絕對正確)2016-02-04
- 使用datapump傳輸加密表請先確認TDE Encryption wallet狀態2014-12-03加密
- 隨著新日益增多的技術,如何正確的提升自己?2017-07-20
- 鯤鵬展翅|SphereEx 獲華為鯤鵬技術認證2021-09-13
- 使用Json.NET驗證json字串是否格式正確2024-09-07JSON字串
- js檢查身份證號是否正確2019-01-21JS
- JS驗證18位身份證號的正確性2018-11-01JS
- 使用OpenSSH證書認證2020-08-19
- 如何正確使用 Slim 框架2019-06-14框架