Implementações personalizadas da validação da integridade do arquivo de CloudTrail log - AWS CloudTrail

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Implementações personalizadas da validação da integridade do arquivo de CloudTrail log

Como CloudTrail usa algoritmos criptográficos e funções de hash padrão do setor e disponíveis abertamente, você pode criar suas próprias ferramentas para validar a integridade dos arquivos de log. CloudTrail Quando a validação da integridade do arquivo de log está habilitada, CloudTrail entrega os arquivos de resumo para o seu bucket do Amazon S3. Você pode usar esses arquivos para implementar sua própria solução de validação. Para obter mais informações sobre os arquivos de resumo, consulte Estrutura de arquivo de resumo do CloudTrail.

Este tópico descreve como os arquivos de resumo são assinados e explica detalhadamente as etapas que você precisará seguir para implementar uma solução que valida os arquivos de resumo e os arquivos de log aos quais eles fazem referência.

Entendendo como os arquivos de CloudTrail resumo são assinados

CloudTrail os arquivos de resumo são assinados com assinaturas digitais RSA. Para cada arquivo de resumo, o CloudTrail faz o seguinte:

  1. Cria uma string para assinatura de dados com base em campos de arquivos de resumo designados (descritos na próxima seção).

  2. Obtém uma chave privada exclusiva para a região.

  3. Transmite o hash SHA-256 da string e a chave privada ao algoritmo de assinatura RSA, que produz uma assinatura digital.

  4. Codifica o código de byte da assinatura em formato hexadecimal.

  5. Insere a assinatura digital na propriedade de metadados x-amz-meta-signature do objeto do arquivo de resumo do Amazon S3.

Conteúdo da string de assinatura de dados

Os seguintes CloudTrail objetos são incluídos na string para assinatura de dados:

  • O carimbo de data e hora final do arquivo de resumo no formato estendido UTC (por exemplo, 2015-05-08T07:19:37Z)

  • O caminho atual do arquivo de resumo do S3

  • O hash SHA-256 com codificação hexadecimal do arquivo de resumo atual

  • A assinatura com codificação hexadecimal do arquivo de resumo anterior

O formato para calcular essa string e uma string de exemplo são fornecidos posteriormente neste documento.

Etapas da implementação da validação personalizada

Ao implementar uma solução de validação personalizada, você precisará validar o arquivo de resumo primeiro e os arquivos de log aos quais ele faz referência.

Validar o arquivo de resumo

Para validar um arquivo de resumo, você precisa da assinatura dele, da chave pública cuja chave privada foi usada para assiná-lo e de uma string de assinatura de dados computada.

  1. Obtenha o arquivo de resumo.

  2. Verifique se o arquivo de resumo foi recuperado de seu local original.

  3. Obtenha a assinatura com codificação hexadecimal do arquivo de resumo.

  4. Obtenha a impressão digital com codificação hexadecimal da chave pública cuja chave privada foi usada para assinar o arquivo de resumo.

  5. Recupere as chaves públicas do período correspondente ao arquivo de resumo.

  6. Entre as chaves públicas recuperadas, escolha aquela cuja impressão digital corresponde à impressão digital do arquivo de resumo.

  7. Usando o hash do arquivo de resumo e outros campos de arquivos de resumo, recrie a string de assinatura de dados usada para verificar a assinatura do arquivo de resumo.

  8. Para validar a assinatura, transmita o hash SHA-256 da string, a chave pública e a assinatura como parâmetros ao algoritmo de verificação de assinatura RSA. Se o resultado for verdadeiro, o arquivo de resumo será válido.

Validar os arquivos de log

Se o arquivo de resumo for válido, valide cada um dos arquivos de log aos quais o arquivo de resumo faz referência.

  1. Para validar a integridade de um arquivo de log, compute o valor de hash SHA-256 dele em seu conteúdo não compactado e compare os resultados com o hash do arquivo de log gravado em formato hexadecimal na compilação. Se os hashes forem correspondentes, o arquivo de log será válido.

  2. Com as informações sobre o arquivo de resumo anterior que está incluído no arquivo de resumo atual, valide os arquivos de resumo anteriores e seus arquivos de log correspondentes de maneira consecutiva.

As seções a seguir descrevem essas etapas em detalhes.

A. Obter o arquivo de resumo

As primeiras etapas são: obter o arquivo de resumo mais recente, verificar se você o recuperou do local original dele, verificar sua assinatura digital e obter a impressão digital da chave pública.

  1. Usando S3 Get ou a classe AmazonS3Client (por exemplo), obtenha o arquivo de resumo mais recente do bucket do Amazon S3 para o período que você deseja validar.

  2. Verifique se o bucket e o objeto do S3 usados para recuperar o arquivo correspondem aos locais do bucket e do objeto do S3 que são registrados no próprio arquivo de resumo.

  3. Em seguida, obtenha a assinatura digital do arquivo de resumo da propriedade de metadados x-amz-meta-signature do objeto do arquivo de resumo no Amazon S3.

  4. No arquivo de resumo, obtenha a impressão digital da chave pública cuja chave privada foi usada para assinar o arquivo de resumo do campo digestPublicKeyFingerprint.

B. Recupere a chave pública para validar o arquivo de resumo

Para obter a chave pública para validar o arquivo de resumo, você pode usar a API AWS CLI ou a CloudTrail API. Em ambos os casos, você especifica um período (ou seja, um horário de início e de término) para os arquivos de resumo que você deseja validar. Uma ou mais chaves públicas podem ser retornadas para o período que você especificar. As chaves retornadas podem ter períodos de validade que se sobrepõem.

nota

Como CloudTrail usa diferentes pares de chaves privadas/públicas por região, cada arquivo de resumo é assinado com uma chave privada exclusiva para sua região. Portanto, quando você valida um arquivo de resumo de uma região específica, precisa recuperar a chave pública da mesma região.

Usar a AWS CLI para recuperar chaves públicas

Para recuperar chaves públicas de arquivos de resumo usando AWS CLI, use o comando cloudtrail list-public-keys. O comando tem o formato a seguir:

aws cloudtrail list-public-keys [--start-time <start-time>] [--end-time <end-time>]

Os parâmetros de horário de início e de término são carimbos de data e hora UTC opcionais. Se eles não forem especificados, a hora atual será usada, e a chave ou as chaves públicas atualmente ativas serão retornadas.

Exemplo de resposta

A resposta será uma lista de objetos JSON que representam a chave ou as chaves retornadas:

{ "publicKeyList": [ { "ValidityStartTime": "1436317441.0", "ValidityEndTime": "1438909441.0", "Value": "MIIBCgKCAQEAn11L2YZ9h7onug2ILi1MWyHiMRsTQjfWE+pHVRLk1QjfWhirG+lpOa8NrwQ/r7Ah5bNL6HepznOU9XTDSfmmnP97mqyc7z/upfZdS/AHhYcGaz7n6Wc/RRBU6VmiPCrAUojuSk6/GjvA8iOPFsYDuBtviXarvuLPlrT9kAd4Lb+rFfR5peEgBEkhlzc5HuWO7S0y+KunqxX6jQBnXGMtxmPBPP0FylgWGNdFtks/4YSKcgqwH0YDcawP9GGGDAeCIqPWIXDLG1jOjRRzWfCmD0iJUkz8vTsn4hq/5ZxRFE7UBAUiVcGbdnDdvVfhF9C3dQiDq3k7adQIziLT0cShgQIDAQAB", "Fingerprint": "8eba5db5bea9b640d1c96a77256fe7f2" }, { "ValidityStartTime": "1434589460.0", "ValidityEndTime": "1437181460.0", "Value": "MIIBCgKCAQEApfYL2FiZhpN74LNWVUzhR+VheYhwhYm8w0n5Gf6i95ylW5kBAWKVEmnAQG7BvS5g9SMqFDQx52fW7NWV44IvfJ2xGXT+wT+DgR6ZQ+6yxskQNqV5YcXj4Aa5Zz4jJfsYjDuO2MDTZNIzNvBNzaBJ+r2WIWAJ/Xq54kyF63B6WE38vKuDE7nSd1FqQuEoNBFLPInvgggYe2Ym1Refe2z71wNcJ2kY+q0h1BSHrSM8RWuJIw7MXwF9iQncg9jYzUlNJomozQzAG5wSRfbplcCYNY40xvGd/aAmO0m+Y+XFMrKwtLCwseHPvj843qVno6x4BJN9bpWnoPo9sdsbGoiK3QIDAQAB", "Fingerprint": "8933b39ddc64d26d8e14ffbf6566fee4" }, { "ValidityStartTime": "1434589370.0", "ValidityEndTime": "1437181370.0", "Value": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqlzPJbvZJ42UdcmLfPUqXYNfOs6I8lCfao/tOs8CmzPOEdtLWugB9xoIUz78qVHdKIqxbaG4jWHfJBiOSSFBM0lt8cdVo4TnRa7oG9io5pysS6DJhBBAeXsicufsiFJR+wrUNh8RSLxL4k6G1+BhLX20tJkZ/erT97tDGBujAelqseGg3vPZbTx9SMfOLN65PdLFudLP7Gat0Z9p5jw/rjpclKfo9Bfc3heeBxWGKwBBOKnFAaN9V57pOaosCvPKmHd9bg7jsQkI9Xp22IzGLsTFJZYVA3KiTAElDMu80iFXPHEq9hKNbt9e4URFam+1utKVEiLkR2disdCmPTK0VQIDAQAB", "Fingerprint": "31e8b5433410dfb61a9dc45cc65b22ff" } ] }

Use a CloudTrail API para recuperar chaves públicas

Para recuperar chaves públicas para arquivos de resumo usando a CloudTrail API, transmita os valores de hora de início e hora de término para a ListPublicKeys API. A API ListPublicKeys retorna as chaves públicas cujas chaves privadas foram usadas para assinar arquivos de resumo dentro do período especificado. Para cada chave pública, a API também retorna a impressão digital correspondente.

ListPublicKeys

Esta seção descreve os parâmetros de solicitação e os elementos de resposta da API ListPublicKeys.

nota

A codificação dos campos binários de ListPublicKeys está sujeita a alterações.

Parâmetros de solicitação

Nome Descrição
StartTime

Opcionalmente, especifica, em UTC, o início do intervalo de tempo para pesquisar chaves públicas para arquivos de resumo. CloudTrail Se não StartTime for especificado, a hora atual será usada e a chave pública atual será retornada.

Tipo: DateTime

EndTime

Opcionalmente, especifica, em UTC, o final do intervalo de tempo para pesquisar chaves públicas para arquivos de resumo. CloudTrail Se não EndTime for especificado, a hora atual será usada.

Tipo: DateTime

Elementos de resposta

PublicKeyList, um conjunto de PublicKey objetos que contém:

Name (Nome) Descrição
Value

O valor de chave pública codificado DER no formato PKCS #1.

Tipo: Blob

ValidityStartTime

O horário de início da validade da chave pública.

Tipo: DateTime

ValidityEndTime

O horário de término da validade da chave pública.

Tipo: DateTime

Fingerprint

A impressão digital da chave pública. A impressão digital pode ser usada para identificar a chave pública que você precisa usar para validar o arquivo de resumo.

Tipo: String

C. Escolha a chave pública a ser usada para a validação

Entre as chaves públicas recuperadas por list-public-keys ou ListPublicKeys, escolha a chave retornada cuja impressão digital corresponde à impressão digital gravada no campo digestPublicKeyFingerprint do arquivo de resumo. Esta é a chave pública que você usará para validar o arquivo de resumo.

D. Recrie a string de assinatura de dados

Agora que você tem a assinatura do arquivo de resumo e a chave pública associada, precisa calcular a string de assinatura de dados. Depois que você calcular a string de assinatura de dados, terá o necessário para verificar a assinatura.

A string de assinatura de dados tem o seguinte formato:

Data_To_Sign_String = Digest_End_Timestamp_in_UTC_Extended_format + '\n' + Current_Digest_File_S3_Path + '\n' + Hex(Sha256(current-digest-file-content)) + '\n' + Previous_digest_signature_in_hex

Veja a seguir um exemplo de Data_To_Sign_String.

2015-08-12T04:01:31Z S3-bucket-name/AWSLogs/111122223333/CloudTrail-Digest/us-east-2/2015/08/12/111122223333_us-east-2_CloudTrail-Digest_us-east-2_20150812T040131Z.json.gz 4ff08d7c6ecd6eb313257e839645d20363ee3784a2328a7d76b99b53cc9bcacd 6e8540b83c3ac86a0312d971a225361d28ed0af20d70c211a2d405e32abf529a8145c2966e3bb47362383a52441545ed091fb81 d4c7c09dd152b84e79099ce7a9ec35d2b264eb92eb6e090f1e5ec5d40ec8a0729c02ff57f9e30d5343a8591638f8b794972ce15bb3063a01972 98b0aee2c1c8af74ec620261529265e83a9834ebef6054979d3e9a6767dfa6fdb4ae153436c567d6ae208f988047ccfc8e5e41f7d0121e54ed66b1b904f80fb2ce304458a2a6b91685b699434b946c52589e9438f8ebe5a0d80522b2f043b3710b87d2cda43e5c1e0db921d8d540b9ad5f6d4$31b1f4a8ef2d758424329583897339493a082bb36e782143ee5464b4e3eb4ef6

Depois que você recriar essa string, poderá validar o arquivo de resumo.

E. Valide o arquivo de resumo

Transmita o hash SHA-256 da string de assinatura de dados recriada, a assinatura digital e a chave pública ao algoritmo de verificação de assinatura RSA. Se o resultado for verdadeiro, a assinatura do arquivo de resumo será verificada, e o arquivo de resumo será válido.

F. Valide os arquivos de log

Depois que você validar o arquivo de resumo, poderá validar os arquivos de log aos quais ele faz referência. O arquivo de resumo contém os hashes SHA-256 dos arquivos de log. Se um dos arquivos de log for modificado após a CloudTrail entrega, os hashes SHA-256 serão alterados e a assinatura do arquivo de resumo não corresponderá.

Veja a seguir como validar os arquivos de log:

  1. Faça um S3 Get do arquivo de log usando as informações de local do S3 nos campos logFiles.s3Bucket e logFiles.s3Object do arquivo de resumo.

  2. Se a operação S3 Get for bem-sucedida, percorra os arquivos de log listados no conjunto de arquivos de log seguindo estas etapas:

    1. Recupere o hash original do arquivo no campo logFiles.hashValue do log correspondente no arquivo de resumo.

    2. Faça hash do conteúdo descompactado do arquivo de log com o algoritmo de hashing especificado em logFiles.hashAlgorithm.

    3. Compare o valor de hash que você gerou com o valor do log no arquivo de resumo. Se os hashes forem correspondentes, o arquivo de log será válido.

G. Valide arquivos de log e de compilação adicionais

Em cada arquivo de resumo, os campos a seguir fornecem o local e a assinatura do arquivo de resumo anterior:

  • previousDigestS3Bucket

  • previousDigestS3Object

  • previousDigestSignature

Use essas informações para acessar os arquivos de resumo anteriores em sequência, validando a assinatura de cada um deles e os arquivos de log aos quais eles fazem referência seguindo as etapas das seções anteriores. A única diferença é que, para os arquivos de resumo anteriores, você não precisa recuperar a assinatura digital das propriedades de metadados do Amazon S3 do objeto do arquivo de resumo. A assinatura do arquivo de resumo anterior é fornecida a você no campo previousDigestSignature.

Você pode voltar até que o arquivo de resumo inicial seja atingido ou até que a cadeia de arquivos de resumo seja interrompida, o que ocorrer primeiro.

Validar arquivos de log e de resumo offline

Ao validar os arquivos de log e de compilação offline, você pode seguir os procedimentos descritos nas seções anteriores. No entanto, é necessário levar em conta as seguintes áreas:

Processar o arquivo de resumo mais recente

A assinatura digital do arquivo de resumo mais recente (ou seja, "atual") está nas propriedades de metadados do Amazon S3 do objeto do arquivo de resumo. Em um cenário offline, a assinatura digital do arquivo de resumo atual não é disponibilizada.

Veja a seguir duas possíveis maneiras de fazer isso:

  • Como a assinatura digital do arquivo de resumo anterior está no arquivo de resumo atual, comece a validar a partir do next-to-last arquivo de resumo. Com esse método, não é possível validar o arquivo de resumo mais recente.

  • Como etapa preliminar, obtenha a assinatura do arquivo de resumo atual das propriedades de metadados do objeto do arquivo de resumo e armazene-a offline com segurança. Isso permite que o arquivo de resumo atual seja validado, além dos arquivos anteriores da cadeia.

Resolução de caminho

Os campos nos arquivos de resumo obtidos por download, como s3Object e previousDigestS3Object, ainda apontarão para os locais online do Amazon S3 dos arquivos de log e de resumo. Uma solução offline precisa encontrar uma maneira de redirecioná-los para o caminho atual dos arquivos de log e de compilação baixados.

Chaves públicas

Para fazer a validação offline, todas as chaves públicas de que você precisa para validar os arquivos de log em um determinado período precisam primeiro ser obtidas online (ao chamar ListPublicKeys, por exemplo) e, depois, armazenadas offline com segurança. Essa etapa precisará ser repetida sempre que você quiser validar arquivos adicionais fora do período inicial que especificou.

Exemplo de snippet de validação

O trecho de amostra a seguir fornece código básico para validar arquivos de CloudTrail resumo e log. O código esqueleto pode ser online ou offline, ou seja, você decide se o implementará com ou sem conectividade online na AWS. A implementação sugerida usa Java Cryptography Extension (JCE) e Bouncy Castle como um provedor de segurança.

O exemplo de snippet mostra:

  • Como criar a string de assinatura de dados usada para validar a assinatura do arquivo de resumo.

  • Como verificar a assinatura do arquivo de resumo.

  • Como verificar os hashes do arquivo de log.

  • Uma estrutura de código para validar uma cadeia de arquivos de resumo.

import java.util.Arrays; import java.security.MessageDigest; import java.security.KeyFactory; import java.security.PublicKey; import java.security.Security; import java.security.Signature; import java.security.spec.X509EncodedKeySpec; import org.json.JSONObject; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.apache.commons.codec.binary.Hex; public class DigestFileValidator { public void validateDigestFile(String digestS3Bucket, String digestS3Object, String digestSignature) { // Using the Bouncy Castle provider as a JCE security provider - http://www.bouncycastle.org/ Security.addProvider(new BouncyCastleProvider()); // Load the digest file from S3 (using Amazon S3 Client) or from your local copy JSONObject digestFile = loadDigestFileInMemory(digestS3Bucket, digestS3Object); // Check that the digest file has been retrieved from its original location if (!digestFile.getString("digestS3Bucket").equals(digestS3Bucket) || !digestFile.getString("digestS3Object").equals(digestS3Object)) { System.err.println("Digest file has been moved from its original location."); } else { // Compute digest file hash MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); messageDigest.update(convertToByteArray(digestFile)); byte[] digestFileHash = messageDigest.digest(); messageDigest.reset(); // Compute the data to sign String dataToSign = String.format("%s%n%s/%s%n%s%n%s", digestFile.getString("digestEndTime"), digestFile.getString("digestS3Bucket"), digestFile.getString("digestS3Object"), // Constructing the S3 path of the digest file as part of the data to sign Hex.encodeHexString(digestFileHash), digestFile.getString("previousDigestSignature")); byte[] signatureContent = Hex.decodeHex(digestSignature); /* NOTE: To find the right public key to verify the signature, call CloudTrail ListPublicKey API to get a list of public keys, then match by the publicKeyFingerprint in the digest file. Also, the public key bytes returned from ListPublicKey API are DER encoded in PKCS#1 format: PublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, PublicKey BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL } */ pkcs1PublicKeyBytes = getPublicKey(digestFile.getString("digestPublicKeyFingerprint"))); // Transform the PKCS#1 formatted public key to x.509 format. RSAPublicKey rsaPublicKey = RSAPublicKey.getInstance(pkcs1PublicKeyBytes); AlgorithmIdentifier rsaEncryption = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null); SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(rsaEncryption, rsaPublicKey); // Create the PublicKey object needed for the signature validation PublicKey publicKey = KeyFactory.getInstance("RSA", "BC").generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded())); // Verify signature Signature signature = Signature.getInstance("SHA256withRSA", "BC"); signature.initVerify(publicKey); signature.update(dataToSign.getBytes("UTF-8")); if (signature.verify(signatureContent)) { System.out.println("Digest file signature is valid, validating log files…"); for (int i = 0; i < digestFile.getJSONArray("logFiles").length(); i++) { JSONObject logFileMetadata = digestFile.getJSONArray("logFiles").getJSONObject(i); // Compute log file hash byte[] logFileContent = loadUncompressedLogFileInMemory( logFileMetadata.getString("s3Bucket"), logFileMetadata.getString("s3Object") ); messageDigest.update(logFileContent); byte[] logFileHash = messageDigest.digest(); messageDigest.reset(); // Retrieve expected hash for the log file being processed byte[] expectedHash = Hex.decodeHex(logFileMetadata.getString("hashValue")); boolean signaturesMatch = Arrays.equals(expectedHash, logFileHash); if (!signaturesMatch) { System.err.println(String.format("Log file: %s/%s hash doesn't match.\tExpected: %s Actual: %s", logFileMetadata.getString("s3Bucket"), logFileMetadata.getString("s3Object"), Hex.encodeHexString(expectedHash), Hex.encodeHexString(logFileHash))); } else { System.out.println(String.format("Log file: %s/%s hash match", logFileMetadata.getString("s3Bucket"), logFileMetadata.getString("s3Object"))); } } } else { System.err.println("Digest signature failed validation."); } System.out.println("Digest file validation completed."); if (chainValidationIsEnabled()) { // This enables the digests' chain validation validateDigestFile( digestFile.getString("previousDigestS3Bucket"), digestFile.getString("previousDigestS3Object"), digestFile.getString("previousDigestSignature")); } } } }