Quão fácil é hackear JavaScript (em um navegador)?


37

Minha pergunta tem a ver com segurança JavaScript.

Imagine um sistema de autenticação em que você esteja usando uma estrutura JavaScript como Backbone ou AngularJS e precise de pontos de extremidade seguros. Isso não é um problema, pois o servidor sempre tem a última palavra e verifica se você está autorizado a fazer o que deseja.

Mas e se você precisar de um pouco de segurança sem envolver o servidor? Isso é possível?

Por exemplo, digamos que você tenha um sistema de roteamento do lado do cliente e deseje que uma rota concreta seja protegida para usuários logados. Então você faz ping no servidor perguntando se você tem permissão para visitar rotas protegidas e continua. O problema é que, quando você executa ping no servidor, armazena a resposta em uma variável; portanto, na próxima vez em que você for para uma rota privada, ele verificará se você já está logado (sem ping no servidor) e depende na resposta vai ou não.

Quão fácil é para um usuário modificar essa variável e obter acesso?

Meu conhecimento de segurança (e JavaScript) não é bom. Mas se uma variável não está no escopo global e está na parte privada de um padrão de módulo que possui apenas getters, mas não setters, mesmo nesse caso, você pode resolver isso?


29
Todas essas respostas longas. Em suma, para responder ao cabeçalho, "muito hackável". Para responder às suas duas primeiras perguntas alinhadas entre si, "sem servidor, você perdeu a segurança" e& "Não". Para responder à 3ª, "Muito fácil" e, finalmente, "Sim, com facilidade". Ai está. Todas as perguntas respondidas. : P
SpYk3HH

Exemplo excelente, veja meu post sobre como adicionar jQuery a qualquer página da web. spyk3lc.blogspot.com/2013/05/… Agora, jovem padawan, vá em frente e experimente. Veja como é fácil adicionar o jQuery a qualquer site que ainda não o tenha e use o jquery para manipular facilmente qualquer parte da visão sem longas linhas de javascript. Estrondo!
SpYk3HH

7
Uma vez, ao se registrar para um nome de domínio, o número de telefone era um campo obrigatório, mas que era aplicado apenas no javascript - não no lado do servidor. Então, eu a desabilitei redefinindo uma função (usando o Firebug) e pronto! Eles não têm o meu número de telefone.
precisa saber é o seguinte

8
manipulate any part of the sight without long lines site vs sight
mplungjan

8
Programadores profissionais da web precisam acertar essas coisas. Por favor. É embaraço mais de uma coisa gramática nazi :) plus.google.com/u/0/+MonaNomura/posts/h9ywDhfEYxT
mplungjan

Respostas:


26

Por favor, leia a resposta de Joachim antes de ler esta. Ele aborda os motivos gerais por trás da vulnerabilidade do cliente. Agora, para uma sugestão de como você pode contornar esse problema ...

Um esquema seguro para comunicação cliente-servidor sem precisar se autenticar com o servidor manualmente em todas as solicitações:

Você ainda está permitindo que o servidor tenha a última palavra, e ainda precisa validar tudo o que o cliente diz, mas isso acontece de forma transparente.

Suponha que o protocolo HTTPS impeça ataques MITMA ( man-in-the-middle ).

  • O cliente faz handshakes com o servidor pela primeira vez e o servidor gera uma chave pública para o cliente e mantém uma chave privada usando um esquema de criptografia assimétrica. O cliente armazena a chave "pública" do servidor no armazenamento local, criptografada com uma senha segura que você não salva em nenhum lugar.

  • O cliente agora está offline. O cliente deseja executar ações confiáveis. O cliente digita sua senha e pega a chave pública do servidor.

  • O cliente agora executa ações com base em seu conhecimento desses dados e criptografa todas as ações que executa com a chave pública do servidor para esse cliente .

  • Quando o cliente está online, o cliente envia seu ID do cliente e todas as ações executadas são enviadas ao servidor criptografado com a chave pública do servidor.

  • O servidor descriptografa as ações e, se estiverem no formato correto, confia que foram originadas no cliente.

Nota:

  • Você não pode armazenar a senha do cliente em nenhum lugar; caso contrário, um invasor poderá buscar a chave e assinar as ações como suas. A segurança desse esquema depende exclusivamente da integridade da chave que o servidor gera para o cliente. O cliente ainda precisará ser autenticado com o servidor ao solicitar essa chave.

  • Na verdade, você ainda conta com o servidor por segurança e não com o cliente. Toda ação que o cliente executa, você deve validar no servidor.

  • É possível executar scripts externos em trabalhadores da web . Lembre-se de que toda solicitação JSONP que você possui agora é um problema de segurança muito maior. Você precisa proteger a chave a todo custo. Depois de perdê-lo, um invasor pode se passar por um usuário.

  • Isso atende à sua demanda de que 'nenhum ping no servidor' seja realizado. Um invasor não pode simplesmente imitar uma solicitação HTTP com dados falsificados se não souber a chave.

  • A resposta de Joachim ainda está correta . Na verdade, você ainda está executando toda a autenticação no servidor . A única coisa que você salvou aqui é a necessidade de validação de senha com o servidor sempre. Agora você só precisa envolver o servidor quando desejar confirmar ou extrair dados atualizados. Tudo o que fizemos aqui foi salvar uma chave confiável no lado do cliente e fazer com que o cliente a valide novamente.

  • Esse é um esquema bastante comum para aplicativos de página única (por exemplo, com AngularJS).

  • Chamo a chave pública do servidor de "pública" por causa do que isso significa em esquemas como o RSA , mas na verdade são informações confidenciais no esquema e devem ser protegidas.

  • Eu não guardaria a senha em nenhum lugar da memória. Eu faria o usuário enviar sua senha 'offline' toda vez que ele / ela começar a executar o código offline.

  • Não role sua própria criptografia - use uma biblioteca conhecida como a de Stanford para autenticação.

  • Tome este conselho como está . Antes de lançar esse tipo de autenticação em um aplicativo crítico para os negócios do mundo real, consulte um especialista em segurança . Esse é um problema sério, doloroso e fácil de errar.

É fundamental que nenhum outro script tenha acesso à página. Isso significa que você só permite scripts externos com trabalhadores da web. Você não pode confiar em nenhum outro script externo que possa interceptar sua senha quando o usuário a inserir.

Use um campo de senha inline prompte não se não tiver certeza absoluta e não adie sua execução (ou seja, ele não deve chegar a um ponto em que os eventos tenham acesso a ele, mas apenas no código de sincronização). E, na verdade, não armazene a senha em uma variável - novamente, isso funcionará apenas se você confiar que o computador do usuário não está comprometido (embora isso também seja válido para validar em um servidor).

Gostaria de acrescentar novamente que ainda não confiamos no cliente . Você não pode confiar apenas no cliente, e acho que a resposta de Joachim é a melhor. Apenas ganhamos a conveniência de não precisar executar ping no servidor antes de começar a trabalhar.

Material relacionado:


3
"Não role sua própria criptografia" se aplica tanto aos protocolos quanto às primitivas, se não mais, porque, embora as pessoas geralmente não sejam tolas o suficiente para criar suas próprias primitivas, elas pensam que podem criar um protocolo adequado . Também não vejo por que o RSA é necessário aqui. Como você usa HTTPS, por que não gerar um segredo compartilhado de 16 a 32 bytes e exigir que ele seja enviado com solicitações futuras? Claro, se você fizer isso, certifique-se de uso corrente igualdade invariante no tempo na corda ...
Reid

2
+1 @Reid Sim, eu acabei de ter uma discussão sobre isso com um especialista recentemente. Baseei aproximadamente o esquema primitivo aqui em um protocolo que aprendi (por Rabin IIRC), mas pelo menos até encontrar os detalhes (e justificar, por exemplo, por que a criptografia assimétrica é necessária neste caso). Não use o esquema sugerido nesta resposta sem consultar um especialista em segurança primeiro . Eu já vi essa abordagem usada em vários lugares, mas na prática isso significa muito pouco, se é que alguma coisa. Também editei a resposta para melhorar isso.
Benjamin Gruenbaum

Usar um prompt adiciona pouca segurança. Um invasor pode substituir o window.promptmétodo para interceptar a frase secreta ou iniciar um próprio prompt (possivelmente dentro de um iframe!). Um campo de senha tem um benefício adicional: os caracteres não são mostrados.
Rob W

@ RobW Obviamente, todo esse esquema não vale nada se a página foi comprometida. Se o invasor tiver acesso para executar o JavaScript na página, estamos ferrados. A idéia por trás do prompt era que ele está bloqueando, mas você está certo, qualquer benefício de segurança que ele oferece é duvidoso. Veja o último link na lista.
Benjamin Gruenbaum

11
Em primeiro lugar. Resposta incrível, acho que não posso pedir mais nada. Por outro lado, eu queria fazer projetos de estimação para mim (e, claro, para quem quer usá-lo) e talvez isso seja um exagero (ou talvez não). Quero dizer, não quero fazer nada sério e tenho medo de não poder lançar esse tipo de autenticação que você explicou, falta de conhecimento. Acho que faria um simples cheque 401 ou, se não tiver nenhuma solicitação, basta executar o ping no servidor toda vez que acessar esse tipo de rota. O token de autenticação também é uma opção (e talvez a coisa mais próxima relacionada ao que você explicou aqui no AFAIK). Obrigado :)
Jesus Rodriguez

89

É simples: qualquer mecanismo de segurança que depende do cliente para fazer apenas o que você pede pode ser comprometido quando um invasor tem controle sobre o cliente.

Você pode ter verificações de segurança no cliente, mas apenas para atuar efetivamente como um "cache" (para evitar fazer uma ida e volta cara ao servidor se o cliente já souber que a resposta será "não").

Se você deseja manter as informações de um conjunto de usuários, verifique se o cliente desses usuários nunca chega a essas informações. Se você enviar esses "dados secretos" junto com as instruções "mas não os exibir", será trivial desativar o código que verifica essa solicitação.

Como você vê, esta resposta realmente não menciona nenhuma especificação de JavaScript / navegador. Isso ocorre porque esse conceito é o mesmo, independentemente do seu cliente. Realmente não importa que seja um cliente gordo (aplicativo cliente / servidor tradicional), um aplicativo Web da velha escola ou um aplicativo de página única com amplo JavaScript do lado do cliente.

Depois que seus dados saem do servidor, você deve assumir que um invasor tem acesso total a eles.


Obrigado. Seria ótimo se você pudesse explicar um pouco mais, meu conhecimento de segurança não é tão bom e a única parte que entendo é que estou errado: P. Quando você fala sobre cache, é apenas para cache quando o servidor diz que não? Quero dizer, se o servidor disse sim uma vez, você não pode armazenar isso em cache, certo?
Jesus-rodriguez1

3
Eu apenas gostaria de acrescentar que é possível variáveis fechamento acesso no navegador (como no padrão de módulo de seu mencionado), especialmente com um depurador :)
Benjamin Gruenbaum

2
@BenjaminGruenbaum: fique à vontade para expandir isso para uma resposta que se concentre no lado JS das coisas. Eu dei apenas a visão geral de alto nível e independente de tecnologia aqui. Uma resposta focada no próprio JS também seria legal!
Joachim Sauer

11
Então, resumindo, se alguma informação / rota / qualquer coisa estiver acessível, dependendo de uma variável javascript, isso não será seguro em nenhum caso, porque você pode alterar facilmente essa variável para dizer o que você precisa para acessar essas informações privadas.
Jesus Rodriguez

4
@JoachimSauer Eu acho que isso perderia o ponto desta questão que você acertou em cheio. Independentemente das medidas de segurança adotadas pelo cliente, é possível comprometer a comunicação . Você pode criar sistemas de autenticação muito sólidos em JavaScript, mas tudo não vale nada no minuto em que você insere a comunicação com um servidor que outros clientes tratam como uma fonte de verdade também. O código fonte é todo enviado para o lado do cliente, um invasor inteligente pode apenas lê-lo e imitar uma solicitação HTTP para o servidor. É preciso tratar qualquer coisa originada no cliente como não confiável, a menos que seja validada no servidor.
Benjamin Gruenbaum

10

Existe um ditado na comunidade de jogos: " O cliente está nas mãos do inimigo". Qualquer código que esteja sendo executado fora de uma área protegida como o servidor é vulnerável. No cenário mais básico, vulnerável a não ser executado em primeiro lugar. É decisão do cliente executar o seu" código de segurança "e o usuário pode Enquanto o código nativo você tem pelo menos uma ofuscação automática na montagem e uma camada adicional de proteção pelo fato de um invasor precisar ser um bom programador para manipular isso, o JS normalmente não é exibido e é um texto simples. Tudo o que você precisa para organizar um ataque são ferramentas primitivas, como um servidor proxy e um editor de texto.O invasor ainda precisará de um certo nível de instrução sobre programação, mas é muito mais fácil modificar um script usando qualquer editor de texto do que injetar código em um executável.


Assembléia não é uma ofuscação, e que acredita que de outra forma nunca jogou um jogo de guerra
miniBill

@miniBill: Embora um invasor com experiência moderada não tenha problemas com o código montado, ele forneceria um filtro passa-alto muito básico. (Infelizmente, este é acadêmica, como capina os script kiddies proporciona aumento mínimo na segurança para o cenário do OP)
Piskvor

1

Não se trata de invadir o JavaScript. Se eu quisesse atacar seu aplicativo, usaria um proxy privado que me permitisse capturar, modificar e reproduzir o tráfego. Seu esquema de segurança proposto não parece ter nenhuma proteção contra isso.


0

Falando especificamente sobre o Angular:

Proteger uma rota do lado do cliente simplesmente não existe. Mesmo se você 'ocultar' um botão para essa rota, o usuário sempre poderá digitá-lo, a Angular reclamará, mas o cliente poderá codificar isso.

Em algum momento, seu controlador precisará solicitar ao servidor os dados necessários para renderizar a exibição. Se o usuário não estiver autorizado a acessar esses dados, eles não os receberão, pois você os protegeu no lado do servidor , e seu aplicativo Angular deve lidar com o 401 adequadamente.

Se você está tentando proteger os dados brutos, não pode usar o lado do cliente. Se você deseja que um usuário em particular possa apenas visualizar dados comuns de uma certa maneira, crie as "visualizações" de dados no servidor em vez de enviar dados brutos para o cliente e reorganizá-los (você já deve fazer isso por razões de desempenho).

Nota lateral: nunca deve haver nada sensível incorporado nos modelos de exibição solicitados pelo Angular, apenas não faça isso. Se, por algum motivo maluco, houver, você precisará proteger esses modelos de exibição no lado do servidor, como faria se estivesse fazendo a renderização no lado do servidor.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.