C/C++ 加密解密

2021-05-28 10:10 更新

4.1 【必須】不得明文存儲用戶密碼等敏感數據

用戶密碼應該使用 Argon2, scrypt, bcrypt, pbkdf2 等算法做哈希之后再存入存儲系統(tǒng), https://password-hashing.net

https://libsodium.gitbook.io/doc/password_hashing/default_phf#example-2-password-storage

用戶敏感數據,應該做到傳輸過程中加密,存儲狀態(tài)下加密 傳輸過程中加密,可以使用 HTTPS 等認證加密通信協(xié)議

存儲狀態(tài)下加密,可以使用 SQLCipher 等類似方案。

4.2 【必須】內存中的用戶密碼等敏感數據應該安全抹除

例如用戶密碼等,即使是臨時使用,也應在使用完成后應當將內容徹底清空。

錯誤:

#include <openssl/crypto.h>
#include <unistd.h>


    {
        ...
        string user_password(100, '\0');
        snprintf(&user_password, "password: %s", user_password.size(), password_from_input);
        ...
    }

正確:

    {
        ...
        string user_password(100, '\0');
        snprintf(&user_password, "password: %s", user_password.size(), password_from_input);
        ...
        OPENSSL_cleanse(&user_password[0], user_password.size());
    }

關聯漏洞:

  • 高風險-敏感信息泄露

4.3 【必須】rand() 類函數應正確初始化

rand類函數的隨機性并不高。而且在使用前需要使用srand()來初始化。未初始化的隨機數可能導致某些內容可預測。

// Bad
int main() {
  int foo = rand();
  return 0;
}

上述代碼執(zhí)行完成后,foo的值是固定的。它等效于 srand(1); rand();。

// Good


int main() {
  srand(time(0));
  int foo = rand();
  return 0;
}

關聯漏洞:

  • 高風險-邏輯漏洞

4.4 【必須】在需要高強度安全加密時不應使用弱PRNG函數

在需要生成 AES/SM1/HMAC 等算法的密鑰/IV/Nonce, RSA/ECDSA/ECDH 等算法的私鑰,這類需要高安全性的業(yè)務場景,必須使用密碼學安全的隨機數生成器 (Cryptographically Secure PseudoRandom Number Generator (CSPRNG) ), 不得使用 rand() 等無密碼學安全性保證的普通隨機數生成器。

推薦使用的 CSPRNG 有:

  1. OpenSSL 中的 RAND_bytes() 函數, https://www.openssl.org/docs/man1.1.1/man3/RAND_bytes.html
  2. libsodium 中的 randombytes_buf() 函數
  3. Linux kernel 的 getrandom() 系統(tǒng)調用, https://man7.org/linux/man-pages/man2/getrandom.2.html . 或者讀 /dev/urandom 文件, 或者 /dev/random 文件。
  4. Apple IOS 的 SecRandomCopyBytes(), https://developer.apple.com/documentation/security/1399291-secrandomcopybytes
  5. Windows 下的 BCryptGenRandom(), CryptGenRandom(), RtlGenRandom()

#include <openssl/aes.h>
#include <openssl/crypto.h>
#include <openssl/rand.h>
#include <unistd.h>


    {
        unsigned char key[16];
        if (1 != RAND_bytes(&key[0], sizeof(key))) {  //... 錯誤處理
            return -1;
        }


        AES_KEY aes_key;
        if (0 != AES_set_encrypt_key(&key[0], sizeof(key) * 8, &aes_key)) {
            // ... 錯誤處理
            return -1;
        }


        ...


        OPENSSL_cleanse(&key[0], sizeof(key));
    }

rand()類函數的隨機性并不高。敏感操作時,如設計加密算法時,不得使用rand()或者類似的簡單線性同余偽隨機數生成器來作為隨機數發(fā)生器。符合該定義的比特序列的特點是,序列中“1”的數量約等于“0”的數量;同理,“01”、“00”、“10”、“11”的數量大致相同,以此類推。

例如 C 標準庫中的 rand() 的實現只是簡單的線性同余算法,生成的偽隨機數具有較強的可預測性。

當需要實現高強度加密,例如涉及通信安全時,不應當使用 rand() 作為隨機數發(fā)生器。

實際應用中, C++11 標準提供的random_device保證加密的安全性和隨機性 但是 C++ 標準并不保證這一點??缙脚_的代碼可以考慮用 OpenSSL 等保證密碼學安全的庫里的隨機數發(fā)生器。

關聯漏洞:

  • 高風險-敏感數據泄露

4.5 【必須】自己實現的rand范圍不應過小

如果在弱安全場景相關的算法中自己實現了PRNG,請確保rand出來的隨機數不會很小或可預測。

// Bad
int32_t val = ((state[0] * 1103515245U) + 12345U) & 999999;

上述例子可能想生成0~999999共100萬種可能的隨機數,但是999999的二進制是11110100001000111111,與&運算后,0位一直是0,所以生成出的范圍明顯會小于100萬種。

// Good
int32_t val = ((state[0] * 1103515245U) + 12345U) % 1000000;


// Good
int32_t val = ((state[0] * 1103515245U) + 12345U) & 0x7fffffff;

關聯漏洞:

  • 高風險-邏輯漏洞
以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號