3 pontos por GN⁺ 2024-11-25 | 1 comentários | Compartilhar no WhatsApp
  • Daniel Mangum mostra que, usando a estrutura de upload de blobs e referência de records do AT Protocol, é possível servir um arquivo text/html enviado para um PDS do Bluesky como se fosse uma página web aberta no navegador
  • O ponto central é que um blob enviado não se torna público imediatamente; o PDS só o coloca em um estado com acesso público quando algum record referencia o blob
  • O embed de imagem comum do Bluesky só permite MIME type image/*, mas um record com lexicon desconhecido ou um embed customizado baseado em open union pode armazenar uma referência a um blob text/html
  • getBlob pode ser acessado sem autenticação, e uma requisição para bsky.social é redirecionada para o PDS real, porcini.us-east.host.bsky.network, que retorna o HTML
  • O Bluesky aplica headers de CSP e nosniff ao getBlob, e limita o upload padrão de blobs a 5 MB, mas o serviço que opera o PDS ainda fica com o ônus de hospedar arquivos arbitrários e arcar com custos de armazenamento e tráfego

Como publicar um site com blobs do AT Protocol

  • Este caso se baseia mais no funcionamento do AT Protocol e da API de PDS do que em recursos normais do app Bluesky
  • O Bluesky fornece um PDS entryway para acessar o PDS do usuário, expondo várias instâncias de PDS sob o domínio bsky.social
  • Também é possível acessar diretamente cada instância de PDS, e o site de exemplo abre no endpoint com.atproto.sync.getBlob de porcini.us-east.host.bsky.network
  • O autor se comunicou com a equipe do Bluesky antes da publicação, e esse comportamento, embora não seja a forma pretendida de uso do aplicativo, já era conhecido e não se trata de um processo de divulgação de vulnerabilidade

Por que records e blobs são separados

  • No AT Protocol, os dados da aplicação se dividem em records e blobs
    • records são as entidades principais criadas pelo usuário, podendo ter estrutura e metadados
    • blobs são dados maiores e não estruturados, como imagens, e normalmente só ficam expostos quando um record os referencia
  • Ao criar uma postagem com imagem no Bluesky, isso parece uma única ação para o usuário, mas no nível da API o upload do blob e a criação do record são separados
  • Segundo a especificação de blobs, o blob precisa primeiro ser enviado ao PDS antes que um record possa referenciá-lo
  • No momento do upload, o servidor ainda não sabe em qual Lexicon o blob será usado, então aplica apenas restrições genéricas; as restrições baseadas em Lexicon são aplicadas na criação do record
  • Um blob enviado com sucesso vai para um armazenamento temporário e, nesse estado, não pode ser baixado nem distribuído, além de não aparecer na saída de listBlobs
  • Quando a criação do record é bem-sucedida e esse record referencia o blob, o servidor o move para um estado com acesso público

Upload de blob HTML e transição para público

  • O token de autenticação é obtido trocando as credenciais do usuário pelo método XRPC com.atproto.server.createSession
  • No exemplo, com.atproto.sync.listBlobs foi usado para verificar a quantidade de blobs existentes, que era 391 antes do upload
  • O conteúdo do site era um arquivo index.html simples, enviado por com.atproto.repo.uploadBlob
    • O Content-Type era text/html
    • Os metadados retornados do blob incluíam o CID bafkreic5fmelmhqoqxfjz2siw5ey43ixwlzg5gvv2pkkz7o25ikepv4zeq, mimeType: text/html e size: 268
  • Logo após o upload, uma tentativa de buscá-lo com com.atproto.sync.getBlob falhou, e a contagem de listBlobs também permaneceu em 391
  • Ao tentar referenciar o blob HTML como embed de imagem em uma postagem do Bluesky, a validação falha porque o schema app.bsky.embed.image exige MIME type image/*
    • Se text/html for mantido, ocorre o erro Wrong type of file. It is text/html but it must match image/*.
    • Se o mimeType for alterado para image/jpeg, ocorre um erro dizendo que o MIME type do blob armazenado e o MIME type da referência não coincidem

Tornando o blob público com um record de lexicon desconhecido

  • O tipo blob não é algo exclusivo do Bluesky; ele faz parte do modelo de dados do AT Protocol
  • A implementação do PDS do Bluesky é open source, e findBlobRefs percorre recursivamente LexValue em busca de referências com $type: blob
  • Como o PDS precisa suportar novos lexicons ao longo do tempo, ele também deve conseguir processar lexicons que ainda não conhece
  • Ao criar um record do tipo com.danielmangum.hack.website e fazer referência ao blob HTML, o PDS armazenou o record
    • A resposta incluía validationStatus: unknown
    • Como o PDS não conhecia o lexicon com.danielmangum.hack.*, não pôde validá-lo, mas ainda assim permitiu armazenar o record
  • Depois disso, a contagem em listBlobs subiu para 392, confirmando que o blob foi persistido
  • getBlob pode ser chamado sem token de autenticação, porque entidades não autenticadas que precisam lidar com records também precisam poder obter blobs
  • A chamada getBlob em bsky.social retornou um redirecionamento 302 para a URL do PDS real, e ao seguir o redirecionamento com -L, o conteúdo HTML foi retornado normalmente

Headers de segurança, CDN e o custo do acesso direto

  • As Considerações de Segurança da especificação de blobs do AT Protocol tratam dos riscos de segurança de conteúdo ao servir arquivos arbitrários enviados por usuários a partir de um servidor web
    • Um exemplo clássico é o problema de XSS quando scripts ou conteúdo SVG são executados na mesma origin
    • Aplicar Content Security Policy ao endpoint getBlob é, na prática, obrigatório
    • Fornecer diretamente o armazenamento de blobs para navegadores e agentes web praticamente não é suportado; as aplicações devem passar por uma CDN separada, proxy ou outro serviço web
  • O handler getBlob do PDS do Bluesky aplica os headers de segurança recomendados
    • x-content-type-options: nosniff
    • content-security-policy: default-src 'none'; sandbox
  • O limite padrão de upload de blobs é de 5 MB
  • Os blobs de imagem normais do aplicativo Bluesky não são servidos diretamente pelo PDS, e sim por URLs de CDN em cdn.bsky.app
    • Existem URLs separadas para thumbnail no feed e para tamanho completo
    • O record da postagem inclui o CID da imagem, e a aplicação precisa saber como usar o esquema de entrega via CDN
  • O acesso direto via getBlob continua existindo por causa de casos como rotulagem de imagens, exportação de conteúdo do usuário e usos futuros, segundo discussões em issues do GitHub
    • Nessas mesmas discussões também aparece a possibilidade de usuários fazerem hotlink de conteúdo ou usarem o Bluesky como hospedagem gratuita
    • Na implementação inicial não havia headers de segurança, que foram adicionados depois
  • Em plataformas sociais tradicionais, os tipos de conteúdo válidos são mais limitados, então é possível impor restrições mais rígidas aos blobs já no momento do upload; no Bluesky e no AT Protocol, a extensibilidade exige um tratamento mais complexo

Inserindo um embed customizado em postagens com open union

  • O tipo app.bsky.feed.post define a lista de embeds válidos como uma union
  • No lexicon do AT Protocol, uma union é open por padrão, a menos que seja explicitamente marcada como closed
    • Como novos tipos podem ser adicionados em revisões futuras do schema, as implementações devem ser flexíveis na validação
    • Com a flag closed, o conjunto de tipos fica fixo
  • A union de embeds das postagens do Bluesky não está marcada como closed
  • Por isso, é possível criar uma postagem mesmo com um $type de embed não enumerado
  • No exemplo, foi inserido um embed com.danielmangum.hack.sites dentro de um record app.bsky.feed.post, e esse embed referenciava o blob HTML
    • O validationStatus da resposta foi valid
    • No aplicativo Bluesky, esse embed é simplesmente ignorado em silêncio
    • Como o conteúdo e a referência permanecem armazenados no record, outros aplicativos podem renderizar esse embed
  • Essa característica do lexicon pode ser usada para adicionar pequenas extensões sobre casos de uso já existentes; um exemplo citado é executar pequenos trechos de código dentro de uma postagem em um sandbox WebAssembly

1 comentários

 
GN⁺ 2024-11-25
Comentários no Hacker News
  • Foi legal o Daniel ter entrado em contato diretamente com a equipe, e hospedagem de blobs é uma área que inevitavelmente vai continuar sendo ajustada à medida que se entende melhor os caminhos de abuso
    Por enquanto, é divertido ver esse tipo de uso funcionando de verdade, e a arquitetura pretendida é que o PDS seja um host de banco de dados, assim como um servidor web hospeda um site

    • Fico curioso se há planos de reviver o Beaker Browser
      Ele era divertido de brincar no passado, e eu não sabia que tinha migrado para o Bluesky; é bem interessante
    • Se o conteúdo for vinculado ao domínio do próprio usuário, sem passar por uma appview específica como a bsky, talvez a possibilidade de abuso diminua
      O Bsky já oferece suporte a associar domínios de usuários como ALIAS para redirect.bsky.com: https://bsky.app/profile/jacob.gold/post/3kh6rnpdzmp2v
    • O Beaker foi um projeto muito legal de acompanhar, e essa experiência parece que pode ser bastante útil no Bluesky
    • Esse método é, na prática, encarar de frente o buraco da moderação de conteúdo envolvendo direitos autorais, CSAM e pornografia
  • Fiquei curioso sobre em qual contexto de segurança isso é executado, então verifiquei uma URL de blob do PDS com curl, e os cabeçalhos de resposta incluíam access-control-allow-origin: *, content-security-policy: default-src 'none'; sandbox, x-content-type-options: nosniff, entre outros
    access-control-allow-origin: * é interessante porque significa que JavaScript de uma página web em qualquer domínio pode acessar esse conteúdo com fetch(), e default-src 'none'; sandbox é uma configuração muito restritiva, o que é bom: impede o carregamento de scripts ou imagens adicionais e também bloqueia a execução de JavaScript: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Co...
    ratelimit-limit: 3000 provavelmente parece ser um limite por IP

    • Bloquear todo JavaScript ou restringi-lo a uma lista de permissões é praticamente a única forma de confinar totalmente um app com CSP e impedir vazamento de dados; se houver prefetch, isso talvez nem seja suficiente
      A sugestão no fim do texto de usar WebAssembly está na direção certa, e o problema dos cliques em links pode ser limitado colocando código não confiável em um iframe e permitindo, pelo pai com child-src, apenas domínios específicos ou data:
      https://github.com/w3c/webappsec/issues/656#issuecomment-246...
      https://www.w3.org/TR/CSP3/#exfiltration
    • Fico na dúvida se, mesmo usando sandbox, default-src ainda é necessário, ou se isso é uma configuração redundante
  • Estou bem animado com a possibilidade de usar o Bluesky como repositório de dados blob
    Pensei, junto com um amigo, em armazenar WADs de DOOM no Bluesky e compartilhar “map packs” como se fossem posts; se você seguir contas, listas e starter packs, daria para modificar um cliente como o GZDoom para pesquisar e ver os WADs enviados por essas contas
    Seria parecido com o Steam Workshop, mas passando pelo Bluesky

    • Ideia legal; fico curioso se a intenção é hospedar isso por conta própria ou subir nos servidores do Bluesky
      Também não sei se o Bluesky já mencionou diretrizes para uso como armazenamento de blobs; arquivos de DOOM são pequenos, então são um bom exemplo, mas fico me perguntando se isso poderia ser abusado como forma de externalizar espaço de servidor em grande escala
    • No fim das contas, não é basicamente usar o Bluesky como um feed RSS para dados arbitrários?
  • Não vivi essa época específica, mas ver usuários do BlueSky se jogando em águas técnicas mais profundas me lembra os tempos em que as pessoas aprendiam HTML pela primeira vez no MySpace
    O mercado de redes sociais está muito mais saturado do que antes, mas fico curioso se uma nova geração de programadores pode surgir do BlueSky

    • MySpace e fóruns antigos ofereciam uma escada de abstração que começava com colocar texto em uma caixa para ele aparecer numa página web, depois adicionar imagens, BBCode/markup, HTML e CSS, até chegar à escrita de scripts completos
      O Bluesky oferece só o primeiro degrau, e a partir daí parece haver um salto bem grande
    • Dizem que é “14 minutos”, mas vejo cerca de uma dúzia de links que parecem leitura obrigatória para tentar uma ideia nova
      Com HTTP/HTML, dá para mostrar a alguém que só conhece Python básico ou outra linguagem de programação como criar um servidor do zero em 14 minutos
      Acho que precisamos de um protocolo de comunicação com endereçamento por hash para impedir que grandes empresas de tecnologia explorem comunidades, mas não tenho certeza de que o AT Protocol tenha acertado o alvo o bastante para desencadear uma revolução como o HTTP
  • Um dos pontos principais aqui é que o PDS em questão não é o produto Bluesky em si, mas parte de um Personal Data Server, então o resultado pode parecer um armazenamento de dados gratuito e infinito
    O serviço de assinatura que a equipe do Bluesky mencionou algumas vezes parece poder estar ligado a isso, e uma hospedagem que ofereça mais espaço, largura de banda e vídeo em alta qualidade no PDS parece adequada para ser vendida como um nível premium

    • Se entendi corretamente, esse PDS era hospedado pelo Bluesky
      Deve ser possível hospedá-lo em outros lugares também, então parece correto dizer que dá para interagir para além do app do Bluesky
  • Parece que precisamos de um nome para o fenômeno em que, se você cria um jeito de colocar algo na internet, eventualmente alguém vai tentar fazer hospedagem arbitrária de arquivos em cima disso

    • Provavelmente é por isso que é importante que as contas do Google agora tenham um limite de 15 GB até que se pague a mais
      Na época em que era ilimitado, surgiram vários projetos open source para fazer backup de sistemas de arquivos no Gmail etc.; depois que o Drive apareceu, ficou ainda pior
      Serviços gratuitos precisam impor limites básicos já prevendo que esse tipo de problema vai surgir
    • Como já existe o termo “parasitic computation”, daria para chamar isso de “parasitic data storage
    • Lei de Johnson: quanto mais atenção algo recebe, maior também se torna seu raio de impacto
    • É algo próximo do Inner Platform effect
      https://en.wikipedia.org/wiki/Inner-platform_effect
  • Se esse tema parecer interessante, vale ver também o atfile: https://github.com/electricduck/atfile

  • É bem legal, e dá para ver direto uma issue interessante do GitHub linkada no fim do texto
    A figura conhecida do Bluesky, pfrazee, também aparece: https://github.com/bluesky-social/atproto/issues/523
    Tenho grandes expectativas para o AT, e certamente há muitas pessoas inteligentes que fizeram um excelente trabalho no Fediverse, mas esse paradigma parece mais sustentável e realista
    A ideia é oferecer uma base centralizada, mas dar suporte real à descentralização quando necessário ou para usuários avançados

    • Em termos de sustentabilidade, espero um modelo de negócio melhor do que “receber financiamento da Blockchain Capital”
      É preciso haver uma forma de gerar retorno sobre o investimento com o espelhamento do firehose; por exemplo, uma alternativa ao Discord em que alguns usuários paguem pelo custo de hospedar vídeos mais longos, uma estrutura no estilo Patreon em que relays gerenciem chaves de acesso e descriptografia e cobrem uma taxa, ou até um marketplace social como o Bandcamp
      De todo modo, como é uma plataforma aberta, não há motivo para que isso já não possa ser feito
      https://www.blockchaincapital.com/blog/bluesky-13m-users-and...
      https://bsky.social/about/blog/09-11-2024-video
  • Fico com a impressão de que esse método pode ser abusado para phishing ou distribuição de malware

    • Será que existe algum site de hospedagem que não seja abusado?
      Parece quase uma lei da computação: se você cria um site de hospedagem, alguém vai tentar usá-lo para fins maliciosos
    • Na prática, é isso que vai acontecer
      O mesmo problema já existiu com anexos no Matrix
    • Em uns 5 minutos, *.bsky.network deve começar a ser bloqueado pelo Google Safe Browsing, Palo Alto, Bluecoat etc.
    • Não sei bem
      Isso é um link direto para o servidor Bluesky do autor, ou seja, para o PDS, então naturalmente está sob controle do autor
  • As mudanças recentes na API do Strava me fizeram lembrar o quanto o acesso aos dados armazenados na plataforma deles é restrito
    Como é o serviço dominante na área de fitness, eles também poderiam colocar recursos gradualmente atrás de uma assinatura
    Pode haver preocupações de privacidade ou segurança, mas fico me perguntando se o AT Protocol poderia servir como uma plataforma de armazenamento para arquivos GPX ou FIT

    • Seria bom ter uma alternativa federada ao Strava
      Mas, até onde sei, o AT Protocol ainda não oferece suporte a postagens privadas ou com visibilidade limitada, e vejo isso como algo bastante essencial para o uso no estilo Strava