AMP

Proteja o conteúdo de suas assinaturas com criptografia lado-cliente

Se você for uma publicação on-line, você provavelmente depende de assinantes para obter receita. Você talvez bloqueie o conteúdo premium atrás de um paywall no cliente usando obfuscação de CSS (display: none).

Infelizmente, pessoas mais experientes em tecnologia podem facilmente contornar esse bloqueio.

Como alternativa, você pode estar mostrando aos usuários um documento que não possui nenhum conteúdo premium e mostrar uma página totalmente nova apenas quando o back-end validar o usuário. Embora esta seja uma solução mais segura, é um método que custa tempo, recursos e felicidade do usuário.

Resolva esses dois problemas implementando a validação de assinante premium e a descriptografia de conteúdo no lado do cliente. Com esta solução, usuários com acesso premium poderão descriptografar conteúdo sem precisar carregar uma nova página ou esperar a resposta do back-end!

Visão geral da configuração

Para implementar a descriptografia do lado do cliente, você combinará criptografia de chave simétrica com criptografia de chave pública da seguinte maneira:

  1. Crie uma chave simétrica aleatória para cada documento, concedendo a cada documento uma chave unívoca.
  2. Criptografe o conteúdo premium com a chave simétrica do documento.
    A chave é simétrica para permitir que a mesma chave criptografe e descriptografe o conteúdo.
  3. Criptografe a chave do documento com uma chave pública, usando um protocolo de criptografia híbrida para criptografar as chaves simétricas.
  4. Usando o(s) componente(s) <amp-subscriptions> e/ou <amp-subscriptions-google>, armazene a chave do documento criptografado dentro do documento AMP, juntamente com o conteúdo premium criptografado.

O documento AMP armazena a chave criptografada nele mesmo. Isto evita a dissociação do documento criptografado com a chave que o decodifica.

Como funciona?

  1. O AMP processa a chave a partir do conteúdo criptografado no documento que o usuário acessa.
  2. Ao servir o conteúdo premium, o AMP envia a chave simétrica criptografada do documento ao mecanismo de autorização como parte da obtenção das permissões do usuário.
  3. O mecanismo de autorização decide se o usuário tem as permissões corretas. Se sim, o mecanismo de autorização descriptografa a chave simétrica do documento com a chave privada do mecanismo de autorização, obtido do seu par de chaves pública/privada. Em seguida, o mecanismo de autorização retorna a chave do documento para a lógica do componente amp-subscriptions.
  4. O AMP descriptografa o conteúdo premium com a chave do documento e mostra ao usuário!

Passo-a-passo da implementação

Siga os passos abaixo para integrar a manipulação de criptografia AMP ao seu servidor interno de permissões.

Passo 1: Crie um par de chaves pública/privada

Para criptografar a chave simétrica do documento, você precisa ter seu próprio par de chaves pública/privada. A criptografia de chave pública é um protocolo de criptografia híbrida, especificamente um método de criptografia assimétrica ECIES Curva elíptica P-256 com um método de criptografia simétrica AES-GCM (128 bits).

É necessário que o tratamento de chaves públicas seja feito com Tink usando este tipo de chave assimétrica. Para criar seu par de chaves pública-privada, use uma das opções a seguir:

  • A classe KeysetManager do Tink
  • Tinkey (ferramenta utilitária do Tink para geração de chaves)

Ambos suportam rotação de chaves. A implementação da rotação de chaves limita a vulnerabilidade de uma chave privada comprometida.

Para ajudá-lo a criar chaves assimétricas, criamos este script. Ele:

  1. Cria um novo ECIES com chave AEAD.
  2. Gera uma chave pública em texto comum e grava num arquivo de saída.
  3. Gera uma chave privada e grava noutro arquivo de saída.
  4. Criptografa a chave privada gerada usando uma chave hospedada no Google Cloud (GCP) antes de gravar no arquivo de saída (isto é geralmente chamado de Criptografia de Envelope).

É necessário armazenar/publicar seu Conjunto de Chaves Tink público em formato JSON. Isto permite que outras ferramentas fornecidas pela AMP funcionem sem interrupções. Nosso script já gera a chave pública nesse formato.

Passo 2: Criptografe os artigos

Decida se você prefere criptografar manualmente ou automaticamente o conteúdo premium .

Criptografia manual

É necessário usar o método simétrico AES-GCM 128 com o Tink para criptografar conteúdo premium. A chave simétrica do documento, usada para criptografar o conteúdo premium, deve ser exclusiva para cada documento. Adicione a chave do documento a um objeto JSON que contenha a chave em formato texto simples codificado em base64, bem como os SKUs necessários para acessar o conteúdo criptografado do documento.

O objeto JSON abaixo contém um exemplo da chave em formato texto simples codificado em base64 e do SKU.

{
  AccessRequirements: ['thenewsynews.com:premium'],
  Key: 'aBcDef781-2-4/sjfdi',
}

Criptografe o objeto JSON acima usando a chave pública gerada em Crie um par de chaves pública/privada.

Adicione o resultado criptografado como valor da chave "local". Coloque o par chave-valor em um objeto JSON empacotado dentro de uma tag <script type="application/json" cryptokeys="">. Coloque a tag no head do documento.

<head>
...
<script type="application/json" cryptokeys="">
{
  "local": ['y0^r$t^ff'], // This is for your environment
  "google.com": ['g00g|e$t^ff'], // This is for Google's environment
}
</script></head>

Você precisa criptografar a chave do documento com o ambiente local e a chave pública Google. A inclusão da chave pública Google permite que o cache de AMP do Google sirva seu documento. Você deve instanciar um Conjunto de chaves Tink para aceitar a chave pública Google da sua URL:

https://news.google.com/swg/encryption/keys/prod/tink/public\_key

A chave pública Google é um Conjunto de chaves Tink em formato JSON. Veja aqui um exemplo de como trabalhar com esse conjunto de chaves.

Criptografia automática

Criptografe o documento usando nosso script. O script aceita um documento HTML e criptografa todo o conteúdo dentro das tags <section subscriptions-section="content" encrypted>. Usando as chaves públicas localizadas nas URLs passadas para ele, o script criptografa a chave do documento que é criada pelo script. O uso desse script garante que todo o conteúdo seja codificado e formatado corretamente para ser servido. Veja aqui para mais instruções sobre como usar este script.

Passo 3: Integre o mecanismo de autorização

Você precisa atualizar seu mecanismo de autorização para descriptografar as chaves do documento quando um usuário possuir as permissões corretas. O componente amp-subscriptions envia automaticamente a chave do documento criptografado para o mecanismo de autorização "local" através de um parâmetro de URL “crypt=”. Ele faz o seguinte:

  1. Processa a chave do documento obtida do campo JSON "local".
  2. Decodifica o documento.

Você deve usar o Tink para decodificar as chaves do documento no seu mecanismo de autorização. Para decodificar com o Tink, instancie um cliente HybridDecrypt usando as chaves privadas geradas na seção Crie um par de chaves pública/privada. Faça isto na inicialização do servidor para o melhor desempenho.

Sua implantação do HybridDecrypt/Mecanismo de Autorização deve corresponder aproximadamente ao seu cronograma de rotação de chaves. Isso garante a disponibilidade de todas as chaves geradas para o cliente HybridDecrypt.

Tink possui ampla documentação e exemplos em C++, Java, Go e Javascript para lhe ajudar a começar com sua implementação lado-servidor.

Gestão de solicitações

Quando uma solicitação chega ao seu mecanismo de autorização:

  1. Analise a URL de pingback de permissões para extrair dados do parâmetro "crypt="
  2. Decodifique o valor do parâmetro "crypt=” usando base64. O valor armazenado no parâmetro da URL é o objeto JSON criptografado e codificado em base64.
  3. Depois que a chave criptografada estiver no formato de bytes brutos, use a função decrypt do HybridDecrypt para descriptografar a chave usando sua chave privada.
  4. Se a descriptografia ocorrer com sucesso, faça o processamento do resultado como um objeto JSON.
  5. Verifique o acesso do usuário a uma das permissões listadas no campo JSON AccessRequirements.
  6. Retorne a chave do documento obtida no campo "Key" do objeto JSON descriptografado, na sua resposta de permissões. Adicione a chave do documento descriptografada em um novo campo intitulado "decryptedDocumentKey" na resposta de permissões. Isto garante o acesso ao framework AMP.

O exemplo abaixo é um trecho de pseudo-código que descreve as etapas detalhadas acima:

string decryptDocumentKey(string encryptedKey, List < string > usersEntitlements,
    HybridDecrypt hybridDecrypter) {
    // 1. Base64 decode the input encrypted key.
    bytes encryptedKeyBytes = base64.decode(encryptedKey);
    // 2. Try to decrypt the encrypted key.
    bytes decryptedKeyBytes;
    try {
        decryptedKeyBytes = hybridDecrypter.decrypt(
            encryptedKeyBytes, null /* contextInfo */ );
    } catch (error e) {
        // Decryption error occurred. Handle it how you want.
        LOG("Error occurred decrypting: ", e);
        return "";
    }
    // 3. Parse the decrypted text into a JSON object.
    string decryptedKey = new string(decryptedKeyBytes, UTF_8);
    json::object decryptedParsedJson = JsonParser.parse(decryptedKey);
    // 4. Check to see if the requesting user has the entitlements specified in
    //    the AccessRequirements section of the JSON object.
    for (entitlement in usersEntitlements) {
        if (decryptedParsedJson["AccessRequirements"]
            .contains(entitlement)) {
            // 5. Return the document key if the user has entitlements.
            return decryptedParsedJson["Key"];
        }
    }
    // User doesn't have correct requirements, return empty string.
    return "";
}

JsonResponse getEntitlements(string requestUri) {
    // Do normal handling of entitlements here…
    List < string > usersEntitlements = getUsersEntitlementInfo();

    // Check if request URI has "crypt" parameter.
    String documentCrypt = requestUri.getQueryParameters().getFirst("crypt");

    // If URI has "crypt" param, try to decrypt it.
    string documentKey;
    if (documentCrypt != null) {
        documentKey = decryptDocumentKey(
            documentCrypt,
            usersEntitlements,
            this.hybridDecrypter_);
    }

    // Construct JSON response.
    JsonResponse response = JsonResponse {
        signedEntitlements: getSignedEntitlements(),
        isReadyToPay: getIsReadyToPay(),
    };
    if (!documentKey.empty()) {
        response.decryptedDocumentKey = documentKey;
    }
    return response;
}

Recursos relacionados

Confira a documentação e exemplos encontrados na página Github do Tink.

Todos os scripts auxiliares estão no repositório GitHub subscriptions-project/encryption.

Suporte adicional

Para qualquer dúvida, comentário ou outras questões, por favor registre um Issue no Github.