← Professional Cloud Security Engineer: controlos e evidência
11 / 13 · 135 MIN

Proteção de dados, chaves e contexto de IA

Relaciona leitura autorizada, dependências de recuperação e fronteiras de IA em decisões de produção.

Começar pela leitura que deve ser permitida

Num incidente de acesso a dados, começa por escrever quem pede, que operação pretende executar e sobre que dados. A frase “a equipa precisa de acesso ao relatório” esconde decisões distintas: consultar operações de uma carteira, ler identificadores completos, exportar resultados e partilhá-los com outro grupo. Num serviço fictício de operações de fundos, um analista pode precisar de investigar diferenças de reconciliação sem receber os dados identificadores de todos os clientes. O exercício não descreve procedimentos internos BNP Paribas. No BigQuery, pensa em linhas e colunas separadamente. A restrição de linhas escolhe operações visíveis; a proteção de colunas limita valores dentro dessas operações. No fluxo com policy tags, a leitura da coluna protegida exige também autorização para a tag, além do acesso ao dataset. Antes de corrigir uma recusa, confirma a necessidade aprovada e a identidade efetiva da consulta. Um teste com a conta do administrador pode funcionar e continuar a não explicar a falha do analista. Ao rever políticas de linhas, considera todas as concessões aplicáveis. Uma política ampla mantida pode continuar a permitir linhas apesar de uma nova política mais restrita. Prepara dois conjuntos fictícios de operações, um permitido e outro que deve ser recusado. Regista o resultado esperado antes do teste. A revisão está incompleta se apenas demonstrar que uma consulta devolve dados; precisa de verificar que o utilizador recebe o conjunto certo e que as exceções antigas foram tratadas.

Migrar permissões sem alargar a divulgação

Considera um bucket histórico onde cada fornecedor recebe os seus ficheiros através de ACL por objeto. Uma mudança para uniform bucket-level access precisa de mapear essas relações para o desenho IAM pretendido. Conceder leitura no bucket inteiro a todos os antigos leitores pode manter o batch a funcionar e, ao mesmo tempo, quebrar a separação entre fornecedores. O indicador de sucesso técnico não capta sozinho esse efeito. Desenha primeiro a matriz fornecedor, conjunto de objetos e operações autorizadas. Separa conjuntos com requisitos diferentes através de um desenho suportado, por exemplo agrupamentos em buckets adequados. Antes da janela, confirma consumidores, identidades, políticas e processos de publicação de novos ficheiros. Um objeto criado depois da migração também precisa de herdar o âmbito correto. Inclui testes negativos: a identidade usada pelo fornecedor A deve continuar sem ler um ficheiro destinado apenas a B. Usa nomes e conteúdo fictícios na evidência do ensaio. Public access prevention trata um âmbito diferente e não bloqueia Signed URLs. Por isso, uma conclusão como “o bucket é privado, logo ninguém pode partilhar um acesso” fica por demonstrar. Revê também quem emite esses URLs, o objeto abrangido e o processo de entrega. No go-live, apresenta dependências por resolver, resultado dos testes e responsáveis pela decisão. Se a separação não estiver demonstrada, o plano pode precisar de ser corrigido ou adiado; alargar permissões não deve ser o mecanismo automático de contingência.

Interpretar deteções e preservar contexto

Um relatório de inspeção descreve uma execução, não toda a realidade dos dados. Guarda a origem, o intervalo temporal, a configuração dos detetores, as exclusões e o âmbito analisado. Se foram examinados 100 de 50000 registos, a ausência de ocorrências não demonstra que os restantes não contêm informação sensível. O mesmo cuidado aplica-se a campos não analisados e a tipos de informação que a configuração não procurou. Uma amostra pode orientar investigação sem sustentar uma classificação definitiva de todo o conjunto. Na reconciliação fictícia, compara também o requisito de utilidade com o de redução de exposição. Um identificador transformado pode permitir joins necessários entre duas tabelas. Se a transformação usa um context tweak diferente em cada pipeline, o mesmo valor e a mesma chave podem produzir tokens diferentes. Antes de concluir que existe corrupção, compara configuração, normalização e contexto efetivamente fornecidos. Documenta quais as correlações pretendidas e quais as que devem permanecer separadas. A correção não é tornar todas as transformações iguais sem analisar o requisito. Uma diferença de contexto pode ser intencional para limitar correlação entre carteiras. Se a necessidade de join mudou, a decisão deve incluir os responsáveis pelos dados e os consumidores a jusante. Ensaiar com valores sintéticos permite verificar correspondências sem colocar identificadores reais no ticket. No resumo, distingue deteção, classificação e transformação: cada uma responde a uma pergunta diferente e nenhuma dispensa saber quem pode usar o resultado.

Tratar rotação de segredos como uma mudança

Uma agenda de rotação só é útil se o processo completo tiver dono. Secret Manager envia a notificação SECRET_ROTATE através de Pub/Sub; um subscriber e o respetivo workflow precisam de executar as ações necessárias. No exemplo de uma integração externa, identifica quem altera a credencial no sistema de origem, quem publica a nova versão, quem atualiza os consumidores e como se confirma a operação. Receber a mensagem é evidência de uma etapa, não prova de conclusão de todas. Desenha a sequência antes da janela. Se o sistema externo aceitar temporariamente duas credenciais, pode haver uma estratégia de transição diferente daquela usada quando só aceita uma. Essa capacidade é uma condição do cenário que precisa de confirmação no sistema concreto. Define o que acontece se a criação funciona e a publicação falha, ou se parte das instâncias ainda usa a versão antiga. Um rollback de código pode não restaurar uma credencial já invalidada no fornecedor. Referências numéricas de versão tornam a dependência da release explícita e permitem ensaiar a alteração antes de a adotar. Evita que uma versão incompatível passe a ser consumida sem decisão apenas porque latest mudou. Finalmente, observa as cópias: uma integração suportada pode injetar o segredo numa variável, mas uma biblioteca que regista o ambiente cria exposição nos logs. A entrega a produção deve incluir diagnóstico sem valores secretos, sinais de falha e um responsável por resolver consumidores que não concluíram a transição.

Separar disponibilidade, integridade e identidade da chave

Quando uma operação criptográfica falha, distingue estado da versão, permissões, chave selecionada e integridade dos dados. Uma versão restaurada durante o período de destruição agendada regressa a DISABLED. É necessário avaliar a reativação para voltar a utilizá-la. Não confundas o sucesso do restauro administrativo com uma confirmação de que o arquivo já pode ser recuperado. A decisão deve relacionar o estado observado com a operação pretendida e a autorização da equipa responsável. Na resposta de uma operação KMS, verificar bytes e verificar o recurso são passos diferentes. Se o checksum do ciphertext não corresponder, descarta esse resultado e trata a repetição de forma limitada. Se os bytes forem consistentes mas name apontar para uma chave inesperada, investiga a identidade do recurso. Substituir a metadata pelo nome desejado não altera a chave que produziu o resultado. Guarda evidência de diagnóstico sem incluir plaintext ou material secreto. Num recurso compatível com CMEK em Agent Platform, o administrador que inicia a criação e o service agent que usa a chave são identidades distintas. Se a falha pertence ao service agent, dar mais privilégios à pessoa não corrige essa dependência. Confirma a identidade documentada, a chave e a localização aplicáveis ao recurso concreto. Para o PM técnico, a entrega útil é uma matriz operação, identidade, recurso, evidência e responsável; não apenas um screenshot de um papel administrativo concedido.

Planear a recuperação para além do ciphertext

Num desenho de envelope encryption, o arquivo pode ser cifrado por uma DEK que é depois protegida por uma KEK. A recuperação precisa do arquivo, da DEK protegida e das referências e autorizações relevantes. Guardar apenas o nome da KEK não reconstrói uma DEK descartada. Desenha o pacote de recuperação e pergunta quais os componentes que sobreviveriam a uma falha do serviço de origem. O exercício conceptual não implementa criptografia nem recomenda criar algoritmos próprios. AAD acrescenta uma dependência de contexto. A descifragem exige o mesmo valor usado na cifragem. No caso fictício, a equipa incluiu uma versão de contrato no AAD e depois substituiu essa metadata durante a migração do arquivo. A disponibilidade da chave não resolve a perda do contexto original. Testa se o processo preserva ou reproduz esse valor sem o confundir com a versão atual do contrato. Regista também a localização escolhida: uma chave global não demonstra confinamento a uma região específica. Para dados em utilização, attestation fornece evidência que um verificador deve confrontar com a política. Uma assinatura válida sobre uma observação antiga não demonstra atualidade. Se a política exige desafio recente, confirma essa ligação, as medições esperadas e a decisão do serviço que entrega a chave. O PM deve pedir evidência suficiente para o critério aprovado, incluindo falhas esperadas. Não extrapoles um teste positivo para todas as tecnologias Confidential VM ou para toda a segurança da aplicação.

Controlar o que entra no contexto de IA

Um assistente de pesquisa introduz duas identidades que não devem ser confundidas: a identidade técnica que lê a origem e a pessoa que recebe a resposta. Se a service account lê todas as carteiras, isso não demonstra que cada utilizador pode receber todos os documentos. Autoriza o conteúdo para o pedido antes de o colocar no contexto do modelo. Filtrar apenas as citações na resposta chega tarde para impedir que o conteúdo seja transmitido para processamento. O caminho de chamada também importa. Um agente construído para confiar num frontend pode depender desse frontend para gerir utilizadores e sessões. Dar acesso direto ao endpoint a um cliente novo exige rever os controlos no código recetor. Não uses um identificador de utilizador fornecido pelo cliente como prova suficiente de autorização. Escreve um caso permitido e outro em que o cliente tenta usar uma sessão alheia, sem utilizar dados reais de clientes no ensaio. Texto recuperado pode conter instruções que tentam desviar o comportamento do modelo. A relevância do documento não lhe dá autoridade para mandar exportar um relatório ou chamar uma ferramenta. A autorização da ação continua a precisar de um controlo no servidor. Model Armor pode ajudar a inspecionar conteúdo, mas a integração deve definir resultados e falhas. Nesta aula, a política fictícia exige uma resposta de inspeção válida e permissiva; um timeout deixa essa condição por cumprir. Isso é uma regra explícita do exemplo, não uma afirmação sobre o comportamento predefinido de todas as integrações.

Exercício: selecionar documentos com evidência delimitada

O exercício Python é local e usa apenas metadata fictícia. Não lê documentos, não chama modelos e não avalia Google IAM. A política definida para aprender exige mesmo tenant, correspondência de grupo e evidência completa e recente de ambos os lados. Os grupos pertencem ao âmbito do tenant; ter o mesmo nome de grupo noutro tenant não concede acesso. A função primeiro classifica elegibilidade e só depois ordena os documentos elegíveis por score, usando o identificador para desempatar. Antes de executar python3 run.py, prevê o resultado: um documento permitido com score 0.70 e outro de tenant diferente com 0.99. Só o primeiro deve ser selecionado. Depois altera a idade da ACL: o limite é inclusivo, logo idade 10 passa com max_age=10, mas idade 11 não demonstra atualidade. Evidência incompleta, sem data ou com data futura mantém o documento por esclarecer. Uma incompatibilidade de tenant conhecida continua registada mesmo quando também falta evidência da ACL. Lê selectedIds como resultado deste modelo e não como prova de acesso real. Os identificadores e motivos de diagnóstico destinam-se ao operador do exercício, não são um contrato de resposta para utilizadores finais. Completa a atividade escrevendo uma nota com o documento excluído, a condição conhecida e a próxima recolha necessária. O resumo é simples: dados autorizados, contexto criptográfico preservado e ações de IA delimitadas exigem decisões diferentes. A evidência deve permitir distingui-las durante incidentes e antes da passagem a produção.

"""Original offline teaching model. No cloud IAM or real retrieval evaluation.

Policy: same tenant, matching group, complete and fresh ACL/membership evidence.
Freshness ages 0..max_age inclusive. Unknown or stale evidence withholds eligibility.
Diagnostic IDs are for the exercise operator, never an end-user response contract.
"""
from copy import deepcopy
from hashlib import sha256
from itertools import permutations, product
from math import isfinite
from pathlib import Path
import json


def number(value, label):
    if type(value) not in (int, float) or not isfinite(value):
        raise ValueError(label + ' must be a finite number')
    return value


def string(value, label):
    if not isinstance(value, str) or not value.strip():
        raise ValueError(label + ' must be a nonempty string')
    return value


def groups(value):
    if not isinstance(value, list):
        raise ValueError('groups must be a list')
    for group in value:
        string(group, 'group')
    if len(value) != len(set(value)):
        raise ValueError('duplicate group')
    return set(value)


def evidence(record, now, max_age):
    if type(record.get('complete')) is not bool:
        raise ValueError('complete must be boolean')
    at = record.get('checkedAt')
    if at is not None:
        number(at, 'checkedAt')
    unknown = []
    if not record['complete']:
        unknown.append('incomplete')
    if at is None:
        unknown.append('missing-time')
    elif at > now:
        unknown.append('future-time')
    elif now - at > max_age:
        unknown.append('stale')
    return unknown


def select_documents(caller, documents, now=100, max_age=10, top_k=2):
    number(now, 'now')
    number(max_age, 'max_age')
    if max_age < 0 or type(top_k) is not int or top_k < 0:
        raise ValueError('invalid age or selection limit')
    if not isinstance(caller, dict) or not isinstance(documents, list):
        raise ValueError('caller and documents shape')
    tenant = string(caller.get('tenant'), 'caller tenant')
    caller_groups = groups(caller.get('groups'))
    caller_unknown = evidence(caller, now, max_age)
    seen, decisions, eligible = set(), [], []
    for doc in documents:
        if not isinstance(doc, dict):
            raise ValueError('document must be an object')
        identifier = string(doc.get('id'), 'document ID')
        if identifier in seen:
            raise ValueError('duplicate document ID')
        seen.add(identifier)
        doc_tenant = string(doc.get('tenant'), 'document tenant')
        doc_groups = groups(doc.get('groups'))
        score = number(doc.get('score'), 'score')
        acl_unknown = evidence(doc, now, max_age)
        unknown = ['membership:' + x for x in caller_unknown]
        unknown += ['acl:' + x for x in acl_unknown]
        failures = []
        if tenant != doc_tenant:
            failures.append('tenant-mismatch')
        # Incomplete or stale group lists cannot prove either current grant or denial.
        if not caller_unknown and not acl_unknown and not caller_groups & doc_groups:
            failures.append('no-group-grant')
        status = 'rejected' if failures else ('unresolved' if unknown else 'eligible')
        decisions.append(dict(id=identifier, status=status, knownFailures=failures,
                              unresolvedEvidence=unknown))
        if status == 'eligible':
            eligible.append((score, identifier))
    ranked = [identifier for _, identifier in sorted(eligible, key=lambda x: (-x[0], x[1]))]
    return dict(selectedIds=ranked[:top_k], eligibleIds=ranked,
                decisions=sorted(decisions, key=lambda x: x['id']),
                realAccessProven=False, productionAuthorized=False)


def main():
    caller = dict(tenant='A', groups=['ops'], complete=True, checkedAt=100)
    doc = dict(id='a', tenant='A', groups=['ops'], complete=True, checkedAt=100, score=.7)
    fixtures = []

    def case(name, docs, expected, actor=None, **kwargs):
        actor = caller if actor is None else actor
        original = deepcopy((actor, docs))
        result = select_documents(actor, docs, **kwargs)
        assert result['selectedIds'] == expected, name
        assert (actor, docs) == original
        fixtures.append(dict(id=name, **result))
        return result

    case('authorized-before-ranking', [doc, dict(doc, id='b', tenant='B', score=.99)], ['a'])
    r = case('unknown-acl', [dict(doc, complete=False)], [])
    assert r['decisions'][0]['status'] == 'unresolved'
    case('stale-acl', [dict(doc, checkedAt=89)], [])
    case('fresh-boundary', [dict(doc, checkedAt=90)], ['a'])
    case('unknown-membership', [doc], [], dict(caller, complete=False))
    r = case('empty-membership', [doc], [], dict(caller, groups=[]))
    assert r['decisions'][0]['knownFailures'] == ['no-group-grant']
    case('future-evidence', [dict(doc, checkedAt=101)], [])
    case('tie-order', [dict(doc, id='b'), doc], ['a'], top_k=1)
    r = case('known-failure-and-unknown', [dict(doc, tenant='B', complete=False)], [])
    assert r['decisions'][0]['knownFailures'] == ['tenant-mismatch']
    assert r['decisions'][0]['unresolvedEvidence'] == ['acl:incomplete']
    case('empty-inventory', [], [])
    case('zero-selection-limit', [doc], [], top_k=0)
    count = 0
    for same_tenant, member, complete_caller, complete_acl, timestamp in product(
            (False, True), (False, True), (False, True), (False, True), (None, 89, 90, 100, 101)):
        actor = dict(caller, complete=complete_caller, groups=['ops'] if member else [])
        item = dict(doc, tenant='A' if same_tenant else 'B', complete=complete_acl, checkedAt=timestamp)
        result = select_documents(actor, [item])
        expected = same_tenant and member and complete_caller and complete_acl and timestamp in (90, 100)
        assert result['selectedIds'] == (['a'] if expected else [])
        decision = result['decisions'][0]
        assert ('tenant-mismatch' in decision['knownFailures']) == (not same_tenant)
        if not same_tenant:
            assert decision['status'] == 'rejected'
        count += 1
    docs = [doc, dict(doc, id='b', score=.8), dict(doc, id='c', tenant='B', score=.99)]
    expected = select_documents(caller, docs)
    permutation_count = 0
    for permutation in permutations(docs):
        assert select_documents(caller, list(permutation)) == expected
        permutation_count += 1
    invalid = [
        (dict(caller, complete='true'), [doc], {}),
        (dict(caller, groups='ops'), [doc], {}),
        (dict(caller, groups=['ops', 'ops']), [doc], {}),
        (dict(caller, tenant=''), [doc], {}),
        (dict(caller, checkedAt=True), [doc], {}),
        (caller, [dict(doc, score=float('nan'))], {}),
        (caller, [dict(doc, score=float('inf'))], {}),
        (caller, [dict(doc, score=True)], {}),
        (caller, [doc, doc], {}),
        (caller, [dict(doc, id='')], {}),
        (caller, [dict(doc, groups=[5])], {}),
        (caller, [dict(doc, checkedAt='100')], {}),
        (caller, [dict(doc, complete=None)], {}),
        (caller, [doc], dict(top_k=True)),
        (caller, [doc], dict(top_k=-1)),
        (caller, [doc], dict(max_age=-1)),
        (caller, [doc], dict(now=float('inf'))),
        (caller, [None], {}),
    ]
    for actor, items, args in invalid:
        try:
            select_documents(actor, items, **args)
        except ValueError:
            pass
        else:
            raise AssertionError('invalid input accepted')
    print(json.dumps(dict(labId='pcse-retrieval-scope', fixtures=fixtures,
                          stateCombinations=count, inputPermutations=permutation_count,
                          invalidInputs=len(invalid), inputPreserved=True, orderIndependent=True,
                          network=False, cloudExecuted=False, persistentWrites=False,
                          scriptSha256=sha256(Path(__file__).read_bytes()).hexdigest()), indent=2))


if __name__ == '__main__':
    main()
NA PRÁTICA

Uma migração preserva o batch mas amplia leitura; um restauro deixa a chave desativada; um assistente encontra documentos fora do tenant. Cada resultado exige uma decisão própria.

Armadilhas comuns

Confundir uma amostra sem deteções com classificação completa, uma notificação com rotação executada e relevância documental com autorização.

Tópicos relacionados: Identidades e autorização de workloads · Continuidade e recuperação de dados · Segurança de aplicações com IA

Leva esta ideia contigo

Define quem lê, preserva o contexto necessário à recuperação e autoriza dados e ações antes de os entregar ao modelo.

Criar conta

Referência: Introduction to column-level access control · Current linked guide; edition date unconfirmed (2026-09-30 inspection)

Google Cloud é uma marca comercial de Google LLC. A dr.pt é uma plataforma de preparação independente e não está afiliada, associada, patrocinada, autorizada nem aprovada por Google. Os conteúdos e as perguntas são originais, não são perguntas oficiais de exame, e concluir os nossos testes não atribui nem garante qualquer certificação. Os nomes são usados apenas para identificar o tema. Todas as outras marcas pertencem aos respetivos titulares.