Execute o código não confiável em seu próprio segmento. Isso, por exemplo, evita problemas com loops infinitos e outros, e torna as etapas futuras mais fáceis. Faça com que o thread principal espere até que o thread termine e, se demorar muito, elimine-o com Thread.stop. Thread.stop está obsoleto, mas como o código não confiável não deve ter acesso a nenhum recurso, seria seguro eliminá-lo.
Defina um SecurityManager nesse Thread. Crie uma subclasse de SecurityManager que sobrescreve checkPermission (Permission perm) para simplesmente lançar uma SecurityException para todas as permissões, exceto algumas. Há uma lista de métodos e as permissões que eles exigem aqui: Permissões no Java TM 6 SDK .
Use um ClassLoader personalizado para carregar o código não confiável. Seu carregador de classes seria chamado para todas as classes que o código não confiável usa, portanto, você pode fazer coisas como desabilitar o acesso a classes JDK individuais. A única coisa a fazer é ter uma lista branca de classes JDK permitidas.
Você pode querer executar o código não confiável em uma JVM separada. Embora as etapas anteriores tornem o código seguro, há uma coisa irritante que o código isolado ainda pode fazer: alocar o máximo de memória possível, o que faz com que a pegada visível do aplicativo principal aumente.
JSR 121: Especificação de API de isolamento de aplicativo foi projetada para resolver isso, mas infelizmente ainda não tem uma implementação.
Este é um tópico bem detalhado, e principalmente estou escrevendo isso de cima da minha cabeça.
Mas de qualquer maneira, algum código imperfeito, use por sua própria conta e risco, provavelmente com erros (pseudo):
ClassLoader
class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name is white-listed JDK class) return super.loadClass(name);
return findClass(name);
}
@Override
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the untrusted class data here
}
}
Gerente de segurança
class MySecurityManager extends SecurityManager {
private Object secret;
public MySecurityManager(Object pass) { secret = pass; }
private void disable(Object pass) {
if (pass == secret) secret = null;
}
// ... override checkXXX method(s) here.
// Always allow them to succeed when secret==null
}
Fio
class MyIsolatedThread extends Thread {
private Object pass = new Object();
private MyClassLoader loader = new MyClassLoader();
private MySecurityManager sm = new MySecurityManager(pass);
public void run() {
SecurityManager old = System.getSecurityManager();
System.setSecurityManager(sm);
runUntrustedCode();
sm.disable(pass);
System.setSecurityManager(old);
}
private void runUntrustedCode() {
try {
// run the custom class's main method for example:
loader.loadClass("customclassname")
.getMethod("main", String[].class)
.invoke(null, new Object[]{...});
} catch (Throwable t) {}
}
}
Thread.stop
causará problemas no código da biblioteca Java. Da mesma forma, o código da biblioteca Java exigirá permissões. Muito melhor permitir oSecurityManager
usojava.security.AccessController
. O carregador de classes provavelmente também deve permitir o acesso às próprias classes do código do usuário.System.setSecurityManager(…)
afetará toda a JVM, não apenas o encadeamento que invoca esse método, a ideia de tomar decisões de segurança com base no encadeamento foi abandonada quando o Java mudou de 1.0 para 1.1. Neste momento, foi reconhecido que o código não confiável pode invocar código confiável e vice-versa, independentemente de qual thread executa o código. Nenhum desenvolvedor deve repetir o erro.Obviamente, tal esquema levanta todos os tipos de preocupações de segurança. Java tem uma estrutura de segurança rigorosa, mas não é trivial. A possibilidade de bagunçar tudo e permitir que um usuário sem privilégios acesse componentes vitais do sistema não deve ser esquecida.
Esse aviso à parte, se você estiver pegando a entrada do usuário na forma de código-fonte, a primeira coisa que você precisa fazer é compilá-la para bytecode Java. AFIAK, isso não pode ser feito nativamente, então você precisará fazer uma chamada de sistema para javac e compilar o código-fonte para bytecode no disco. Aqui está um tutorial que pode ser usado como ponto de partida para isso. Edit : como aprendi nos comentários, você realmente pode compilar o código Java da fonte nativamente usando javax.tools.JavaCompiler
Depois de ter o bytecode JVM, você pode carregá-lo na JVM usando a função defineClass de um ClassLoader . Para definir um contexto de segurança para esta classe carregada, você precisará especificar um ProtectionDomain . O construtor mínimo para um ProtectionDomain requer um CodeSource e um PermissionCollection . O PermissionCollection é o objeto de uso principal para você aqui - você pode usá-lo para especificar as permissões exatas que a classe carregada possui. Essas permissões devem ser aplicadas pelo AccessController da JVM .
Há muitos pontos de erro possíveis aqui, e você deve ser extremamente cuidadoso para entender tudo completamente antes de implementar qualquer coisa.
fonte
O Java-Sandbox é uma biblioteca para execução de código Java com um conjunto limitado de permissões. Ele pode ser usado para permitir o acesso a apenas um conjunto de classes e recursos da lista branca. Não parece ser capaz de restringir o acesso a métodos individuais. Ele usa um sistema com um carregador de classes e gerenciador de segurança personalizados para fazer isso.
Não o usei, mas parece bem desenhado e razoavelmente bem documentado.
@waqas deu uma resposta muito interessante explicando como isso é possível implementar você mesmo. Mas é muito mais seguro deixar esse código complexo e crítico de segurança para especialistas.
Observe, porém, que o projeto não é atualizado desde 2013 e os criadores o descrevem como "experimental". Sua página inicial desapareceu, mas a entrada do Source Forge permanece.
Exemplo de código adaptado do site do projeto:
fonte
Para resolver o problema na resposta aceita em que o customizado
SecurityManager
se aplicará a todos os threads na JVM, em vez de por thread, você pode criar um customSecurityManager
que pode ser ativado / desativado para threads específicos da seguinte maneira:ToggleSecurirtyManagerPermission
é apenas uma implementação simples dejava.security.Permission
para garantir que apenas o código autorizado pode ativar / desativar o gerenciador de segurança. Se parece com isso:fonte
Bem, é muito tarde para dar sugestões ou soluções, mas mesmo assim eu estava enfrentando um problema semelhante, mais voltado para a pesquisa. Basicamente, eu estava tentando fornecer uma provisão e avaliações automáticas para atribuições de programação para o curso Java em plataformas de e-learning.
Eu sei que isso parece muito complexo e com muitas tarefas, mas o Oracle Virtual Box já fornece API Java para criar ou clonar máquinas virtuais dinamicamente. https://www.virtualbox.org/sdkref/index.html (observe que até o VMware também fornece API para fazer o mesmo)
E para o tamanho mínimo e a configuração da distribuição do Linux, você pode consultar este aqui http://www.slitaz.org/en/ ,
Portanto, agora se os alunos bagunçarem ou tentarem fazer isso, pode ser com memória ou sistema de arquivos ou rede, soquete, no máximo ele pode danificar sua própria VM.
Também internamente nessas VMs, você pode fornecer segurança adicional como Sandbox (gerenciador de segurança) para Java ou criar contas específicas de usuário no Linux e, assim, restringir o acesso.
Espero que isto ajude !!
fonte
Esta é uma solução thread-safe para o problema:
https://svn.code.sf.net/p/loggifier/code/trunk/de.unkrig.commons.lang/src/de/unkrig/commons/lang/security/Sandbox.java
Por favor comente!
CU
Arno
fonte
Você provavelmente precisará usar um SecurityManger e / ou AccessController personalizado . Para obter muitos detalhes, consulte Java Security Architecture e outras documentações de segurança da Sun.
fonte