“Anular” uma variável em rubi [duplicado]
Tenho um caso de uso em que preciso descartar alguns dados no exato momento em que não preciso mais deles , por motivos de segurança.
Estou escrevendo um servidor em Ruby que lida com logins e senhas. Eu uso o BCrypt para armazenar senhas em meu banco de dados. Meu servidor recebe a senha, faz um hash bcrypt dela e não usa mais a senha original.
Eu conheço um tipo de ataque cibernético que envolve o roubo de dados direto da RAM e estou preocupado que um invasor possa roubar a senha de um usuário na forma de string bruta no período em que a senha ainda está na memória. Não tenho certeza se simplesmente usar password_in_string_form = nil
seria suficiente.
Quero anular a variável que contém a senha do usuário no momento em que terminar com ela. Por anular, quero dizer algo semelhante a usar / dev / null para preencher algo com zeros. O objetivo final é a destruição irreversível de dados .
Respostas
Não tenho certeza se simplesmente usar
password_in_string_form = nil
seria suficiente.
Não, não seria o suficiente. O objeto pode ou não ser coletado como lixo imediatamente e, mesmo que fosse, isso não faz com que o conteúdo seja apagado da memória.
No entanto, a menos que tenham sido congeladas, as strings Ruby são mutáveis . Assim, contanto que você não congele a string da senha, você pode substituir seu conteúdo por zeros, ou caracteres aleatórios, ou o que quer que seja, antes de soltá-la. Em particular, isso deve funcionar, sujeito a algumas ressalvas, abordadas posteriormente:
(0 ... password_in_string_form.length).each do |i|
password_in_string_form[i] = ' '
end
Mas é preciso ter cuidado, pois essa abordagem, que pode parecer mais idiomática, não funciona:
# SURPRISE! This does not reliably remove the password from memory!
password_in_string_form.replace(' ' * password_in_string_form.length)
Em vez de atualizar o conteúdo da string de destino no local, replace()
libera o conteúdo para o alocador interno do Ruby (que não o modifica) e escolhe uma estratégia para o novo conteúdo com base nos detalhes da substituição.
A diferença de efeito entre essas duas abordagens deve ser um grande sinal de alerta para você, no entanto. Ruby é uma linguagem de alto nível. Isso dá a você uma grande vantagem, mas ao custo do controle sobre os detalhes, como se e por quanto tempo os dados são retidos na memória.
E isso me leva às ressalvas. Aqui estão os principais:
Ao lidar com a string da senha, você deve tomar cuidado para evitar fazer cópias dela ou de qualquer parte dela, ou então capturar todas as cópias e jogá-las no lixo também. Isso exigirá alguma disciplina e atenção aos detalhes, porque é muito fácil fazer essas cópias.
Eliminar a string de senha em si pode não ser suficiente para atingir seu objetivo. Você também precisa descartar quaisquer outras cópias de senha na memória, como do upstream de isolar a string de senha. Se o seu for um aplicativo da web, por exemplo, isso incluiria o conteúdo da solicitação HTTP em que a senha foi entregue ao seu aplicativo e, provavelmente, mais strings derivadas dele do que apenas a string de senha isolada. Semelhante se aplica a outros tipos de aplicativos.
as senhas podem não ser a única coisa que você precisa proteger. Se um adversário estiver em uma posição em que possa roubar senhas da memória da máquina host, ele também poderá roubar os dados confidenciais que os usuários acessam após o login.
Por essas e outras razões, se os requisitos de segurança para seu servidor ditarem que as cópias na memória das senhas do usuário sejam destruídas assim que não forem mais necessárias, então o Ruby (puro) pode não ser uma linguagem de implementação apropriada.
Por outro lado, se um adversário obtiver acesso suficiente para extrair senhas da memória / troca, provavelmente o jogo já acabou. No mínimo, eles terão acesso a tudo que seu aplicativo pode acessar. Isso não torna as senhas totalmente discutíveis, mas você deve levar isso em consideração ao avaliar quanto esforço deve se dedicar a esse problema.
Isso não é possível em Ruby.
Você terá que escrever algum código específico para cada implementação (Opal, TruffleRuby, JRuby, Rubinius, MRuby, YARV, etc.) para garantir isso. Dependendo da implementação, pode nem mesmo ser possível fazer dentro da memória gerenciada da implementação, sem ter um pedaço de memória separado que você mesmo gerencia.
Ou seja, você provavelmente precisará ter algum pequeno pedaço de código nativo que gerencie seu próprio pequeno pedaço de memória nativa e o injete em seu programa Ruby.