- Ataques à cadeia de suprimentos acontecem quando atualizações maliciosas se infiltram no código open source, e o Obsidian usa uma estratégia de reduzir as próprias dependências externas para minimizar isso
- A maior parte das funcionalidades do app é implementada internamente ou, quando necessário, código com fork é incorporado e mantido dentro da própria base de código
- Bibliotecas grandes e indispensáveis (
pdf.js, Mermaid, MathJax etc.) têm sua versão fixada para garantir segurança, e as atualizações só são feitas com cautela quando há patches de segurança
- Todas as dependências são fixadas por lockfile, e scripts de
postinstall não são executados, bloqueando a execução de código arbitrário durante a instalação
- Com esse processo cauteloso de atualização e estratégia de atraso no tempo, o Obsidian consegue reagir antes que ameaças potenciais sejam detectadas pela comunidade
O que é um ataque à cadeia de suprimentos
- Um ataque à cadeia de suprimentos é uma forma de infiltrar atualizações maliciosas no código distribuído dentro do ecossistema open source
- Como muitos apps usam código open source, uma única atualização maliciosa pode afetar em cascata vários aplicativos
- O Obsidian reduz essa superfície de ataque por meio de uma estratégia de minimização de dependências, projetando o app para ser mais seguro
Estratégia de minimização de dependências: Less is Safer
- O Obsidian depende de um número muito pequeno de bibliotecas externas em comparação com outros apps da mesma categoria
- Funcionalidades principais (por exemplo, Bases e Canvas) são implementadas internamente em vez de adotar bibliotecas externas
- Isso permite manter controle total sobre o código executado
- Funções utilitárias pequenas quase sempre são implementadas diretamente pela equipe de desenvolvimento
- Módulos de porte intermediário são forkados e incluídos na base de código quando a licença permite
- Bibliotecas grandes (
pdf.js, Mermaid, MathJax etc.) são incluídas com versões validadas e fixadas, com upgrades mínimos apenas quando surgem questões de segurança importantes
- Todas as mudanças externas são revisadas em detalhes, com um processo rigoroso de testes
- Dessa forma, o número de subdependências também é minimizado, reduzindo o risco primário de entrada de código malicioso
O que realmente entra no app
- No app que o usuário realmente executa, entram apenas pouquíssimos pacotes, como Electron, CodeMirror e
moment.js
- As demais ferramentas de desenvolvimento são usadas apenas no processo de build e não são entregues ao usuário final
Fixação de versões e gerenciamento do lockfile
- Todas as dependências externas são gerenciadas com pin rígido de versão e commit do lockfile
- Isso garante que a instalação seja sempre reproduzível e facilita o rastreamento das mudanças
- Com a política de não executar scripts de
postinstall, elimina-se na origem a possibilidade de execução de código arbitrário durante a instalação
Processo lento e cuidadoso de atualização
- Quando é necessário fazer upgrade de dependências, o processo segue uma revisão sistemática como a abaixo
- revisão minuciosa, linha por linha, do changelog
- verificação de novas subdependências adicionadas na nova versão
- se a mudança for grande ou houver risco, conferência direta do diff em relação ao código upstream
- execução de testes automáticos e manuais nos principais fluxos e plataformas
- o lockfile só é comitado depois que todas essas etapas forem aprovadas
- Como a maioria das dependências não precisa mudar com frequência, a própria frequência de atualização é baixa
- A introdução de novo código externo é avaliada e gerenciada no mesmo nível da adoção de uma nova dependência
"Tempo de folga" para estabilidade: Time is a buffer
- Os upgrades não são implantados imediatamente, e sim atrasados por um certo período
- Durante esse intervalo, a comunidade open source e pesquisadores de segurança podem detectar versões maliciosas, funcionando como uma janela de defesa antecipada
- Quando chega o momento da implantação real, a chance de o problema já ter sido identificado é maior, o que ajuda a minimizar o risco
Conclusão
- Uma única medida de segurança não é suficiente para eliminar completamente o risco de ataques à cadeia de suprimentos
- Ainda assim, o Obsidian reduz esse risco de forma significativa ao combinar minimização de dependências, grafo raso, fixação de versões, proibição de
postinstall e atualizações lentas baseadas em revisão
- Com esse processo, também se ganha muito mais tempo para detectar riscos antes que o código chegue aos usuários
- A abordagem completa de segurança do Obsidian e o histórico de auditorias de segurança podem ser consultados na security page oficial
1 comentários
Comentários do Hacker News
Muita gente parece ignorar que a maioria dos usuários usa plugins comunitários de terceiros no Obsidian; na prática, o modelo de segurança dos plugins do Obsidian é muito fraco, já que os plugins têm permissão para acessar todos os arquivos dentro do vault. Se o Obsidian tivesse se esforçado para incorporar mais recursos diretamente, o risco de segurança teria sido menor. Outra possibilidade seria melhorar a estrutura como nas extensões de navegador, explicitando quais permissões o plugin usa e bloqueando acessos não autorizados. Tudo isso oferece uma segurança real para o usuário muito maior do que a lógica de “depender menos de terceiros”.
Antigamente, algumas pessoas do mundo do software falavam sobre casos em que ideias de design de videogames acabavam influenciando outros tipos de software, mas hoje quase não se ouve mais isso. Seria de grande ajuda para todo o ecossistema de software se alguém do núcleo das antigas empresas de jogos, como a Blizzard, escrevesse um livro detalhando como o sistema de plugins do World of Warcraft funcionou durante seus primeiros 10 anos, quais foram os problemas e como a segurança foi reforçada. Muitos sistemas de plugins de vários projetos são uma mistura de descuido e improviso.
Os plugins do Obsidian podem acessar não só os arquivos do vault, mas todos os arquivos do computador. Já apontei isso no Discord antes, mas fui ignorado.
Vejo isso de forma parecida com o Arch Linux. Mesmo para engenheiros, segurança já é difícil quando se gerencia software diretamente pelo AUR; esperar que o usuário comum assuma essa responsabilidade é demais.
Acho que em breve vai aparecer algum caso de vazamento de dados envolvendo plugins do Obsidian. Quando isso acontecer, o time vai introduzir mecanismos de segurança. No mínimo, é preciso implementar um sistema de publicadores verificados.
Estou desenvolvendo plugins comerciais para o Obsidian e gostaria que houvesse um processo de revisão mais rigoroso para plugins de um certo porte para cima. Se existissem dois repositórios — um gerido pela comunidade, como o AUR do Arch Linux, e outro com revisão mais rígida — isso ajudaria tanto a melhorar a velocidade de análise dos plugins quanto a aumentar a segurança.
Há a explicação de que “ataques à cadeia de suprimentos são atualizações maliciosas escondidas em código open source usado por muitos apps”, mas qualquer código-fonte, não apenas FOSS, pode ser alvo de ataque. A percepção de que FOSS é necessariamente mais vulnerável é problemática.
A política de “não executar scripts
postinstalldurante a instalação” tem boa intenção, mas se o pacote já estiver comprometido, pular opostinstallnão torna o restante do código seguro. Se o pacote estiver íntegro, opostinstalltambém pode ajudar numa instalação legítima. Como, na prática, incidentes acontecem mais por correções comuns de vulnerabilidades do que por ataques à cadeia de suprimentos, bloquear atualizações pode até aumentar o risco.Hoje em dia, o escaneamento de segurança normalmente é feito depois da instalação (
post install). É melhor impedir que qualquer coisa seja executada durante a instalação. Espero que no futuro haja mais recursos de escaneamento ou restrição já na etapa de instalação. Algumas ferramentas comerciais já oferecem isso, mas ainda não é algo comum.Ainda assim, isso faz sentido para proteger a máquina de build. Você não precisa se preocupar com scripts arbitrários sendo executados a partir das suas inúmeras dependências.
O desenvolvedor é responsável por todo código distribuído ao usuário. Se você não fixa as dependências, está basicamente “baixando código aleatório da internet e contando com a sorte”.
Fixar dependências pode fazer você perder patches de segurança futuros. Isso também é arriscado, então é indispensável ter algum sistema que permita perceber quando novos patches de segurança são lançados. Aí é preciso fazer backport da correção ou atualizar para uma nova versão.
Mesmo dependências fixadas incluem outras dependências, então no fim a estrutura continua sendo sempre “baixar código aleatório e torcer”. Especialmente em apps baseados em Electron, vem junto uma quantidade realmente enorme de código.
Entre os conselhos que vejo com frequência ultimamente está o de não atualizar dependências nem quando sai um patch release, e isso não faz sentido para mim. Deixar de atualizar talvez reduza o risco de instalar malware, mas normalmente patches existem para melhorar a segurança. Fico em dúvida se não aplicar os patches mais recentes é mesmo uma escolha sensata.
O ponto principal é entender por que você vai aplicar um patch e o que mudou. Ninguém tem tempo de ler todo o código-fonte, então uso ferramentas e serviços importantes, como o Npm Audit, para verificar resumos de vulnerabilidades. Minha estratégia é adiar atualizações a menos que sejam realmente necessárias, porque atualizar também é um vetor de ataque e uma das principais fontes de bugs. Mas eu verifico periodicamente a quais vulnerabilidades estou exposto. Se a falha estiver em um recurso que eu não uso, às vezes adio a atualização. Só atualizo imediatamente quando o defeito é realmente crítico. Segurança é um processo ativo e contínuo, e a resposta deve variar conforme o nível de tolerância a risco da organização. A resposta não é simplesmente “atualize sempre” ou “nunca atualize”.
Hoje em dia, toda vez que atualizo o Z-WaveJS UI, as atualizações de dependências se repetem. Estou acompanhando isso manualmente por causa desse padrão frustrante. Hoje todo mundo depende de dependências, então “não tem fim”; um único auto-update já pode ser arriscado.
Sempre existe risco de melhoria ou regressão ao atualizar, e esse risco é muito maior no ecossistema npm (onde o Obsidian se encaixa). No npm, a remoção de pacotes maliciosos exige intervenção humana, então a resposta é lenta. Atrasar atualizações de propósito pode ser uma forma de defesa.
Ultimamente tenho esperado um pouco depois de um patch release antes de instalar. Hoje em dia, muitos incidentes são detectados em poucas horas. Várias empresas monitoram o npm e até fazem negócio com segurança em cima disso. O pnpm pode ser configurado para instalar apenas pacotes publicados há mais de X minutos; eu costumo esperar pelo menos 24 horas configuração
minimumreleaseagedo pnpmO ataque que meu pacote sofreu duas semanas atrás mirava justamente um patch release. Não era um script
postinstall. Como o escaneamento automatizado detecta isso rapidamente, esse problema já não chama tanta atenção. Quando uma vulnerabilidade é encontrada num pacote, o alerta chega imediatamente e de forma clara. Usar version ranges é péssimo para lidar com ataques à cadeia de suprimentos.Tenho dificuldade em concordar com a afirmação de que o pacote não é tão grande por incluir apenas Electron, CodeMirror e moment.js. O Electron é um software com complexidade enorme por ser baseado em webview, e o moment.js já tem APIs melhores hoje em dia. O nível de gestão de dependências do Obsidian me parece mais o mínimo aceitável do que uma política de segurança particularmente impressionante. Ainda assim, é positivo que façam auditorias de segurança regulares.
Eu vinha usando outros apps em vez do Obsidian, mas comecei a me interessar por ele também. O fato de ser um app em Electron me incomoda porque consome muitos recursos, não é nativo e JavaScript sempre me passa certa insegurança. Não sei se isso é só nostalgia da minha parte.
JavaScript é uma linguagem muito segura. Os navegadores são um exemplo bem-sucedido de execução segura de JavaScript em escala mundial: cada site não consegue ler os dados dos outros. O Electron também faz sandbox do JavaScript no motor v8. Se você evitar executar código vindo da entrada do usuário, é bastante seguro. O problema dos ataques à cadeia de suprimentos é do npm em si, não da linguagem JS. O npm tem a responsabilidade de aplicar políticas de segurança mais rígidas na publicação de pacotes.
JavaScript é, por qualquer critério, uma das linguagens mais usadas do mundo. Roda em praticamente todos os computadores e smartphones. Por isso, problemas de segurança são encontrados com mais frequência. Não há base para dizer que apps nativos sejam mais seguros.
Apps em Electron não são necessariamente um problema. GitHub, VS Code, Slack, Discord e Postman são todos baseados em Electron. Eu sempre fico com vontade de perguntar: que tipo de tarefa alguém faz num app de notas em Markdown para que desempenho vire um problema real? Dá quase para imaginar a pessoa usando um notebook muito antigo e vendo tudo em modo texto no navegador Lynx.
Não gosto muito de Electron (por isso gosto de Tauri), mas o Obsidian em si é excelente, então não há motivo para descartá-lo só por causa do Electron. Também recomendo porque ele funciona bem integrado com MCP e serve muito bem como base de conhecimento pessoal.
Electron é pesado mesmo. No PC isso raramente é um grande problema, mas no mobile, quando você acumula milhares de notas, até sem plugins o app passa a abrir devagar. Os usuários acabam contornando isso com plugins/apps de captura rápida, mas eu gostaria que existisse um Obsidian nativo mais leve.
O Obsidian é baseado em Electron e, por essa própria característica, carrega peso e risco de vulnerabilidades de segurança.
Uso Emacs com Org-Roam e rodo isso numa VM sem conexão de rede (um Qube do Qubes OS). Não consigo revisar pessoalmente todo o código que é executado dentro do Emacs.
Se você quer um app nativo com risco menor de cadeia de suprimentos, uma alternativa é o Zim Wiki, baseado em GTK e empacotado nas principais distribuições Linux acessar o Zim Wiki