Quais são os riscos / vulnerabilidades de segurança que todo programador C deve estar ciente? [fechadas]

13

Existem muitos riscos de segurança decorrentes do contato próximo com o hardware, em vez de usar APIs bem testadas e comprovadas em linguagens de programação de alto nível. É muito mais fácil causar um estouro de buffer em C do que em uma linguagem como Java.

Quais são os riscos ou vulnerabilidades (por exemplo, estouros de buffer) que todo programador C deve estar ciente (vulnerabilidades do IE relevantes para os programadores C)? A que problemas isso poderia levar? Como evitá-los e quais são os erros comuns que causam esses erros nos programas?

Anto
fonte
O que há nesta lista: owasp.org/index.php/Category:OWASP_Top_Ten_Project O que mais é necessário?
S.Lott
2
@ S.Lott: Parece ser muito sobre questões de segurança no desenvolvimento da web. Parece haver mais recursos sobre isso do que realmente estou pedindo, ao que parece.
Anto
@ Anto: Por favor, atualize a pergunta para distinguir entre todos os recursos de segurança e a segurança da sua pergunta.
S.Lott
@ S.Lott: Não sei o que você quer dizer. Peço para a segurança que é de importância para a maioria dos programadores C, ou seja, coisas como estouros de buffer e outras coisas que são possíveis em C.
Anto
@Anto: "Parece haver mais recursos nessa [segurança da web?] Em geral do que aquilo que estou realmente pedindo" Parece dizer que você está perguntando sobre alguma segurança que não é a segurança da web. Verdade? Nesse caso, atualize a pergunta para explicar o que você está procurando. Falso? Então você está perguntando sobre segurança na Web; nesse caso, por que a lista OWASP não é mencionada na sua pergunta?
S.Lott

Respostas:

13

Os estouros de buffer são grandes. Nada em C é verificado no intervalo por padrão, por isso é muito fácil substituir um buffer. Há uma função de biblioteca padrão gets(), que não pode ser impedida de estourar o buffer e quase nunca deve ser usada.

Existem algumas técnicas no nível de implementação para impedir a exploração, como embaralhar blocos de heap, mas isso não interrompe o estouro de buffer nos buffers locais, o que geralmente pode fazer coisas interessantes, como alterar o endereço para o qual a função retornará.

Não existe uma boa solução geral em C. Muitas funções da biblioteca possuem versões que limitarão a quantidade que serão gravadas. embora calcular isso possa ser desajeitado. Existem softwares que podem detectar estouros de buffer de heap em teste, desde que o teste apropriado seja executado, e o estouro de pilha geralmente aparece como uma falha nos testes. Fora isso, é uma questão de codificação cuidadosa e revisão de código.

Um problema relacionado é o problema de gravar em um buffer muito pequeno por um caractere, esquecendo que uma string C com n caracteres requer n + 1 caracteres na memória, devido ao '\0'terminador. Se o invasor conseguir armazenar uma string sem o terminador, qualquer função C que espera uma string continuará processando até atingir um byte zero, o que pode resultar em copiar ou emitir mais informações do que o desejado (ou atingir a memória protegida para um ataque do DOS) ) A solução, novamente, é a conscientização, o cuidado e as revisões de código.

Há outro risco com a printf()família. Se você escreve char * str; ... printf(str);, está se preparando para problemas se strcontiver um '%' quando impresso. A %ndiretiva de formato permite printf()gravar na memória. A solução é printf("%s", str);ou puts(str);. (Além disso, use o C99 em snprintf()vez de sprintf().)

O uso de números inteiros não assinados, principalmente como índices de loop, pode causar problemas. Se você atribuir um pequeno valor negativo a um não assinado, obterá um grande valor positivo. Isso pode prejudicar coisas como processar apenas N instâncias de algo ou funções limitadas como strncpy(). Examine todos os números inteiros não assinados. Você pode evitar unsigned short, pois um grande valor em um deles será convertido em um grande valor positivo em um int.

Não esqueça que uma constante de caractere, em C, é na verdade uma int. Escrever algo como char c; while((c = getchar()) != EOF) ...pode falhar facilmente, pois EOFnão será representável em um arquivo char.

Há muitos erros característicos de C em que posso pensar, mas esses podem causar problemas de segurança.

David Thornley
fonte
Não há necessidade de usar printf("%s", str)uma corda nua quando puts(str)fará o mesmo trabalho.
Blrfl
@Blrfl, mas putsacrescenta um caractere de nova linha enquanto printfnão.
rightfold 28/10
Também poderia fazer fputs(str, stdout), o que não.
Blrfl 28/10
Quanto ao excesso de números inteiros: Usar entradas assinadas não é solução, pois o excesso delas causará UB. A única solução (dolorosa) é provar formalmente que você nunca transbordará ou verificar no tempo de execução (mas verifique corretamente, o que também é complicado sem transbordar na verificação).
sleske
@DavidThornley: A função gets () removida padrão C11 e C ++ 14 da biblioteca padrão devido à sua periculosidade.
Destructor
5

Alguns dos riscos específicos de C incluem: estouros de buffer , ataques de string de formatação e estouros de números inteiros .

Nemanja Trifunovic
fonte
1
Não há nada específico de C sobre estouros de buffer - qualquer idioma com ponteiros pode ter isso. Estouros de número inteiro se aplicam a praticamente qualquer idioma e também podem ocorrer facilmente no código gerenciado.
27411 Steve
1
@ Steve, não são realmente indicadores que causam esse problema, mas como a linguagem não impõe limites à matriz.
Doug T.
2
@ Steve a pergunta não estava perguntando sobre coisas que apenas dizem respeito a C, mas algo que os programadores de C devem estar cientes.
precisa
1
@Steve: C é incomumente suscetível a estouros de buffer, em parte devido à falta de suporte à verificação de intervalo e ao número de funções da biblioteca que felizmente estourarão buffers para você.
David Thornley
Entendo que a pergunta está perguntando sobre C em particular, mas acho que vale a pena esclarecer caso a resposta seja lida fora do contexto de que esses riscos são mais gerais. Em particular, os desenvolvedores de código gerenciado são (IMHO) muito complacentes com relação à segurança, e os estouros de número inteiro afetam principalmente a maioria dos idiomas.
27411 Steve
4

Aqui está um risco fácil de perder que pode causar problemas que levarão horas para serem corrigidos.

Considere o código a seguir, que será compilado sem problemas.

if(lpstr_current_state = CONST_EMERGENCY_STATE_HOLY_CRAP)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}

Quando você verifica se lpstr_current_stateestá dentro, CONST_EMERGENCY_STATE_HOLY_CRAPestá realmente atribuindo. É melhor sempre colocar a variável constante à esquerda. Quando você coloca a constante à esquerda, o compilador falha porque você não pode atribuir um valor a uma variável.

if(CONST_EMERGENCY_STATE_HOLY_CRAP = lpstr_current_state)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}

Então você pode facilmente dizer para si mesmo: "Caramba, isso poderia ter sido ruim", enquanto corrigia o código para ler ...

if(CONST_EMERGENCY_STATE_HOLY_CRAP == lpstr_current_state)
{
    do_warn_joint_chiefs_of_staff_of_nuclear_attack();
}
Kristofer Hoch
fonte
7
É fácil para um compilador capturar e sinalizar como aviso, ao contrário de outros problemas. Infelizmente, nem todos os compiladores facilitam a execução.
precisa
2
Isso pode acontecer em idiomas diferentes de C, qualquer idioma que use =e ==.
FrustratedWithFormsDesigner
3
Isso também não é realmente uma vulnerabilidade de segurança, é um bug.
31811 Chris Pitman
1
Estes são chamados de condições Yoda.
2
@ Kristofer Hoch: Se vamos considerar um risco provável de um bug C, e considerá-lo aqui, precisaremos de um fórum muito maior.
David Thornley
0

Há apenas um risco de segurança: o fato de haver pessoas de fora que farão o possível para capturar qualquer vulnerabilidade no seu software e explorá-la para seu próprio benefício. Tudo o resto segue a partir daí.

Então, quando você pensa que "ninguém em sã consciência faria ...", precisará pensar imediatamente "exceto que alguém que queira invadir os computadores de outras pessoas faria exatamente isso".

A maior consequência é que sempre que você reage a eventos externos (por exemplo, processando dados fornecidos de fora), você deve assumir que esses dados estavam sob controle de seu pior inimigo.

gnasher729
fonte
Embora eu concorde com os parágrafos dois e três, colocar toda a culpa no atacante é um pouco grossa nos meus olhos. Sempre são necessários dois para um ataque bem-sucedido: um programador que estraga tudo e um atacante que pega o programador em flagrante. No entanto, a vulnerabilidade de segurança existe antes que o invasor possa explorá-la. E por isso, o programador deve ser responsabilizado.
Cmaster - reinstate monica