Práticas recomendadas para execução de código não confiável

31

Eu tenho um projeto em que preciso permitir que os usuários executem código python arbitrário e não confiável ( um pouco como este ) no meu servidor. Sou bastante novo em python e gostaria de evitar erros que introduzam falhas de segurança ou outras vulnerabilidades no sistema. Existem práticas recomendadas, leituras recomendadas ou outras dicas que você pode me dar para tornar meu serviço utilizável, mas não abusivo?

Aqui está o que eu considerei até agora:

  • Remova __builtins__do execcontexto para proibir o uso de pacotes potencialmente perigosos como os. Os usuários poderão usar apenas os pacotes que eu forneço a eles.
  • Use threads para impor um tempo limite razoável.
  • Gostaria de limitar a quantidade total de memória que pode ser alocada dentro do execcontexto, mas não tenho certeza se é possível.

Existem algumas alternativas para uma sequência exec, mas não tenho certeza qual delas seria útil aqui:

  • Usando um ast.NodeVisitorpara capturar qualquer tentativa de acessar objetos não seguros. Mas que objetos devo proibir?
  • Procurando por qualquer sublinhado duplo na entrada. (menos elegante que a opção acima).
  • Usando PyPyou algo semelhante à sandbox do código.

NOTA: Estou ciente de que há pelo menos um intérprete baseado em JavaScript. Isso não vai funcionar no meu cenário.

pswg
fonte
3
@MartijnPieters: Excelente. Provavelmente digno de uma resposta, se você resumir cada uma.
Robert Harvey
Considere também: lixo deixado no disco, rede (não permita que eles enviem spam ou o que for), permissões para outros arquivos (lendo seus arquivos). Até a ejeção no loop while pode destruir a mecânica do CD ... Eu usaria a virtualização (cadeias ou algum kvm que você escolher) ou pelo menos o usuário quase sem privilégios. Defina uma quantidade razoável e agradável de memória para aproveitar seus próprios programas.
kyticka
1
Experimente o PyPy :> Sandboxing: O PyPy oferece a capacidade de executar códigos não confiáveis ​​de uma maneira totalmente segura.
Vorac 12/09

Respostas:

28

O sandbox do Python é difícil . Python é inerentemente introspectável, em vários níveis.

Isso também significa que você pode encontrar os métodos de fábrica para tipos específicos desses tipos e construir novos objetos de baixo nível, que serão executados diretamente pelo intérprete, sem limitação.

Aqui estão alguns exemplos de como encontrar maneiras criativas de sair das caixas de proteção do Python:

A idéia básica é sempre encontrar uma maneira de criar tipos básicos de Python; funções e classes e quebre o shell fazendo com que o interpretador Python execute bytecode arbitrário (não verificado!).

O mesmo e mais se aplicam à execinstrução ( exec()função no Python 3).

Então, você quer:

  • Controle estritamente a compilação de bytes do código Python ou, pelo menos, pós-processe o bytecode para remover qualquer acesso aos nomes que começam com sublinhados.

    Isso requer conhecimento profundo de como o interpretador Python funciona e como o bytecode do Python está estruturado. Objetos de código estão aninhados; o bytecode de um módulo cobre apenas o nível superior de instruções, cada função e classe consiste em sua própria sequência de bytecode mais metadados, contendo outros objetos de bytecode para funções e classes aninhadas, por exemplo.

  • Você precisa colocar os módulos na lista de permissões que podem ser usados. Cuidadosamente.

    Um módulo python contém referências a outros módulos. Se você importar os, existe um nome local osno namespace do módulo que se refere ao osmódulo. Isso pode levar um invasor determinado a módulos que podem ajudá-lo a sair da área restrita. O picklemódulo, por exemplo, permite carregar objetos de código arbitrários, por exemplo, portanto, se algum caminho através dos módulos na lista de permissões levar ao picklemódulo, você ainda terá um problema.

  • Você precisa limitar estritamente as cotas de tempo. Mesmo o código mais neutralizado ainda pode tentar executar para sempre, amarrando seus recursos.

Dê uma olhada no RestrictedPython , que tenta fornecer o controle estrito do bytecode. RestrictedPythontransforma o código Python em algo que permite controlar quais nomes, módulos e objetos são permitidos no Python 2.3 até 2.7.

Se RestrictedPythonfor suficientemente seguro para seus propósitos, depende das políticas que você implementa. Não permitir o acesso a nomes começando com um sublinhado e estritamente na lista de permissões dos módulos seria um começo.

Na minha opinião, a única opção verdadeiramente robusta é usar uma máquina virtual separada, uma que não tenha acesso à rede para o mundo externo que você destrói após cada execução. Cada novo script recebe uma nova VM. Dessa forma, mesmo que o código consiga sair da sua caixa de proteção do Python (o que não é improvável), tudo o que o invasor obtém acesso é de curta duração e sem valor.

Martijn Pieters
fonte
10

TL; DR Use um chroot / jail e execute como um usuário personalizado sem privilégios.

A melhor prática para executar código não confiável é segregá-lo por meio de uma caixa de proteção do sistema . Para maior segurança:

  • crie um contêiner apenas com Python e suas dependências e as dependências do contêiner
  • crie um contêiner sem todos os dispositivos que não são absolutamente necessários (por exemplo, rede e armazenamento)
  • criar um contêiner com restrições de uso de memória e processo
  • recrie o contêiner a cada execução (ou pelo menos a cada usuário exclusivo e período máximo)
  • executar como um usuário com o menor privilégio necessário
  • executado como um usuário que não tem permissão para gravar arquivos

Você também segue práticas padrão para executar coisas com segurança em um chroot. Você pode reconstruir o sistema de arquivos do chroot a cada chamada também é particularmente paranóico. Normalmente, você apenas torna o usuário incapaz de fazer modificações no sistema de arquivos em que o chroot é executado.

dietbuddha
fonte
Essa é a única coisa em que você terá certeza remota de que está certo - dê um processo próprio.
Michael Kohne
3

Não há como você fazer isso com segurança.

Se você quiser fazer algo assim com segurança, terá que começar com sua própria implementação de python, que roda em um ambiente completamente controlado, de preferência no navegador do usuário, e não no sistema. Você pode começar com Jython (python para java) e empacotá-lo como um applet java. Como estaria em execução na sandbox java, na máquina do usuário, seu sistema estaria razoavelmente seguro.

ddyer
fonte
4
A questão da segurança era do servidor, não da máquina do cliente. Os riscos potenciais de segurança de Java, como os de qualquer outra tecnologia da web, são que o servidor possa ser usado para implantar programas perigosos para o cliente.
Ddyer
1
O @grasGendarme, bem como novas histórias sobre acidentes de avião, na verdade, dizem muito sobre o quão raras são; histórias sobre falhas de segurança em java dizem que o java é comparativamente seguro. Você nunca chegaria a tal história sobre C porque a resposta que você deseja obter seria "bem duh, se você executá-lo ele vai fazer o que quiser"
Richard Tingle
2

Como Martijn disse acima, isso é realmente muito difícil em Python. Para ser franco, porque o Python é tão introspectável, não acho que seja possível limitando os recursos da linguagem. E se você tiver uma sandbox trabalhando para uma versão do Python, há uma chance de que a próxima versão a interrompa.

Eu daria uma olhada no PyPy em vez do CPython padrão. Em resumo, é uma implementação alternativa compatível do Python. Ele tem várias vantagens e recursos distintos, e um deles é o sandbox via substituição de chamadas do sistema, em vez de limitar os recursos de idioma.

James
fonte
0

Desde que o desempenho não seja extremamente importante para você, você sempre pode executá-lo no Brython, o que efetivamente o coloca na sandbox JavaScript

Big Ian
fonte