コンテンツにスキップ

Cryptographic Vulnerability: Insecure mode

暗号化の脆弱性: 安全でない暗号利用モード

説明

ブロック暗号は、固定サイズのデータブロックを処理します。通常、メッセージのサイズはブロックサイズよりも大きくなります。そのため、長いメッセージは一連の連続したメッセージブロックに分割され、暗号はこれらのブロックに対して一度に1つずつ処理を行います。

一般的なブロック暗号の利用モードは以下の通りです。

  • ECB (Electronic Code Book): 連続してリストされた一連のメッセージブロックを処理するための最も簡単な方法です。ユーザーは最初の平文ブロックを受け取り、キーで暗号化して最初の暗号文ブロックを生成します。次に、2番目の平文ブロックを受け取り、同じキーで同じプロセスを実行し、これを繰り返します。このモードは安全ではないため、使用しないでください
  • CBC (Cipher Block Chaining): 暗号文を生成するためのメッセージ依存性を提供し、システムを非決定論的なものにします。CBCモードでは、現在の平文ブロックは前の暗号文ブロックと加算され、その結果がキーで暗号化されます。したがって、復号化は逆のプロセスであり、現在の暗号文を復号化してから、前の暗号文ブロックを結果に加算します。
  • CFB (Cipher Feedback): 次の平文ブロックを暗号化するために、各暗号文ブロックが暗号化プロセスに「フィードバック」されます。CFBモードでは、最初のランダムなnビットの入力ブロックとして初期化ベクトル(IV)が必要です。IVは秘密である必要はありません。CFBモードはECBモードとは大きく異なり、特定の平文ブロックに対応する暗号文は、その平文ブロックとキーだけでなく、前の暗号文ブロックにも依存します。言い換えれば、暗号文ブロックはメッセージに依存します。
  • OFB (Output Feedback): 基盤となるブロック暗号からの連続した出力ブロックをブロック暗号にフィードバックします。これらのフィードバックブロックは、CFBモードの場合と同様に、キーストリームジェネレーターとして機能する暗号化アルゴリズムに供給するビット列を提供します。生成されたキーストリームは平文ブロックとXOR演算されます。OFBモードでは、最初のランダムなnビットの入力ブロックとしてIVが必要です。IVは秘密である必要はありません。
  • CTR (Counter): フィードバックのないCFBモードのカウンターベースのバージョンと見なすことができます。このモードでは、送信者と受信者の両方が信頼できるカウンターにアクセスする必要があり、暗号文ブロックが交換されるたびに新しい共有値を計算します。この共有カウンターは必ずしも秘密の値である必要はありませんが、両者がカウンターの同期を維持する必要があるという課題があります。

ほとんどの実装では、明示的に指定されていない場合、安全ではない ECB モードがデフォルトで使用されます。データの暗号化に使用される動作モードに脆弱性があるため、安全なものに置き換える必要があります。

推奨事項

AES暗号化を使用する場合、暗号化されたデータの機密性と整合性を確保するために、適切な動作モードを選択することが重要です。モードの選択は、特定のユースケースとセキュリティ要件に依存する可能性があります。以下は、検討すべき一般的な暗号化モードです。

  • Electronic Codebook (ECB): これは最も単純なモードであり、平文をブロックに分割し、各ブロックを同じキーを使用して個別に暗号化します。ただし、同一の平文ブロックは同一の暗号文ブロックを生成するため、このモードは攻撃に対して脆弱です。
  • Cipher Block Chaining (CBC): このモードでは、各平文ブロックは暗号化される前に、前の暗号文ブロックとXOR演算されます。これによりランダム性が追加され、暗号化されたデータのパターンを特定することがより困難になります。ただし、同じIV(初期化ベクトル)が複数回使用されたり、ランダムでない値に初期化されたりすると、このモードは攻撃に対して脆弱になります。
  • Counter (CTR): このモードでは、カウンター値を暗号化し、平文とXOR演算して暗号文を生成します。並列暗号化および復号化が可能なため、ストリーミングアプリケーションで人気のあるモードです。ただし、一意のカウンター値が必要であり、攻撃者がカウンター値を推測できる場合は脆弱になる可能性があります。
  • Galois/Counter Mode (GCM): このモードは、CTRモードとメッセージ認証コード(MAC)の組み合わせを使用することにより、機密性と整合性の両方の保護を提供します。一意のIVを使用し、リプレイ攻撃を防ぐためにシーケンス番号などの追加データを組み込みます。このモードは、機密性と整合性の両方の保護を必要とするアプリケーションで一般的に使用されます。

一般に、機密性と整合性の両方の保護を提供するため、AES 暗号化には GCM モードを使用することをお勧めします。ただし、セキュリティを確保するためには、キーとIVを適切に管理することが重要です。

GCM などの認証付き暗号化モードを使用することで、パディングオラクル攻撃などの攻撃を阻止することができます。

import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
import java.security.SecureRandom
import java.util.Base64

fun encrypt(plaintext: String, key: ByteArray): String {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    val random = SecureRandom()
    val iv = ByteArray(12)
    random.nextBytes(iv)
    val ivSpec = GCMParameterSpec(128, iv)
    val secretKey = SecretKeySpec(key, "AES")
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec)
    val encryptedBytes = cipher.doFinal(plaintext.toByteArray())
    val combined = ByteArray(iv.size + encryptedBytes.size)
    System.arraycopy(iv, 0, combined, 0, iv.size)
    System.arraycopy(encryptedBytes, 0, combined, iv.size, encryptedBytes.size)
    return Base64.getEncoder().encodeToString(combined)
}

fun decrypt(ciphertext: String, key: ByteArray): String {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    val combined = Base64.getDecoder().decode(ciphertext)
    val iv = combined.copyOfRange(0, 12)
    val ivSpec = GCMParameterSpec(128, iv)
    val secretKey = SecretKeySpec(key, "AES")
    cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec)
    val decryptedBytes = cipher.doFinal(combined.copyOfRange(12, combined.size))
    return String(decryptedBytes)
}

fun main() {
    val key = "01234567890123456789012345678901".toByteArray()
    val plaintext = "Hello, world!"
    val encryptedText = encrypt(plaintext, key)
    println("Encrypted: $encryptedText")
    val decryptedText = decrypt(encryptedText, key)
    println("Decrypted: $decryptedText")
}
import Foundation
import CryptoKit

func encrypt(plaintext: String, key: SymmetricKey) throws -> Data {
    let sealedBox = try AES.GCM.seal(plaintext.data(using: .utf8)!, using: key)
    return sealedBox.combined!
}

func decrypt(ciphertext: Data, key: SymmetricKey) throws -> String {
    let sealedBox = try AES.GCM.SealedBox(combined: ciphertext)
    let decryptedData = try AES.GCM.open(sealedBox, using: key)
    return String(data: decryptedData, encoding: .utf8)!
}

func main() {
    let key = SymmetricKey(size: .bits256)
    let plaintext = "Hello, world!"
    let encryptedData = try! encrypt(plaintext: plaintext, key: key)
    print("Encrypted: \(encryptedData.base64EncodedString())")
    let decryptedText = try! decrypt(ciphertext: encryptedData, key: key)
    print("Decrypted: \(decryptedText)")
}

main()
import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';

Uint8List encrypt(Uint8List plaintext, Uint8List key) {
  final cipher = GCMBlockCipher(AESFastEngine());
  final parameters = AEADParameters(KeyParameter(key), 128, Uint8List(12));
  cipher.init(true, parameters);
  final encrypted = cipher.process(plaintext);
  final Uint8List iv = parameters.nonce;
  final combined = Uint8List(iv.length + encrypted.length);
  combined.setRange(0, iv.length, iv);
  combined.setRange(iv.length, combined.length, encrypted);
  return combined;
}

Uint8List decrypt(Uint8List ciphertext, Uint8List key) {
  final cipher = GCMBlockCipher(AESFastEngine());
  final parameters = AEADParameters(KeyParameter(key), 128, Uint8List(12));
  cipher.init(false, parameters);
  final Uint8List iv = ciphertext.sublist(0, 12);
  final encrypted = ciphertext.sublist(12);
  final decrypted = cipher.process(Uint8List.fromList(encrypted));
  return decrypted;
}

void main() {
  final key = Uint8List.fromList(List.generate(32, (index) => index)); // Your 256-bit key
  final plaintext = 'Hello, world!';
  final plaintextBytes = Uint8List.fromList(utf8.encode(plaintext));
  final encrypted = encrypt(plaintextBytes, key);
  print('Encrypted: ${base64Encode(encrypted)}');
  final decrypted = decrypt(encrypted, key);
  print('Decrypted: ${utf8.decode(decrypted)}');
}

リンク

標準

  • OWASP_MASVS_L1:
    • MSTG_CRYPTO_2
    • MSTG_CRYPTO_3
    • MSTG_CRYPTO_4
  • OWASP_MASVS_L2:
    • MSTG_CRYPTO_2
    • MSTG_CRYPTO_3
    • MSTG_CRYPTO_4
  • PCI_STANDARDS:
    • REQ_2_2
    • REQ_3_6
    • REQ_3_7
    • REQ_4_2
    • REQ_6_2
  • OWASP_MASVS_v2_1:
    • MASVS_CRYPTO_1
    • MASVS_CRYPTO_2
  • SOC2_CONTROLS:
    • CC_2_1
    • CC_4_1
    • CC_6_7
    • CC_7_1
    • CC_7_2
    • CC_7_4
    • CC_7_5
  • CNIL_FOR_DEVELOPERS:
    • DEVELOPERS_4_1_4
  • HIPAA_CONTROLS:
    • SECURITY252
    • SECURITY212
    • SECURITY213