安党な暗号化プログラミング。 パヌト2ファむナル

私たちは、Jean-Phillip Omassonによる安党な暗号化プログラミングのための䞀連のルヌルの翻蚳を続けおいたす...



セキュリティ䞊重芁なコヌドの䞀郚に察するコンパむラの改ざんを防止



問題



䞀郚のコンパむラは、圹に立たないず考えられる操䜜を最適化したす。



たずえば、コンパむラMS Visual C ++は挔算子| memset |を芋぀けたした。 次のTor匿名ネットワヌク実装コヌドのスニペット



int crypto_pk_private_sign_digest(...) { char digest[DIGEST_LEN]; (...) memset(digest, 0, sizeof(digest)); return r; }
      
      





ただし、この挔算子の圹割| memset | |ダむゞェスト|バッファをクリアするこずです 機密デヌタから、初期化されおいないスタックからのデヌタの埌続の読み取りでは、機密情報を取埗するこずは䞍可胜でした。



䞀郚のコンパむラは、プログラムのどこかでコヌドが間違っおいるず考えお、条件付きチェックを削陀できるず考えおいたす。 たずえば、次のコヌドスニペットを芋぀ける



  call_fn(ptr); //   ptr. //    if (ptr == NULL) { error("ptr must not be NULL"); }
      
      





䞀郚のコンパむラは、条件| ptr == NULL | そうでなければ、関数| call_fn|でそれを間接参照するのは間違っおいるので、垞にFALSEでなければなりたせん。



解決策



コンパむルされたコヌドを分析し、すべおの呜什がそこに存圚するこずを確認したす。 これは暙準サむズのアプリケヌションでは䞍可胜ですが、セキュリティの芳点から重芁なコヌドに察しおは実行する必芁がありたす。



コンパむラが実行できる最適化を理解し、安党なプログラミングの原則の芳点からそれぞれの効果を慎重に評䟡したす。 特に、コヌドフラグメントたたはブランチを削陀する最適化、およびプログラムの残りの郚分が正しい堎合に「衚瀺されない」゚ラヌを防止するコヌドフラグメントに泚意しおください。



可胜な限り、コンパむル䞭にその最適化を無効にしお、セキュリティに圱響する条件の怜蚌を削陀たたは匱めるこずを怜蚎しおください。



最適化による呜什の削陀を防ぐために、volatileキヌワヌドを䜿甚しお関数をオヌバヌラむドできたす。 これは、たずえばmemset |をオヌバヌラむドするずきにlibotteryで䜿甚されたす。



 void * (*volatile memset_volatile)(void *, int, size_t) = memset;
      
      





memset_sの呌び出しはC11で導入されおおり、最適化䞭の削陀は犁止されおいたす。



 #define __STDC_WANT_LIB_EXT1__ 1 #include <string.h> ... memset_s(secret, sizeof(secret), 0, sizeof(secret));
      
      







安党なプログラミングむンタヌフェむスず安党でないプログラミングむンタヌフェむスの混同を避ける



問題



倚くのプログラミング環境は、同じ゜フトりェアむンタヌフェむスの異なる実装を提䟛したす。機胜は倖芋的には同じですが、セキュリティ機胜は根本的に異なりたす。



この問題は、乱数センサヌの兞型的なものですOpenSSLには| RAND_bytes|がありたす。 および| RAND_pseudo_bytes| BSD Cラむブラリには| RAND_bytes| および| RAND_pseudo_bytes|、Javaで-| SecureRandom | および|ランダム|



もう1぀の䟋は、時間に䟝存しないバむトワヌド比范機胜を提䟛するシステムには、同時にタむムリヌクを匕き起こす可胜性のあるオプションがあるずいう事実です。



悪い決断



機胜は、䞀郚のプラットフォヌムでは安党で、他のプラットフォヌムでは危険な堎合がありたす。 このような堎合、プログラマヌはこの関数を䜿甚しお、自分のコヌドが安党なプラットフォヌムで実行されるず信じおいたす。 コヌドが他のプラットフォヌムに移怍されお安党でなくなる可胜性があるため、これは悪いアプロヌチです-誰も気付かないでしょう。



プラットフォヌム䟝存の関数を再定矩できるシステムでは、䞀郚のプログラマヌは安党でない関数を安党なものずしおオヌバヌラむドし、䞀般に安党ではないプログラムむンタヌフェむスを䜿甚しおプログラムを蚘述したす。 これは、プログラマヌが安党でないように芋えるコヌドを曞くずいう事実に぀ながるため、かなり議論の䜙地のあるアプロヌチです。 さらに、オヌバヌラむドされたメ゜ッドが機胜しない堎合、プログラムは安党でなくなり、これを刀別できたせん。 そしお最埌に、これは、そのようなプログラムのコヌド断片が他のプロゞェクトにコピヌされた堎合、安党でないずいう事実に぀ながりたす。



解決策



可胜な限り、安党な機胜に安党でないオプションを䜿甚しないでください。 たずえば、ランダムな初期パディングを䜿甚した堅牢なストリヌム暗号に基づくMIFは、ほずんどのアプリケヌションで十分に高速です。 デヌタ型に䟝存しないmemcmpの眮換は、すべおのメモリ領域比范操䜜に䜿甚できるほど高速です。



安党でない関数を削陀できない堎合は、コンパむル段階で゚ラヌが生成されるように再定矩するか、静的コヌド分析甚のツヌルを䜿甚しお、安党でない関数の䜿甚を特定しお譊告したす。 安党でないオプションを安党なオプションずしおオヌバヌラむドできる堎合は、セキュリティを匷化するために、安党でないAPIを呌び出さないで、その䜿甚の事実を怜出できるこずを確認しおください。



䞡方のオプション安党なものず安党でないものを残す必芁がある堎合は、関数名が非垞に異なり、誀っお安党でないオプションを䜿甚するのが困難であるこずを確認しおください。 たずえば、安党なMAPず安党でないMAPがある堎合、安党でないオプションに「Random」、「FastRandom」、「MersenneTwister」、たたは「LCGRand」ずいう名前を付けないでください。代わりに、「InsecureRandom」などの名前を付けたす。 安党でない関数の䜿甚が垞に少し怖いように、プログラミングむンタヌフェむスを蚭蚈したす。



プラットフォヌムが安党でないこずを瀺す名前のない関数の安党でないバヌゞョンを提䟛し、この関数を削陀できない堎合は、安党な名前のシステムコヌルラッパヌを䜿甚し、コヌドの静的分析により、安党でない名前のすべおの䜿甚を明らかにしたす。



関数が䞀郚のプラットフォヌムでは安党で、他のプラットフォヌムでは安党でない堎合は、関数を盎接䜿甚しないでください。代わりに、安党なラッパヌを定矩しお䜿甚しおください。



同じAPIレベルでセキュリティレむダヌず暗号化プリミティブの抜象化を混圚させないでください



問題



゜フトりェアむンタヌフェむスのさたざたな郚分に必芁な分析の皮類が明確でない堎合、プログラマは安党に䜿甚できる機胜に぀いお十分に簡単に間違いを犯す可胜性がありたす。



RSA゜フトりェアむンタヌフェヌスの次の䟋発明されたが、実際に芋られるものに䌌おいるを怜蚎しおください。



 enum rsa_padding_t { no_padding, pkcs1v15_padding, oaep_sha1_padding, pss_padding }; int do_rsa(struct rsa_key *key, int encrypt, int public, enum rsa_padding_t padding_type, uint8_t *input, uint8_t *output);
      
      





「キヌ」パラメヌタに詳现のコンポヌネントが含たれおいるず仮定するず、関数は16通りの方法で呌び出すこずができ、その倚くは無意味で、䞀郚は安党ではありたせん。

暗号化/埩号化 察称/非察称

パディングタむプ

発蚀
0 0 なし パディングなしの埩号化。 停物の可胜性。
0 0 pkcs1v15 埩号化PKCS1 v1.5。 おそらく、Blainbacherによる攻撃の察象ずなりたす。
0 0 あえ 埩号化OAEP。 良いオプション。
0 0 pss 埩号化PSS。 意図しない゚ラヌに぀ながる可胜性がある、かなり奇劙なオプション
0 1 なし パディングなしの眲名。 停物の可胜性。
0 1 pkcs1v15 眲名PKCS1 v1.5。 䞀郚のアプリケヌションに適しおいたすが、PSS眲名を䜿甚するこずをお勧めしたす。
0 1 あえ 眲名OAEP。 䞀郚のアプリケヌションに適しおいたすが、PSS眲名を䜿甚するこずをお勧めしたす。
0 1 pss 眲名PSS。 非垞に良いオプション。
... ... ... 残りのオプション暗号化ず眲名の怜蚌。




この関数を呌び出す16の可胜な方法のうち4぀だけが安党で、別の6぀は安党ではなく、堎合によっおは残りの6぀が䜿甚䞭に問題を匕き起こす可胜性があるこずに泚意しおください。 このようなAPIは、RSAシステムでさたざたな远加メ゜ッドを䜿甚するこずの意味を理解しおいる開発者にのみ適しおいたす。



ここで、さたざたなモヌドでのブロック暗号化、キヌ生成、さたざたなメッセヌゞ認蚌コヌドおよび眲名甚のプログラミングむンタヌフェむスを远加するこずを想像しおください。 そのようなプログラムむンタヌフェむスを䜿甚しお認蚌ずデヌタ暗号化を実装する正しい機胜を開発しようずするプログラマヌには、膚倧な遞択肢がありたすが、安党なオプションの数は明らかに枛少したす。



解決策







笊号なしの型を䜿甚しお、バむナリデヌタを衚したす。



問題



䞀郚のCラむクな蚀語では、笊号付き敎数型ず笊号なし敎数型は異なりたす。 特に、Cでは、問題はtype | char | iconicは実装に䟝存したす。 これは、たずえば次のような問題のあるコヌドに぀ながる可胜性がありたす。



 int decrypt_data(const char *key, char *bytes, size_t len); void fn(...) { //... char *name; char buf[257]; decrypt_data(key, buf, 257); int name_len = buf[0]; name = malloc(name_len + 1); memcpy(name, buf+1, name_len); name[name_len] = 0; //... }
      
      





If | char | 笊号なしの堎合、このコヌドは期埅どおりに動䜜したす。 しかし、もし| char | 重芁、| buf [0] | は負の倀をずるこずができ、関数の匕数の倀が非垞に倧きくなりたす| malloc | および| memcpy | 最埌の文字の倀を0に蚭定しようずするず、ヒヌプが砎損する可胜性がありたす。状況はさらに悪化する可胜性がありたす| buf [0] | 255の堎合、name_lenは-1になりたす。 したがっお、メモリにサむズ0バむトのバッファを割り圓お、次にコピヌしたす|size_t-1 memcpy | ヒヌプを詰たらせるこのバッファに。



解決策



笊号付きバむト型ず笊号なしバむト型を区別する蚀語では、実装は笊号なし型を䜿甚しおAPIでバむト文字列を衚す必芁がありたす。



機密デヌタのメモリをクリア



問題



ほずんどのオペレヌティングシステムでは、最初のプロセスがシステムにメモリを停止たたは返したため、あるプロセスで䜿甚されたメモリを最初にクリアせずに別のプロセスで䜿甚できたす。 メモリに秘密キヌが含たれおいる堎合、それらは別のプロセスで䜿甚できるため、䟵害される可胜性が高くなりたす。 マルチナヌザヌシステムでは、これにより、システムの他のナヌザヌのキヌを決定できたす。 同じシステム内であっおも、このような状況は、以前は比范的「安党な」脆匱性が機密デヌタの挏掩に぀ながる可胜性があるずいう事実に぀ながる可胜性がありたす。



解決策



機密デヌタを含むすべおの倉数は、忘れるたでクリアし、䜿甚を停止しおください。 | mmap|関数を䜿甚する 実行䞭| munmap| すぐにメモリが解攟され、メモリを制埡できなくなりたす。



メモリをクリアするか、芖野倖のオブゞェクトを砎壊するには、可胜な堎合はプラットフォヌムに䟝存するメモリクリヌニング機胜を䜿甚したす-SecureZeroMemory|など win32たたは| OPENSSL_cleanse| opensslの堎合。



Cの倚かれ少なかれ普遍的な解決策は次のずおりです。



 void burn( void *v, size_t n ) { volatile unsigned char *p = ( volatile unsigned char * )v; while( n-- ) *p++ = 0; }
      
      







匷い䞀臎を䜿甚する





問題



倚くの暗号システムはランダム性の゜ヌスを必芁ずし、そのようなシステムは、そのような゜ヌスのランダム性からのわずかな逞脱の堎合でも安党でなくなる可胜性がありたす。 たずえば、DSAで乱数を1぀でもリヌクするず、秘密キヌの決定が非垞に高速になりたす。 䞍十分なランダム性を刀断するのは非垞に困難です。OpenSSLでのDebian乱数ゞェネレヌタヌの゚ラヌは2幎間気付かれず、倚数のキヌの䟵害に぀ながりたした。 暗号化アプリケヌションの乱数の芁件は非垞に厳密です。倚くの擬䌌乱数ゞェネレヌタヌはそれらを満たしおいたせん。



悪い決断



暗号化アプリケヌションの堎合







解決策



プリミティブずその蚭蚈を遞択するこずにより、ランダム性の䜿甚を最小限に抑えたすたずえば、 Ed25519を䜿甚するず、決定的な方法で電子眲名の曲線を取埗できたす 。 乱数を生成するには、オペレヌティングシステムによっお提䟛され、暗号化芁件を満たすこずが保蚌された゜ヌス| / dev / random |などを䜿甚したす。 リ゜ヌスに制限のあるプラットフォヌムでは、アナログランダムノむズ゜ヌスず適切な混緎手順の䜿甚を怜蚎しおください。



センサヌが生成した倀をチェックしお、受信したバむトが本来あるべきものであり、正しく曞き蟌たれおいるこずを確認しおください。



Nadi Heninger et al。の蚘事のセクション7の掚奚事項に埓っおください。



Ivy Bridgeアヌキテクチャおよびそれ以降の䞖代を搭茉したIntelプロセッサでは、組み蟌みのゞェネレヌタヌが高い゚ントロピヌず速床を保蚌したす。



Unixシステムは通垞、次を䜿甚したす| / dev / random | たたは| / dev / urandom |。 ただし、それらのうちの最初のものにはブロッキングプロパティがありたす。 十分なランダム性が蓄積されおいないず考えられる堎合、倀を返したせん。 このプロパティは、䜿いやすさを制限したす。

その䜿甚、したがっお| / dev / urandom | より頻繁に䜿甚されたす。 䜿甚| / dev / urandom | 十分簡単



 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> int main() { int randint; int bytes_read; int fd = open("/dev/urandom", O_RDONLY); if (fd != -1) { bytes_read = read(fd, &randint, sizeof(randint)); if (bytes_read != sizeof(randint)) { fprintf(stderr, "read() failed (%d bytes read)\n", bytes_read); return -1; } } else { fprintf(stderr, "open() failed\n"); return -2; } printf("%08x\n", randint); /* assumes sizeof(int) <= 4 */ close(fd); return 0; }
      
      





ただし、この単玔なプログラムでは、ランダム性を安党に生成するには䞍十分な堎合がありたす。関数| getentropy_urandom |のように、远加の゚ラヌチェックを実行する方が安党です。 リブレスル



 static int getentropy_urandom(void *buf, size_t len) { struct stat st; size_t i; int fd, cnt, flags; int save_errno = errno; start: flags = O_RDONLY; #ifdef O_NOFOLLOW flags |= O_NOFOLLOW; #endif #ifdef O_CLOEXEC flags |= O_CLOEXEC; #endif fd = open("/dev/urandom", flags, 0); if (fd == -1) { if (errno == EINTR) goto start; goto nodevrandom; } #ifndef O_CLOEXEC fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); #endif /* Lightly verify that the device node looks sane */ if (fstat(fd, &st) == -1 || !S_ISCHR(st.st_mode)) { close(fd); goto nodevrandom; } if (ioctl(fd, RNDGETENTCNT, &cnt) == -1) { close(fd); goto nodevrandom; } for (i = 0; i < len; ) { size_t wanted = len - i; ssize_t ret = read(fd, (char *)buf + i, wanted); if (ret == -1) { if (errno == EAGAIN || errno == EINTR) continue; close(fd); goto nodevrandom; } i += ret; } close(fd); if (gotdata(buf, len) == 0) { errno = save_errno; return 0; /* satisfied */ } nodevrandom: errno = EIO; return -1; }
      
      





Windowsシステムの堎合| CryptGenRandom | Win32 APIは、暗号化での䜿甚に適した擬䌌ランダムビットを生成したす。 マむクロ゜フトでは、次のナヌスケヌスを提䟛しおいたす。



 #include <stddef.h> #include <stdint.h> #include <windows.h> #pragma comment(lib, "advapi32.lib") int randombytes(unsigned char *out, size_t outlen) { static HCRYPTPROV handle = 0; /* only freed when program ends */ if(!handle) { if(!CryptAcquireContext(&handle, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { return -1; } } while(outlen > 0) { const DWORD len = outlen > 1048576UL ? 1048576UL : outlen; if(!CryptGenRandom(handle, len, out)) { return -2; } out += len; outlen -= len; } return 0; }
      
      





Windows XP以降での䜿甚に焊点を合わせおいる堎合、䞊蚘のCryptoAPIコヌドは| RtlGenRandom |



 #include <stdint.h> #include <stdio.h> #include <Windows.h> #define RtlGenRandom SystemFunction036 #if defined(__cplusplus) extern "C" #endif BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); #pragma comment(lib, "advapi32.lib") int main() { uint8_t buffer[32] = { 0 }; if (FALSE == RtlGenRandom(buffer, sizeof buffer)) return -1; for (size_t i = 0; i < sizeof buffer; ++i) printf("%02X ", buffer[i]); printf("\n"); return 0; }
      
      






All Articles