Estou tentando criar uma estrutura ACL flexível em Java para o meu aplicativo.
Muitas estruturas da ACL são criadas em uma lista de permissões, onde uma regra está na forma de owner: action: resource . Por exemplo,
- "JOHN pode visualizar o recurso FOOBAR-1"
- "MARY pode visualizar o recurso FOOBAR-1"
- "MARY pode editar o recurso FOOBAR-1"
Isso é atraente porque as regras podem ser facilmente serializadas / persistidas em um banco de dados. Mas meu aplicativo tem lógica de negócios complexa. Por exemplo,
- "Todos os usuários do departamento 1 com mais de 5 anos de antiguidade podem VISUALIZAR o recurso FOOBAR-1, caso contrário não estão autorizados"
- "Todos os usuários do departamento 2, se a data for posterior a 15/03/2016, podem VER o recurso FOOBAR-2, caso contrário não autorizado"
Pensando bem, seria um pesadelo conceber um esquema de banco de dados que pudesse lidar com regras infinitamente complexas como essas. Portanto, parece que eu precisaria "inseri-los" no aplicativo compilado, avaliá-los para cada usuário e, em seguida, produzir regras owner: action: resource como resultado da avaliação. Eu quero evitar inserir a lógica no aplicativo compilado.
Então, eu estava pensando em representar uma regra na forma de predicado : action: resource , em que o predicado é uma expressão booleana que determina se um usuário é permitido. O predicado seria uma string de uma expressão JavaScript que poderia ser avaliada pelo mecanismo Rhino do Java. Por exemplo,
return user.getDept() == 1 && user.seniority > 5;
Ao fazer isso, os predicados podem ser facilmente persistidos no banco de dados.
Isso é inteligente ? Isso é desleixado ? Isso é enigmático ? Isso é excesso de engenharia ? Isso é seguro (aparentemente, o Java pode proteger o mecanismo Rhino).
fonte
Respostas:
A inserção de dados dinâmicos em um intérprete da sua linguagem de implementação geralmente é uma má ideia, pois aumenta o potencial de corrupção de dados e a aquisição de aplicativos mal-intencionados. Em outras palavras, você está se esforçando para criar uma vulnerabilidade de injeção de código .
Seu problema pode ser melhor resolvido por um mecanismo de regras ou talvez por uma linguagem específica de domínio (DSL) . Olhe esses conceitos, não há necessidade de reinventar a roda.
fonte
==
vez de===
no seu exemplo. Deseja realmente garantir a integridade quando todas as regras sempre devem terminar? Em vez de pular os bastidores para garantir que todas as interações entre Java e JavaScript sejam kosher, por que você não escreve apenas um analisador e interpretador simples, como sugeriu Kilian? Será muito mais fácil adaptar-se às suas necessidades e proteger. Use ANTLR ou algo assim.while (true) ;
Fiz isso e recomendo que não.
O que fiz foi escrever toda a lógica comercial em Lua e armazenar esse script Lua em um banco de dados. Quando meu aplicativo era iniciado, ele carregava e executava o script. Dessa forma, eu poderia atualizar a lógica comercial do meu aplicativo sem distribuir um novo binário.
Invariavelmente, descobri que sempre precisava atualizar o binário ao fazer alterações. Algumas mudanças estavam no script Lua, mas eu sempre tinha uma lista de mudanças que precisavam ser feitas e, portanto, quase sempre acabava tendo que fazer algumas alterações no binário e algumas no script Lua. Minha imaginação de que eu poderia evitar distribuir binários o tempo todo simplesmente não deu certo.
O que achei muito mais útil foi facilitar a distribuição dos binários. Meu aplicativo verifica automaticamente se há atualizações na inicialização, baixa e instala qualquer atualização. Meus usuários estão, portanto, sempre nos binários mais recentes que enviei. Quase não há diferença entre uma mudança no binário e uma mudança nos scripts. Se eu fizesse de novo, esforçaria ainda mais para tornar a atualização perfeita.
fonte
Eu não gostaria que o banco de dados contivesse código. Mas você pode fazer algo semelhante fazendo com que o banco de dados contenha nomes de funções e use reflexão para chamá-los. Ao adicionar uma nova condição, você deve adicioná-la ao seu código e ao seu banco de dados, mas pode combinar condições e parâmetros que são passados a eles para criar avaliações bastante complexas.
Em outras palavras, se você tiver numerado departamentos, seria fácil fazer uma verificação UserDepartmentIs e TodayIsAfter e depois combiná-los para ter um Departamento = 2 e Hoje> 15/03/2016. Se você deseja fazer uma verificação TodayIsBefore para poder finalizar a permissão da data, você deve escrever a função TodayIsBefore.
Eu não fiz isso para permissões de usuário, mas fiz para validação de dados, mas deve funcionar.
fonte
XACML é a solução que você está realmente procurando. É um tipo de mecanismo de regras focado apenas no controle de acesso. XACML, um padrão definido pelo OASIS, define três partes:
A arquitetura é a seguinte:
Aqui está a aparência do seu primeiro caso de uso:
Seu segundo caso de uso seria:
Você pode combinar os dois casos de uso em uma única política usando referências:
E pronto!
Você pode ler mais sobre XACML e ALFA em:
fonte
O que você realmente deseja aqui é XACML . Ele praticamente fornece exatamente o que você deseja. Você não precisa necessariamente implementar a arquitetura completa com todas as funções sendo completamente separadas ... se você tiver apenas um único aplicativo, provavelmente poderá integrar o PDP e o PEP em seu aplicativo com balana e o PIP é o que for seu banco de dados de usuário existente é.
Agora, em qualquer lugar do seu aplicativo que você precise autorizar algo, crie uma solicitação XACML com o usuário, a ação e o contexto, e o mecanismo XACML tomará a decisão com base nos arquivos de política XACML que você escreveu. Esses arquivos de políticas podem ser mantidos no banco de dados ou no sistema de arquivos ou onde quer que você queira manter a configuração. O Axiomatics tem uma boa alternativa à representação XML XACML chamada ALFA, que é um pouco mais fácil de ler do que o XML bruto, e um plug-in do Eclipse para gerar XML XACML a partir das políticas do ALFA.
fonte
Fizemos isso na minha empresa atual e estamos muito felizes com os resultados.
Nossas expressões são escritas em js e até as usamos para restringir os resultados que os usuários podem obter ao consultar o ElasticSearch.
O truque é garantir que haja informações suficientes disponíveis para tomar a decisão, para que você possa realmente escrever as permissões que desejar, sem alterações no código, mas ao mesmo tempo mantendo-o rápido.
Não estamos realmente preocupados com ataques de injeção de código, pois as permissões são escritas por aqueles que não precisam atacar o sistema. E o mesmo se aplica a ataques do DOS como o
while(true)
exemplo. Os administradores do sistema não precisam fazer isso, eles podem apenas remover as permissões de todos ...Atualizar:
Algo como XACML parece ser melhor como um ponto central de gerenciamento de autenticação para uma organização. Nosso caso de uso é um pouco diferente, pois nossos clientes normalmente não têm um departamento de TI para executar tudo isso. Precisávamos de algo independente, mas tentamos preservar o máximo de flexibilidade possível.
fonte