Saltar a contenido

Cryptographic Vulnerability: Insecure Algorithm

Vulnerabilidad criptográfica: Algoritmo inseguro

Descripción

En criptografía, un cifrado es un algoritmo para realizar el cifrado o descifrado: una serie de pasos que se pueden seguir como un procedimiento.

Generalmente hay dos familias:

  • Block Cipher (Cifrado de bloque): Un cifrado de bloque descompone los mensajes de texto claro en bloques de tamaño fijo antes de convertirlos en texto cifrado utilizando una clave.
  • Stream Cipher (Cifrado de flujo): Un cifrado de flujo, por otro lado, descompone un mensaje de texto claro en bits individuales, que luego son convertidos individualmente a texto cifrado utilizando bits de clave.

Algoritmos de cifrado comunes:

  • DES y Triple DES: Triple DES fue diseñado para reemplazar el algoritmo original Data Encryption Standard (DES), que los atacantes finalmente aprendieron a derrotar con relativa facilidad. En un momento dado, Triple DES fue el estándar recomendado y el algoritmo simétrico más utilizado en la industria. Triple DES utiliza tres claves individuales con 56 bits cada una. La longitud total de la clave suma 168 bits, pero los expertos argumentarían que 112 bits en la fuerza de la clave es más preciso. A pesar de estar siendo eliminado gradualmente, Triple DES ha sido, en su mayor parte, reemplazado por el Advanced Encryption Standard (AES).
  • AES: El Advanced Encryption Standard (AES) es el algoritmo confiable como estándar por el Gobierno de los EE. UU. y numerosas organizaciones. Aunque es muy eficiente en formato de 128 bits, AES también utiliza claves de 192 y 256 bits para fines de cifrado intensivo. AES se considera en gran medida invulnerable a todos los ataques, excepto a la fuerza bruta, que intenta descifrar mensajes utilizando todas las combinaciones posibles en el cifrado de 128, 192 o 256 bits.
  • RSA: RSA es un algoritmo de cifrado de clave pública y el estándar para cifrar los datos enviados a través de Internet. También resulta ser uno de los métodos utilizados en los programas PGP y GPG. A diferencia de Triple DES, RSA se considera un algoritmo asimétrico debido al uso de un par de claves. Usted tiene su clave pública para cifrar el mensaje y una clave privada para para descifrarlo. El resultado del cifrado RSA es un enorme lote de galimatías que a los atacantes les lleva mucho tiempo y potencia de procesamiento para romper.
  • ECC: La Elliptic Curve Cryptography (ECC) es una técnica basada en claves para cifrar datos. ECC se centra en pares de claves públicas y privadas para el descifrado y cifrado del tráfico web. ECC se discute con frecuencia en el contexto del el algoritmo criptográfico Rivest–Shamir–Adleman (RSA). RSA logra el cifrado unidireccional de cosas como correos electrónicos, datos y software utilizando la factorización de números primos.

DES y Triple DES son inseguros y no deben utilizarse. Otros algoritmos sufren de algunas debilidades causadas por configuraciones de modo de cifrado inseguras, el tamaño de la clave o casos de clave especiales.

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:crypto/crypto.dart';
import 'package:pointycastle/export.dart' as pc;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Insecure Encryption Demo'),
        ),
        body: EncryptionWidget(),
      ),
    );
  }
}

class EncryptionWidget extends StatefulWidget {
  @override
  _EncryptionWidgetState createState() => _EncryptionWidgetState();
}

class _EncryptionWidgetState extends State<EncryptionWidget> {
  final _controller = TextEditingController();
  String _encryptedText = '';

  void _encryptText() {
    final key = utf8.encode('insecurekey');
    final iv = Uint8List(8);
    final s = pc.SICStreamCipher(pc.DESEngine())
      ..init(true, pc.ParametersWithIV(pc.KeyParameter(key), iv));
    final input = utf8.encode(_controller.text);
    final output = s.process(input);
    setState(() {
      _encryptedText = base64.encode(output);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        TextField(
          controller: _controller,
          decoration: InputDecoration(
            labelText: 'Enter text to encrypt',
          ),
        ),
        RaisedButton(
          onPressed: _encryptText,
          child: Text('Encrypt'),
        ),
        Text('Encrypted text: $_encryptedText'),
      ],
    );
  }
}
import Foundation
import CommonCrypto

func DES_Encrypt(input: String, key: String) -> String {
    let data = input.data(using: String.Encoding.utf8)!
    let keyData = key.data(using: String.Encoding.utf8)!
    let keyBytes = keyData.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> UnsafePointer<UInt8> in
        return bytes
    }
    let dataLength = Int(data.count)
    let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: dataLength + kCCBlockSizeDES)
    let bufferPtr = UnsafeMutableRawPointer(buffer)
    let bufferPtrBytes = bufferPtr.bindMemory(to: Void.self, capacity: dataLength)
    let iv = [UInt8](repeating: 0, count: kCCBlockSizeDES)
    var numBytesEncrypted :size_t = 0
    let cryptStatus = CCCrypt(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmDES), CCOptions(kCCOptionPKCS7Padding), keyBytes, kCCKeySizeDES, iv, data.bytes, dataLength, bufferPtrBytes, dataLength + kCCBlockSizeDES, &numBytesEncrypted)
    if UInt32(cryptStatus) == UInt32(kCCSuccess) {
        let encryptedData = Data(bytes: UnsafePointer<UInt8>(buffer), count: numBytesEncrypted)
        buffer.deallocate()
        return encryptedData.base64EncodedString()
    } else {
        buffer.deallocate()
        return ""
    }
}

func main() {
    print("Enter text to encrypt:")
    let input = readLine() ?? ""
    print("Enter encryption key:")
    let key = readLine() ?? ""
    let encrypted = DES_Encrypt(input: input, key: key)
    print("Encrypted text: \(encrypted)")
}

main()
import javax.crypto.Cipher
import javax.crypto.SecretKey
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.DESKeySpec
import java.util.*

fun main(args: Array<String>) {
    val scanner = Scanner(System.`in`)
    println("Enter text to encrypt:")
    val text = scanner.nextLine()
    println("Enter DES key:")
    val key = scanner.nextLine()

    val encryptedText = encrypt(text, key)
    println("Encrypted text: $encryptedText")

    val decryptedText = decrypt(encryptedText, key)
    println("Decrypted text: $decryptedText")
}

fun encrypt(text: String, key: String): String {
    val desKey = SecretKeyFactory.getInstance("DES").generateSecret(DESKeySpec(key.toByteArray()))
    val cipher = Cipher.getInstance("DES")
    cipher.init(Cipher.ENCRYPT_MODE, desKey)
    return Base64.getEncoder().encodeToString(cipher.doFinal(text.toByteArray()))
}

fun decrypt(text: String, key: String): String {
    val desKey = SecretKeyFactory.getInstance("DES").generateSecret(DESKeySpec(key.toByteArray()))
    val cipher = Cipher.getInstance("DES")
    cipher.init(Cipher.DECRYPT_MODE, desKey)
    return String(cipher.doFinal(Base64.getDecoder().decode(text)))
}

Recomendación

Para un cifrado seguro y confiable, es crucial utilizar algoritmos criptográficos modernos y robustos que se hayan sometido a un escrutinio exhaustivo y sean ampliamente adoptados por la industria. Dos alternativas recomendadas a los inseguros DES y Triple DES son el Advanced Encryption Standard (AES) y la Elliptic Curve Cryptography (ECC).

AES es ampliamente reconocido como un algoritmo de cifrado simétrico altamente eficiente y seguro. Ofrece longitudes de clave de 128, 192 y 256 bits, lo que lo hace adecuado para varios propósitos de cifrado. AES ha demostrado ser resistente a la mayoría de los ataques conocidos, por lo que es una excelente opción para la protección de datos.

ECC, por otro lado, es una técnica de cifrado asimétrico que utiliza pares de claves públicas y privadas para el cifrado y descifrado. ECC es conocida por proporcionar una seguridad sólida con longitudes de clave más cortas en comparación con otros algoritmos asimétricos como RSA. Esto da como resultado procesos de cifrado y descifrado más rápidos mientras se mantiene un alto nivel de seguridad.

Para garantizar la máxima seguridad al utilizar cualquier algoritmo criptográfico, es fundamental seguir las mejores prácticas:

  • Longitud de clave: Utilice longitudes de clave que se consideren seguras. Para AES, 128 bits son suficientes para la mayoría de los casos de uso, pero puede optar por 192 o 256 bits para datos más confidenciales.

  • Modo de cifrado: Emplee siempre configuraciones de modo de cifrado seguro, como AES-GCM (Galois/Counter Mode) o AES-CBC (Cipher Block Chaining) con el relleno (padding) adecuado.

  • Gestión de claves: Utilice mecanismos de almacenamiento seguro (Keystore para Android, Keychain para iOS) para almacenar de forma segura las claves criptográficas.

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pointycastle/export.dart' as pc;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Secure Encryption Demo'),
        ),
        body: EncryptionWidget(),
      ),
    );
  }
}

class EncryptionWidget extends StatefulWidget {
  @override
  _EncryptionWidgetState createState() => _EncryptionWidgetState();
}

class _EncryptionWidgetState extends State<EncryptionWidget> {
  final _controller = TextEditingController();
  String _encryptedText = '';

  void _encryptText() {
    final key = pc.KeyParameter(Uint8List.fromList(utf8.encode('securekey123456789012345678901234'))); // 32 bytes for AES-256
    final iv = Uint8List(12); // 12 bytes for GCM IV
    final cipher = pc.GCMBlockCipher(pc.AESFastEngine())
      ..init(true, pc.AEADParameters(key, 128, iv));
    final input = utf8.encode(_controller.text);
    final output = cipher.process(Uint8List.fromList(input));
    setState(() {
      _encryptedText = base64.encode(output);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        TextField(
          controller: _controller,
          decoration: InputDecoration(
            labelText: 'Enter text to encrypt',
          ),
        ),
        RaisedButton(
          onPressed: _encryptText,
          child: Text('Encrypt'),
        ),
        Text('Encrypted text: $_encryptedText'),
      ],
    );
  }
}
import Foundation
import CryptoKit

func AES_GCM_Encrypt(input: String, key: String) -> String {
    guard let data = input.data(using: .utf8),
          let keyData = key.data(using: .utf8) else {
        return ""
    }

    let iv = Data(count: AES.GCM.nonceSize)

    do {
        let sealedData = try! AES.GCM.seal(plainData!, using: key, nonce: AES.GCM.Nonce(data:nonce!))
        let encryptedContent = try! sealedData.combined!
        return sealedData.ciphertext.base64EncodedString()
    } catch {
        return ""
    }
}

print("Enter text to encrypt:")
if let input = readLine() {
    print("Enter encryption key:")
    if let key = readLine() {
        let encrypted = AES_GCM_Encrypt(input: input, key: key)
        print("Encrypted text: \(encrypted)")
    }
}
import java.security.SecureRandom
import javax.crypto.Cipher
import javax.crypto.SecretKey
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
import java.security.spec.KeySpec
import java.util.*

fun main(args: Array<String>) {
    val scanner = Scanner(System.`in`)
    println("Enter text to encrypt:")
    val text = scanner.nextLine()
    println("Enter AES key:")
    val key = scanner.nextLine()

    val salt = ByteArray(16)
    SecureRandom().nextBytes(salt)
    val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
    val spec: KeySpec = PBEKeySpec(key.toCharArray(), salt, 65536, 256)
    val secretKey = factory.generateSecret(spec)
    val secretKeySpec = SecretKeySpec(secretKey.encoded, "AES")

    val encryptedText = encrypt(text, secretKeySpec)
    println("Encrypted text: $encryptedText")

    val decryptedText = decrypt(encryptedText, secretKeySpec)
    println("Decrypted text: $decryptedText")
}

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

fun decrypt(text: String, key: SecretKey): String {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    val decodedText = Base64.getDecoder().decode(text)
    val iv = decodedText.copyOfRange(0, 12)
    val encryptedBytes = decodedText.copyOfRange(12, decodedText.size)
    val gcmSpec = GCMParameterSpec(128, iv)
    cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec)
    return String(cipher.doFinal(encryptedBytes))
}

Enlaces

Estándares

  • 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_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