Segurança

Segurança do servidor MCP para Craft CMS: o limite é um utilizador do Craft.

Um cliente de IA que pode alterar um site em funcionamento precisa de limites claros. Esta página lista cada limite pela ordem em que uma chamada o encontra. Diz também o que cada limite não cobre.

Por onde passa cada chamada

Por esta ordem. Uma chamada que falhe qualquer passo nunca chega à ferramenta.

  1. Limite de pedidos — chamadas por minuto, contadas por token. Um travão contra um modelo em ciclo, não uma defesa contra alguém determinado que tenha um token válido.
  2. Âmbito do token — read, content ou full. O âmbito só restringe o que o utilizador associado já pode fazer.
  3. Edição — o plugin é distribuído numa única edição, por isso hoje este passo é sempre superado. Existe para que uma futura divisão em edições não possa disponibilizar uma ferramenta sem se dar por isso.
  4. Modo só de leitura — enquanto estiver ativo, nada que altere o estado é executado, digam o que disserem as definições abaixo.
  5. Ativação das ferramentas — todas as ferramentas que alteram o estado estão desligadas até um administrador as ativar, uma a uma.
  6. Verificação de administrador — as ferramentas de administrador exigem, além disso, que o utilizador associado seja administrador.
  7. Esquema dos argumentos — os argumentos são validados face ao esquema da ferramenta antes de a ferramenta os ver.
  8. Verificação de permissões do próprio Craft, por elemento — depois, a ferramenta pergunta ao Craft, para este elemento e este site, se o utilizador associado o pode ver, guardar ou eliminar.

Dois tipos de recusa, de propósito. Uma ferramenta que não existe e uma ferramenta que o âmbito do token não abrange recebem a mesma resposta — Unknown tool — porque a diferença diria a um atacante o que mais existe. Uma ferramenta desligada, bloqueada pelo modo só de leitura ou reservada a administradores também não é listada, mas uma chamada direta recebe o motivo por palavras simples, para que um operador não procure a falha do seu lado.

Porque é o utilizador o verdadeiro limite

Cada token pertence exatamente a um utilizador do Craft. São feitas duas perguntas, e a segunda é a que importa: primeiro a geral, se este tipo de ação está sequer aberto a este utilizador, e depois a precisa — pode este utilizador fazer isto, a este elemento, neste site. Um editor que só pode trabalhar num site e ver uma secção chega exatamente a isso através do cliente de IA, e a mais nada.

Isto tem uma consequência que vale a pena dizer claramente: um token que pertence a um administrador pode fazer o que um administrador pode fazer. Atribua a um token o utilizador com menos permissões que ainda consiga fazer o trabalho e use o âmbito para restringir ainda mais.

Uma secção ou um grupo de categorias desconhecido ou proibido devolve uma lista vazia, não um erro — um erro confirmaria que existe. Já um identificador de site que não existe é indicado como erro, porque um identificador errado é um engano na chamada, não um limite.

Tokens

  • O texto simples é mostrado uma única vez, quando o token é criado. Só é guardado um hash SHA-256, por isso não pode ser mostrado de novo.
  • Os tokens ficam numa tabela própria, nunca na configuração do projeto — que é enviada para o Git.
  • A página de tokens no painel de controlo está aberta apenas a administradores. O Craft regista uma permissão «Access AlphaBridge MCP» para cada plugin com uma entrada na barra lateral; concedê-la não abre esta página a quem não é administrador.
  • Um token pode ter uma data de expiração e pode ser revogado a qualquer momento; a chamada seguinte com ele falha.
  • O cabeçalho Authorization é ocultado nas entradas de registo com o contexto do pedido do próprio Craft, por isso um token não acaba em storage/logs quando um pedido é registado.

O registo de auditoria, e o que deliberadamente omite

Cada chamada de ferramenta é registada: quando, que ferramenta, que utilizador do Craft e o resultado — permitida, recusada ou falhada. As chamadas recusadas também são registadas; são as mais interessantes.

Os nomes dos argumentos são registados, os valores não. Consegue ver que uma entrada foi criada com uma secção e um título; não consegue ler o título a partir do registo. É deliberado: um registo que guardasse valores seria uma segunda cópia, menos protegida, dos seus conteúdos.

Ler o registo exige o âmbito de administrador. O registo é reduzido a um tamanho fixo, por isso não pode crescer sem limite.

Um limite honesto: a escrita da entrada no registo é feita na medida do possível. Se essa própria escrita falhar — por exemplo, por um erro da base de dados — a chamada não é interrompida; a falha vai para o registo do próprio Craft. Por isso, trate o registo de auditoria como um histórico muito bom, não como uma garantia de que nada pode faltar.

Ler os registos do Craft é mais restrito do que parece

A ferramenta de registos devolve as entradas de registo do Craft sem o contexto do pedido que o Craft lhes junta. Esse contexto contém $_POST, $_COOKIE e $_SESSION — incluindo, num início de sessão falhado, uma senha em texto simples. As instruções SQL e os stack traces são cortados no primeiro marcador.

É por isso que a ferramenta de registos é uma ferramenta de administrador e está desligada por predefinição.

As ferramentas de base de dados, e os seus limites

Duas ferramentas chegam à base de dados, e só uma delas executa SQL escrito por si. O craft_db_schema lista os nomes das tabelas e marca as protegidas como ilegíveis. O craft_db_query executa um SELECT sobre uma tabela e rejeita, antes de qualquer coisa chegar à base de dados:

  • joins, subconsultas, common table expressions e UNION
  • todas as funções exceto count, min, max, avg e sum
  • tabelas protegidas — utilizadores, sessões, tokens, a configuração do projeto e outras que contêm credenciais ou segredos
  • expressões que indiquem a conta do servidor ou uma sequência

Uma barreira feita de regras só conhece o que lhe disseram. É por isso que ambas são ferramentas de administrador, que a ferramenta de consulta está desligada por predefinição e que nenhuma delas escreve.

Origens e DNS rebinding

Se um pedido tiver um cabeçalho Origin, este tem de estar na lista de permissões, ou o endpoint responde 403. A lista de permissões é o que configurou mais o URL do site, mas só quando este pode ser determinado estaticamente. Um URL do site construído a partir do alias @web do Craft não conta: o Craft deriva @web do cabeçalho Host do pedido recebido, por isso um atacante seria comparado com um valor escolhido por ele próprio.

Numa instalação destas, a lista configurada é a única fonte e, se estiver vazia, todos os pedidos com um Origin são recusados. Os clientes servidor-a-servidor não enviam Origin e não são afetados.

O que este plugin não faz

Cada um destes pontos é uma decisão, e cada um elimina uma classe de risco em vez de a gerir.

  • Sem execução arbitrária de código. Nenhuma ferramenta executa PHP, comandos de shell ou um template que lhe seja passado.
  • Sem implementação nem transferência de ficheiros. O plugin não move ficheiros e nunca toca num servidor por SSH ou FTP.
  • Sem gestão do ciclo de vida de plugins. Nada instala, ativa, desativa ou remove plugins.
  • Sem escrita na configuração do projeto nem nos utilizadores. O Craft impõe as regras para estes nos seus controladores; este plugin fala com a API de elementos, que não vê essas regras — por isso, não escreve lá de todo.
  • Sem etiquetas. O Craft não define nenhuma permissão para etiquetas, por isso qualquer limite teria de ser inventado em vez de imposto.
  • Sem valores das definições de campos. As definições de campos são devolvidas apenas como nomes, nunca como valores, porque um plugin pode pôr lá qualquer coisa. Isto não é uma promessa geral sobre segredos: a ferramenta de base de dados chega a todas as tabelas físicas cujo nome não esteja na sua lista de protegidas, e uma tabela de terceiros com um nome discreto não está nela. É exatamente por isso que essa ferramenta é para administradores e está desligada por predefinição.

Como isto se mantém verdadeiro

Cada alteração executa um conjunto de testes unitários, análise estática com PHPStan nível 6 sem baseline e uma execução de ponta a ponta numa instalação do Craft acabada de criar, em PostgreSQL 16 e MySQL 8.4. Essa execução cria um editor sem privilégios de administrador que só pode ver uma secção e trabalhar num site, e prova que o filtro de permissões corta mesmo — e não apenas que as ferramentas devolvem alguma coisa.

Encontrou algo? Comunique-o em privado para mail@cultureclub.dev, e não numa issue pública.