O que é uma vulnerabilidade de condição de corrida?

O que é uma vulnerabilidade de condição de corrida?
Leitores como você ajudam a apoiar a MUO. Quando você faz uma compra usando links em nosso site, podemos ganhar uma comissão de afiliado.

Uma condição de corrida ocorre quando duas operações devem ocorrer em uma ordem específica, mas podem ser executadas na ordem oposta.





Por exemplo, em um aplicativo multithread, dois threads separados podem acessar uma variável comum. Como resultado, se um thread alterar o valor da variável, o outro ainda poderá usar a versão mais antiga, ignorando o valor mais recente. Isso causará resultados indesejáveis.





não posso deletar o arquivo em uso
MAKEUSEO VÍDEO DO DIA

Para entender melhor esse modelo, seria bom examinar de perto o processo de comutação de processo do processador.





Como um processador alterna processos

Sistemas operacionais modernos pode executar mais de um processo simultaneamente, chamado multitarefa. Quando você olha para este processo em termos de Ciclo de execução da CPU , você pode descobrir que a multitarefa realmente não existe.

Em vez disso, os processadores estão constantemente alternando entre os processos para executá-los simultaneamente ou pelo menos agir como se estivessem fazendo isso. A CPU pode interromper um processo antes que ele seja concluído e retomar um processo diferente. O sistema operacional controla o gerenciamento desses processos.



Por exemplo, o algoritmo Round Robin, um dos algoritmos de comutação mais simples, funciona da seguinte forma:

  Um diagrama mostrando 3 processos adormecidos em fila e 1 processo ativo que a CPU está executando no momento

Geralmente, esse algoritmo permite que cada processo seja executado por períodos de tempo muito pequenos, conforme determinado pelo sistema operacional. Por exemplo, isso pode ser um período de dois microssegundos.





A CPU leva cada processo por vez e executa comandos que serão executados por dois microssegundos. Em seguida, ele continua para o próximo processo, independentemente de o atual ter terminado ou não. Assim, do ponto de vista de um usuário final, mais de um processo parece estar rodando simultaneamente. No entanto, quando você olha os bastidores, a CPU ainda está fazendo as coisas em ordem.

A propósito, como mostra o diagrama acima, o algoritmo Round Robin não possui nenhuma noção de otimização ou prioridade de processamento. Como resultado, é um método bastante rudimentar que raramente é usado em sistemas reais.





Agora, para entender melhor tudo isso, imagine que duas threads estejam rodando. Se os encadeamentos acessarem uma variável comum, poderá surgir uma condição de corrida.

Um exemplo de aplicativo da Web e condição de corrida

Confira o aplicativo Flask simples abaixo para refletir sobre um exemplo concreto de tudo o que você leu até agora. O objetivo deste aplicativo é gerenciar transações de dinheiro que ocorrerão na web. Salve o seguinte em um arquivo chamado dinheiro.py :

E9332374EFBF51ABFD386CECF7114F9C49CAB05

Para executar esse código, você precisará criar um registro na tabela de contas e continuar as transações sobre esse registro. Como você pode ver no código, este é um ambiente de teste, portanto, ele faz transações em relação ao primeiro registro da tabela.

from money import db 
db.create_all()
from money import Account
account = Account(5000)
db.session.add(account)
db.session.commit()

Você acabou de criar uma conta com um saldo de $ 5.000. Por fim, execute o código-fonte acima usando o seguinte comando, desde que você tenha os pacotes Flask e Flask-SQLAlchemy instalados:

python money.py 

Então você tem o aplicativo web Flask que faz um processo de extração simples. Este aplicativo pode realizar as seguintes operações com links de solicitação GET. Como o Flask é executado na porta 5000 por padrão, o endereço em que você o acessa é 127.0.0.1:5000/ . O aplicativo fornece os seguintes endpoints:

  • 127.0.0.1:5000/ exibe o saldo atual.
  • 127.0.0.1:5000/enviar/{quantidade} subtrai o valor da conta.
  • 127.0.0.1:5000/reinicializar redefine a conta para $ 5.000.

Agora, neste estágio, você pode examinar como ocorre a vulnerabilidade de condição de corrida.

Probabilidade de uma vulnerabilidade de condição de corrida

O aplicativo da web acima contém uma possível vulnerabilidade de condição de corrida.

Imagine que você tenha $ 5.000 para começar e crie duas solicitações HTTP diferentes que enviarão $ 1. Para isso, você pode enviar duas solicitações HTTP diferentes para o link 127.0.0.1:5000/enviar/1 . Suponha que, assim que o servidor web processa a primeira solicitação, a CPU interrompe esse processo e processa a segunda solicitação. Por exemplo, o primeiro processo pode parar depois de executar a seguinte linha de código:

account.amount = int(account.amount) - amount 

Este código calculou um novo total, mas ainda não salvou o registro no banco de dados. Quando a segunda solicitação começar, ela fará o mesmo cálculo, subtraindo $ 1 do valor no banco de dados — $ 5.000 — e armazenando o resultado. Quando o primeiro processo for retomado, ele armazenará seu próprio valor - $ 4.999 - que não refletirá o saldo da conta mais recente.

Portanto, duas solicitações foram concluídas e cada uma deve ter subtraído US$ 1 do saldo da conta, resultando em um novo saldo de US$ 4.998. Mas, dependendo da ordem em que o servidor web os processa, o saldo final da conta pode ser de US$ 4.999.

Imagine que você envie 128 solicitações para fazer uma transferência de US$ 1 para o sistema de destino em um período de cinco segundos. Como resultado dessa transação, o extrato de conta esperado será de $ 5.000 - $ 128 = $ 4.875. No entanto, devido à condição da corrida, o saldo final pode variar entre US$ 4.875 e US$ 4.999.

Programadores são um dos componentes mais importantes da segurança

Em um projeto de software, como programador, você tem algumas responsabilidades. O exemplo acima foi para um aplicativo simples de transferência de dinheiro. Imagine trabalhar em um projeto de software que gerencia uma conta bancária ou o back-end de um grande site de comércio eletrônico.

Você deve estar familiarizado com essas vulnerabilidades para que o programa que você escreveu para protegê-las esteja livre de vulnerabilidades. Isso requer uma forte responsabilidade.

Uma vulnerabilidade de condição de corrida é apenas uma delas. Não importa qual tecnologia você usa, você precisa estar atento a vulnerabilidades no código que você escreve. Uma das habilidades mais importantes que você pode adquirir como programador é a familiaridade com a segurança de software.