Os 10 controles proativos de segurança são uma série de diretrizes recomendadas aos desenvolvedores e arquitetos de sistema a fim de garantir um grau de segurança nos seus projetos.
Eles são definidos em ordem crescente de prioridade, sendo o número 1 o mais importante de todos. Neste artigo iremos cobrir os 10 controles proativos de segurança mais importantes que todo desenvolvedor deveria saber e que podem ser encontrados no site da OWASP.
@soareswallace
C1: Definindo requisitos de segurança
"Um requisito de segurança é uma determinação que garanta que propriedades de segurança de software estão sendo satisfeitas."
Como assim? Basicamente requisitos de segurança funcionam como definições que o software deve atender. Por exemplo: "Não deve ser possível acessar o sistema utilizando padrões de login e senha."
Ou de maneira mais formal:
Existem incrementos aos requisitos de segurança que os levam a uma melhor compreensão pelos arquitetos e desenvolvedores. Um exemplo dessas melhorias seriam os casos de mau-uso do sistema. Os casos de mal-uso são basicamente estórias de usuário em que o usuário é um atacante tentando explorar uma vulnerabilidade. Por exemplo:
Como pode ser visto, este caso de mau-uso tem as mesmas características de sistema que a definição de requisito anterior, porém é mais fácil de ser entendido e testado.
A correta aplicação deste controle envolve descobrir, selecionar, documentar, implementar e confirmar a correta implementação de cada funcionalidade de segurança dentro da aplicação.
2.19 Verifique que não há usuários e senha padrão em uso pela a aplicação ou por qualquer um de seus componentes.(ex.: Admin/admin)
Existem incrementos aos requisitos de segurança que os levam a uma melhor compreensão pelos arquitetos e desenvolvedores. Um exemplo dessas melhorias seriam os casos de mau-uso do sistema. Os casos de mal-uso são basicamente estórias de usuário em que o usuário é um atacante tentando explorar uma vulnerabilidade. Por exemplo:
Como um atacante, eu posso introduzir um login e senha padrão para assim ganhar acesso ao sistema.
Como pode ser visto, este caso de mau-uso tem as mesmas características de sistema que a definição de requisito anterior, porém é mais fácil de ser entendido e testado.
A correta aplicação deste controle envolve descobrir, selecionar, documentar, implementar e confirmar a correta implementação de cada funcionalidade de segurança dentro da aplicação.
Descobrindo e selecionando requisitos
Esta é uma das tarefas mais importantes. Nesta fase de design, o arquiteto pode prevenir uma série de vulnerabilidades que poderiam vir a acontecer. Ela consiste em buscar através de padrões de projeto - ASVS, por exemplo - e escolher aquele que seria prioridade para cada versão do seu software. O foco nesta fase é balancear um número de requisitos de segurança que seriam atingíveis pelo time em uma dada versão e ir incrementando sua cobertura.Investigar e documentar
Nesta fase, dado um conjunto de requisitos de segurança, o desenvolvedor pesquisa e documenta na aplicação existente quais dos requisitos estão sendo atendidos. Esta fase é essencial para propor melhorias ao sistema.Implementar
Após a documentação feita agora é elaborado uma série de mudanças que serão realizadas no sistema a fim de adequá-lo aos requisitos de segurança definidos nas etapas anteriores. Cada aplicação deve aqui sofrer as modificações necessárias para que atenda ao que foi previamente definido como requisito. Após as modificações feitas - ou até durante as mesmas - testes devem ser criados a fim de garantir que o requisito esteja sendo atendido.C2: Explorando frameworks e bibliotecas de Segurança
Este controle foca no uso de bibliotecas e frameworks que garantam uma melhor segurança para a aplicação. Implementar seus próprios é altamente não recomendado.
É importante ter em mente que existe uma grande variedade de bibliotecas que já protegem sua aplicação de vulnerabilidades e está tudo bem usá-las. Entretanto é necessário ter alguns cuidados:
-
Crie e mantenha um inventário que catalogue todas as bibliotecas de terceiros que são utilizadas pelo seu projeto.
-
Proativamente as mantenha atualizadas. Existem ferramentas automatizadas que garantem esta checagem, como o dependabot e o OWASP dependency check.
-
Reduza a superfície de ataque encapsulando o uso das bibliotecas e expondo apenas o comportamento requisitado pelo seu software.
Use bibliotecas e frameworks a partir de fontes confiáveis e que sejam mantidas com frequência.
C3: Protegendo o acesso à base de dados.
Esta seção envolve proteger a base de dados partindo de diversos pontos de ataque:
-
Queries
-
Configuração
-
Autenticação
-
Comunicação
Queries seguras
Até hoje, com todos os avanços que tivemos nos frameworks modernos e mais utilizados pela indústria, existe sempre uma vulnerabilidade recorrente a cada ano nos OWASP Top 10: SQL Injection.
SQL Injection é basicamente quando uma entrada não confiável e que não é tratada é executada na forma de uma query de banco. Esta vulnerabilidade é uma das mais perigosas para uma aplicação porque ela pode não somente apagar com todo o banco, como também expor informações sigilosas. Um atacante pode inclusive escalar essa vulnerabilidade e explorar a rede a qual o servidor pertence.
Para prevenir este tipo de vulnerabilidade toda entrada deve ser tratada. Na verdade, em geral, toda entrada dada a um sistema deve ser tratada como insegura. Com isso em mente um desenvolvedor conseguirá prevenir uma outra série de vulnerabilidades, como por exemplo Cross-site Scripting.
No caso de SQL Injection, ou SQLi, a melhor forma de fazer isso é através de parametrização de queries. Ou utilizar frameworks que já fazem isso por padrão, porém observando os pontos já discutidos em C2.
No caso da utilização de parametrização, é importante lembrar que existem partes de queries que não são parametrizáveis. E estas localizações dependem de qual banco está sendo utilizado.
No caso da utilização de parametrização, é importante lembrar que existem partes de queries que não são parametrizáveis. E estas localizações dependem de qual banco está sendo utilizado.
Configuração segura
Infelizmente muitos bancos de dados não possuem uma configuração segura por padrão e, mais infelizmente ainda, alguns desenvolvedores não se preocupam em garantir que estas configurações sejam modificadas antes de coloca-lo em produção. É necessário que ao se utilizar um banco de dados o desenvolvedor tenha a preocupação de realizar as modificações necessárias para garantir um ambiente seguro para sua base de dados.
Autenticação e comunicação segura
Seguindo na mesma linha sobre a configuração, quando se é feita a autenticação e a comunicação com a base de dados deve-se garantir que:
- É feita através de um canal seguro e criptografado.
- Deve-se gerenciar bem as credencias em mantê-las em repositórios seguros criptografados e não às enviar junto ao código para um VCS(GitHub, GitLab).
- Não é recomendado utilizar a configuração padrão das bases visto que essa é a primeira forma de ataque que adversários podem tomar como foco.
C4: Codifique e escape os seus dados
Codificar e escapar dados são técnicas que tem o objetivo de prevenir ataques de injeção(como o SQLi e XSS). Codificar é basicamente modificar o dado para uma nova representação que previna a realização de ataques de injeção. Um exemplo é modificar
<
para <
em HTML.
Escapar dados envolve adicionar um caracter especial antes de outro para evitar a ma interpretação. Um exemplo é a adição de uma contra barra(\
) antes de aspas("
) para informar que este caracter deve ser interpretado como texto e não como fim de string.
Apesar de efetivo é praticamente impossível um desenvolvedor prevenir através de escape e codificação todos os tipos de ataques de injeção que existirem. A melhor opção é delegar, no geral, que os frameworks e bibliotecas lidem com esse tipo de validação e mantê-los sempre atualizados. Somente em caso de necessidade muito específica deve-se criar a própria regra de validação e que deve ser, futuramente, modificada para a utilização de biblioteca específica.
Além dos ataques mais comuns através de aplicações Web, como o SQLi e o XSS, existem outros tipos de ataque que envolvem LDAP, comandos de sistema operacional(OS Injection) e XML Injections. Todos esses podem ser evitados com codificação e escape, porém seguem a mesma regra de que é praticamente impossível cobrir todos os cenários de ataque.
C5: Valide todas as entradas
Como toda orientação sobre proteção contra ataques costuma dizer: NUNCA CONFIE NA ENTRADA DO USUÁRIO. Toda e qualquer entrada deve ser tratada como não confiável. A validação deve ser feito tanto na forma sintática, como na semântica.
A validação deve ser no sentido de que o dado que foi enviado é exatamente do tipo esperado(ex.: inteiro de 4 digitos) e esse dado ao ser passado entre as camadas deve ser válidado e parametrizado a cada passagem.
Lista de permissões
Uma prática comum de defesa contra estes tipos de ataques é da definição de entradas que seriam permitidas(allowlist) ou de forma análoga entradas que NÃO seriam permitidas(blocklist). Entre estas duas, a medidas a mais recomendada para se iniciar uma proteção de sua aplicação é de se criar uma allowlist visto que o conjunto de entradas que se aceita é bem menor do que o conjunto que se proíbi. De forma simples, é mais difícil cobrir todos os casos em que um ataque é efetuado do que definir quais são as entradas válidas. Mas é importante também o desenvolvedor ter em mente que esta deve ser uma das primeiras barreiras de proteção e que outras medidas devem ser impostas para se ter uma maior segurança na aplicação.Sempre faça a validação no servidor
Apesar de ajudar nos requisitos funcionais do negócio e permitir alguns controles de qual informação esta sendo passada ao servidor, a validação no lado do cliente não deve ser encorajada como unica forma de prevenção deste tipo de ataque. Se um desenvolvedor pretende proteger sua aplicação de adversários, é altamente recomendado que validações sejam feitas no servidor já que controles no lado do cliente podem ser facilmente ultrapassados.
A prática ideal é, na verdade, aplicar a validação entre as todas as camadas, tanto na entrada do dado ao servidor quanto no retorno.
Frontend -> Controlador -> Serviço -> Banco -> Serviço -> Controlador -> Frontend
Validação de entrada deve ser o primeiro passo
Como qualquer medida de prevenção de ataques, a validação de entrada não é uma bala de prata. Ela, sozinha, não consegue prevenir ataques, apesar de (tentar) manter o que esta sendo passado ao servidor como uma entrada válida. Porém uma entrada válida ainda pode ser um ataque.
Esta medida deve, idealmente, ser combinada com outras para aumentar a proteção do sistema.
Serialize aos poucos
Com o crescente uso de JSON para comunicação entre APIs e o frontend muitas vezes a comunicação entre os dois se baseia em objetos enormes. E quanto maior forem esses objetos mais difícil torna-se a validação do dado.
O desenvolvedor deve ter em mente que um conjunto menor de dados e um objeto mais conciso pode facilitar na hora de validar a informação passada.
BASICAMENTE: NUNCA CONFIE NO ENTRADA DO USUÁRIO
C6: Implemente sua identidade digital
A identidade digital é a maneira de se realizar o controle dos usuários que acessam a aplicação. Autenticação é, por exemplo, o processo de validar a identidade daquele que esta acessando o sistema. Controle de sessão a forma com que o servidor mantêm o estado de autenticação dos usuários. Veremos aqui algumas recomendações para uma implementação segura de controle de identidades.
Apesar de ser um dos maiores tópicos deste documento, apenas vimos de forma superficial. Há muito a ser explorado aqui e que estas técnicas apresentadas devem ser combinadas sempre com mais de uma camada de segurança.
Niveis de autenticação
A publicação especial NIST 800-63B descreve um guia de como implementar controles para uma identidade digital, autenticação e gerenciamento de sessão. Nesta, são apresentados 3 níveis de garantias de autenticação, chamados de Authentication Assurance Level(AAL).
Nível 1
O nível 1 é reservado para aplicações de baixo risco. Reservado para aquelas aplicações que não contém PII(Informação pessoal identificavel.)
O nível 1 requer apenas autenticação de um único fator, tipicamente através do uso de senhas.
L1: Requisitos para Senhas
Toda política de senha que sigam estes requisitos devem, no mínimo, possuir:
- Um mínimo de 8 caracteres, se possível utilizar em conjunto com autenticação de multi-fatores(MFA). Caso MFA não possa ser aplicado, o tamanho minimo deve ser aumentado para 10 caracteres.
- Todo caracter ASCII, assim como o caractere de espaço em branco deve ser aceito como válido.
- Deve-se encorajar o uso de senhas longas.
- Remover estes requisitos pode diminuir bastante a efetividade da segurança. MFA e senhas longas deve ser sempre encorajado.
- Senhas que já foram encontradas em vazamentos de login devem ser rejeitadas. Um desenvolvedor podem restringir a busca dentre as top 1000 senhas mais comuns em vazamentos.
L1: Implementar um mecanismo seguro de recuperação de senha
É mais comum do que se imagina que atacantes ganhem acesso a contas de usuário através do mecanismo de senha. Um exemplo de ataque é através do corpo do POST.
Ao se requisitar um reset de senha o cliente manda um POST ao servidor e no seu corpo contém o email do usuário.
Uma manipulação que pode ser feita neste corpo é verificar se o servidor aceita mais de um email como parâmetro válido. Por exemplo:
Por mais incrível que parece, este exemplo é um caso real de ataque em que o atacante conseguiu acesso a conta da vítima, pois ao fazer essa alteração o atacante recebeu no seu email o link de reset de senha. Um bom material de referência, sobre como garantir um procedimento seguro de reset de senha pode ser encontrado no forgot password cheat sheet.
email_to_recover:"victim@email.com"
Uma manipulação que pode ser feita neste corpo é verificar se o servidor aceita mais de um email como parâmetro válido. Por exemplo:
email_to_recover:["victim@email.com", "attacker@email.com"]
Por mais incrível que parece, este exemplo é um caso real de ataque em que o atacante conseguiu acesso a conta da vítima, pois ao fazer essa alteração o atacante recebeu no seu email o link de reset de senha. Um bom material de referência, sobre como garantir um procedimento seguro de reset de senha pode ser encontrado no forgot password cheat sheet.
L1: Armazenar senhas de forma segura
Apesar das falhas graves dos anos recentes, ainda hoje existem aplicações que armazenam senhas em texto plano. Isso, obviamente, não é uma forma segura de armazaenamento. Já existem frameworks que fazem isso de forma automatica e é altamente recomendado que se aplique.
Nível 2
O nível 2 é reservado para aplicações que contém PII ou qualquer outra informação pessoal disponível de forma online. Neste nível é requerido o uso de OTP ou qualquer outro MFA disponível.
MFA garante um outra camada de segurança e garante também que o usuário que esta tentando acessar o recurso é realmente aquele quem diz ser. MFA pode ser uma combinação de:
- Algo que o usuário conhece: senhas ou PIN
- Algo que o usuário possue: token ou telefone
- Algo que faz parte do usuário: biometria
Nível 3
O nível 3 é requerido quando o impacto de sistemas comprometidos pode gerar qualquer tipo de dano pessoal, perda financeira substancial, dano do interesse público ou envolver violações criminais ou civis. O nível 3 requer autenticação que é baseada em prova de posse através de algum tipo de protocolo de criptografia.
Este controle é usado quando se quer atingir o maior grau de segurança possível. Isto é geralmente atingido através de modulos criptograficos de hardware.
Gerenciamento de sessão
É utilizado para manter o estado de autenticação por um tempo limitado de tempo. Este mecanismo permite que o usuário não tenha que se re-autenticar a cada request.
O controle deve ser feito através do servidor e deve conter um identificador para o usuário. A validade deste identificador deve ser limitada e deve expirar após um período de tempo pré-determinado.
Abaixo há algumas sugestões que devem ser consideradas quando o gerenciamento é implementado:
- Garanta que o session id possui uma quantidade grande de caracteres, que seja único e aleatório.
- A aplicação deve gerar um novo id a cada re-autenticação e expirar ids após um periodo de tempo pré-determinado, ou durante o log-out.
Cookies
É o método mais comum de se armazenar os identificadores de sessão. Os cookies armazenam e gerenciam os identificadores de sessão para aplicações web.
Há algumas boas práticas que devem ser seguidas ao se utilizar este metodo gerenciador de sessão.
- Não devem ser aplicáveis a todos os caminhos disponiveis na aplicação. Seu acesso deve ser limitado a um sub-grupo.
- O parâmetro secure deve estar configurado para garantir uma transmissão segura(TLS).
-
O parâmetro
HttpOnly
deve ser configurado para prevenir que o cookie seja acessado via JavaScript. - A adição do parâmetro samesite deve ser feita para evitar Cross-site Requests e assim evitar ataques que explorem esta funcionalidade.
Tokens
São a forma mais moderna de gerenciar a sessão de usuários após uma autenticação inicial. Gerados no servidor, não necessitam de re-autenticação para manter o usuário logado. Apenas após o tempo de expiração. Assim, lidando com apenas o estado do token, o desempenho sentido pelo cliente também é melhorado visto que não se faz necessário armazenar toda a informação e validá-la para autenticar o usuário.
JWT (JSON Web Tokens)
É um padrão aberto que define uma forma compacta e auto-contida de transmitir a informação entre o cliente e o servidor através de um objeto JSON. Esta informação é assinada digitalmente e pode ser verificada no servidor.
Entretanto, é importante ter em mente que o JWT, geralmente, não é salvo no servidor. Ele é apenas gerado e enviado ao cliente que o reutiliza até o tempo de expiração. Após este tempo o servidor gera outro JWT válido que é novamente entregue ao cliente. A cada novo request o cliente "pergunta" ao servidor se a assinatura enviada com o JWT é válida. Em caso de negativa o servidor deve requisitar ao cliente uma nova autenticação para gerar um novo JWT válido.
Apesar de ser um dos maiores tópicos deste documento, apenas vimos de forma superficial. Há muito a ser explorado aqui e que estas técnicas apresentadas devem ser combinadas sempre com mais de uma camada de segurança.
C7: Garanta Controles de Acesso
Inicialmente é preciso diferenciar autenticação de autorização. Autenticação é o processo verificar a identidade do usuário e autentica-lo ao sistema. Conceder autorização é o processo de, uma vez autenticado, verificar se um dado usuário possui os privilégios de acesso ao recurso e/ou serviço.
Controle de acesso pode ser implementado através das mais diferentes áreas, desde controle de acesso a dados, até cache para propósitos de escalabilidade. Existem diversos tipos de design para controle de acesso que podem ser consideradas:
- Controle de acesso discricionario(DAC): é o controle de acesso implementado para arquivos, entidades, objetos, etc. Este controle é geralmente baseado em privilégios dos usuários e acesso a grupos específicos.
- Controle de acesso mandatório(MAC): é o controle de acesso baseado em níveis de sensibilidade de informação.
- Controle de acesso baseado em privilégios(RBAC): é realizar o controle de acesso baseado nos privilégios atribuídos aos usuários.(ex.: Admin, User, Editor)
- Controle de acesso baseado em atributo(ABAC): é o controle de acesso baseado em atributos dos objetos e arquivos e os atributos dados aos usuários. É feito uma validação cruzada entre os dois para conceder ou não o acesso.
Princípios de design para controle de acesso
É apresentado neste documento um conjunto de práticas para design de controle de acesso que devem ser considerados durante a fase inicial de desenvolvimento.
Pense no controle de acesso desde o início
Uma vez desenvolvido um sistema é extremamente custoso pensar e executar uma forma de controle de acesso partindo do mesmo. Quanto mais cedo se pensar neste controle menos trabalhoso será para manter um padrão de controle de acesso.
Controle de acesso é uma das principais áreas que garante a segurança de uma aplicação e deve ser pensado desde as primeiras fases de design.
Inicialmente design para controle de acesso pode ser simples, mas aumenta junto com a complexidade da aplicação. É necessário (também) permitir uma customização do controle de acesso para cada funcionalidade da aplicação. Entretanto esta customização não deve comprometer a segurança da aplicação.
Force que todos os requests passem por checagem de acesso
É recomendado que exista no caminho de entrada dos requests um controle de acesso centralizado. Um módulo que valide os privilégios dos usuários a cada requisição. Esse requisito garantirá que ao realizar qualquer tipo de ação no sistema um usuário terá verificado todos os seus privilégios, evitando assim ações indesejadas.
Negue por padrão
Se não é especificamente permitido, negue a requisição. Existem várias formas de aplicar este principio em código.
- A aplicação pode lançar uma exceção ou um erro enquanto processa a checagem de privilégios. Nestes casos o acesso deve sempre ser negado.
- Quando um usuário é criado, este mesmo usuário deve possuir o mínimo de privilégios possível até que alguém lhe conceda algum.
- Quando uma nova funcionalidade é configurada, esta deve ser negado o acesso a usuários comuns até que esteja totalmente disponivel.
Princípio do menor privilégio
Garanta que todo e qualquer acesso dado a qualquer usuário seja o mínimo necessário para a execução das suas atividades.
Não codifique de forma explicita privilégios
Muitas aplicações fazem o controle de acesso baseado em cargos e acaba se tornando bastante comum a seguinte linha de código.
Este tipo de codificação tem suas limitações e perigos:
if (user.hasRole("ADMIN")) || (user.hasRole("MANAGER")) {
Este tipo de codificação tem suas limitações e perigos:
- É naturalmente frágil e pode facilmente gerar erros de verificação em casos que o desenvolvedor esquece de adicionar novos cargos.
- A sua manutenabilidade é extremamente complicada em ambientes compartilhados.
- Não permite controle de acesso horizontal ou controle específico baseado em dados.
if (user.hasAccess("DELETE_ACCOUNT")) {
Log todos os eventos que envolvem controle de acesso
Todos os eventos que envolvem a checagem de privilégios devem ser logados na aplicação para auxiliar em futuras investigações de ataques e vulnerabilidades.
C8: Proteja seus dados
Não apenas garantido pelas novas leis de proteção de dados(LGDP, GDPR), o dever de um desenvolvedor de proteger os dados dos seus usuários deve ser intríseco.
Atacantes podem roubar dados de usuários de diversas formas, algumas delas são a comunicação não segura e SQL Injection para copia de senhas e outras credenciais.
1ª Etapa: Classifique seus dados
É essencial para os desenvolvedores de sistemas classificar os dados e determinar o grau de sensibilidade de cada um.
Assim que se categoriza esses dados o desenvolvedor de sistemas pode agora mapear as regras necessárias para proteger cada tipo de dado em cada nível de sensibilidade. Por exemplo:
- Informação de marketing que não é sensível pode ser categorizada como pública e seria ok colocá-la em um site púsblico.
- Número de cartão de crédito pode ser classificado como informação privada e deve ser criptografada.
Criptografe dados sensiveis
Ao trafegar e armazenar dados os desenvolvedores de sistemas devem sempre considerar algum tipo de criptografia. No caso da transmissão de dados, TLS é o protocolo de comunicação criptográfica mais comum. Este protocolo é utilizado por diversas aplicações para a comunicação através da rede.
O principal benefício de se utilizar criptografia na camada de transporte é evitar o vazamento e modificação de informações através de acesso não autorizado quando há a comunicação entre o cliente e o servidor.
É também essencial ter preocupação com o tipo de dado que esta se armazenando. Uma das regras mais importantes é de se evitar ao máximo armazenar informações sensiveis. Se for necessário armazenar informações sensiveis o desenvolvedor deve se preocupar em criptografá-las para evitar a visualização não autorizada.
É também essencial ter preocupação com o tipo de dado que esta se armazenando. Uma das regras mais importantes é de se evitar ao máximo armazenar informações sensiveis. Se for necessário armazenar informações sensiveis o desenvolvedor deve se preocupar em criptografá-las para evitar a visualização não autorizada.
Armazenamento seguro em aplicações móveis
Aplicações móveis tem em particular uma preocupação a mais. Como estes tipos de dispositivos podem ser roubados ou perdidos com maior facilidade é recomendado manter a menor quantidade de dados disponiveis no dispositivo.
Ciclo de vida das chaves
Chaves (secret keys) são utilizadas em aplicações para diversas funções. Podem ser utilizadas, por exemplo, para assinar digitalmente JWTs, proteger cartões de crédito e gerenciar acesso a funcionalidades de segurança.
- Garanta que qualquer chave de acesso é protegida de acesso não permitido.
- Armazene chaves em aplicações dedicadas a guardar essas chaves.(ex.: Vault da Hashcorp)
- Utilize chaves independentes quando múltiplas chaves forem requeridas.
- Garanta a atualização frequente de algoritmos de criptografia.
- Crie funcionalidades que saibam lidar com rotação de chave(key rotation)
Aplicações de gerenciamento de chaves
Existem diversas aplicações que realizam o gerenciamento seguro de chaves. Essas aplicações podem gerenciar desde senhas para conexão SQL, a chaves SSH. Portanto é extremamente não recomendado armazenar chaves em arquivos config e "commita-los" em repositórios. Um bom exemplo de aplicação utilizada atualmente para esta finalidade é a Vault da Hashcorp.
C9: Monitore as atividades de segurança
Logs já são bastante utilizado por desenvolvedores para proposito de debug e diagnóstico.
Log com foco em segurança é basicamente a mesma ideia: logar informações relevantes para o monitoramento de segurança durante uma operação na aplicação.
O mais interessante é que as mesmas ferramentas utilizadas para diagnóstico e debug, podem ser utilizadas para um log focado em segurança e existem diversos benefícios em ter esta prática:
- Detecção de intrusão.
- Análise e investigação de ataques.
- Satisfazer requisitos de compliance.
Implementação
Existem algumas recomendações para a implementação de logs focados na segurança. Alguns deles estão descritos abaixo.
- Defina um padrão de log através do sistema. Isso facilitiará a procura e a investigação de incidentes.
- Nem muito, nem tampouco. Informações que podem ajudar nesses tipos de investigações é o timestamp e informações que identifiquem a origem da conexão, como IP e user ID. É importante ter cuidado em não logar informações privadas e/ou confidenciais.
Log de movimentações suspeitas
É essencial para investigações de possiveis ataques detectar movimentações suspeitas. Algumas delas seriam:
Se sua aplicação detectar uma movimentação suspeita esta deve, no mínimo, marcar a atividade como uma tentativa de ataque através de uma ferramenta de monitoramento, como o Sentry. Idealmente a aplicação deve proativamente responder a tentativa, seja invalidando o acesso, seja rejeitando acesso ou executando um lock na conta do usuário.
- Executar uma chamada ao servidor não esperada.
- Submeter uma chamada ao servidor executando uma modificação de um dado que não deve ser modificado.
- Uma chamada que viola o acesso ao servidor.
Se sua aplicação detectar uma movimentação suspeita esta deve, no mínimo, marcar a atividade como uma tentativa de ataque através de uma ferramenta de monitoramento, como o Sentry. Idealmente a aplicação deve proativamente responder a tentativa, seja invalidando o acesso, seja rejeitando acesso ou executando um lock na conta do usuário.
Cuidados com o log
- Codifique e valide todos os caracteres para evitar log injection.
- Não registre informação sensivel.(senhas, session ID, cartões de crédito, etc.)
- Proteja a integridade dos logs. Dada a quantidade de informação que estes registro contém, atacantes irão sempre buscar informações lá.
- Em casos de sistemas distribuidos crie um serviço seguro de logs de forma central. Isto garantirá que o dado não será perdido ou comprometido. Este método também permite um controle centralizado dos logs.
C10: Capture todos erros e exceções
Capturar erros e exceções é critico para qualquer aplicação. É capturando os erros e exceções que, por exemplo, em exceções de banco a tabela não é exposta para o cliente.
Exceções ocorrem em todas as áreas da aplicação, incluindo áreas críticas da regra de negócio. Capturar exceções ajuda na análise de tentativas de ataques.
Pesquisas da Universidade de Toronto chegaram a conclusão de que até o menor dos erros pode levar a falhas catastróficas em sistemas distriuidos.
A não implementação de captura de exceções pode levar as mais diversas vulnerabildiades. Entre elas estão:
- Vazamento de informações: retorno de informações que expoem como o sistema funciona.(ex.: Usuário não existe, expor colunas do banco, stack trace)
- Denial of Service(DOS): Esta é uma tentativa de ataque comum em que um atacante tenta derrubar um servidor fazendo várias requisições, ao ponto que o servidor não consegue lidar com todas as elas. Isso leva a requisições válidas a serem rejeitadas. Uma boa tentativa de evitar este tipo de ataque é lançar HTTP 429 (Too Many Requests) o que rejeitará conexões apenas de atores especificos.
- Evite espalhar try/catch através do código. Lide com as exceções de maneira centralizada. Essa prática ajuda na manutenção de código.
- Garanta que as mensagens de exceção sejam genéricas e não exponham informações sensiveis. Ao mesmo tempo as exceções devem conter informações que ajudem os QAs e as equipes de suporte.
Conclusão
Vimos neste documento apenas algumas sugestões e tópicos sobre esse pequeno subconjunto de contra-medidas à ataques. Este documento serve apenas como uma iniciação em como os desenvolvedores podem evitar ou mitigar ataques às suas aplicações.@soareswallace