Criando APIs seguras

Quem sou eu?

Vinícius Campitelli

Vinícius Campitelli

Agenda

  • Autenticação e autorização
  • Melhores práticas com access tokens
  • Throttling e Rate Limiting
  • Camuflagem de IDs sequenciais
  • Criptografando e assinando requisições e respostas

Autenticação e autorização

Autenticação

É o ato de estabelecer ou confirmar algo (ou alguém) como autêntico (...)
Fonte: Wikipedia

Autenticação

Para autenticar algo ou alguém, precisamos de uma credencial que o identifique.
Ela precisa ser emitida por algo (ou alguém) que o autorizador confie.

“O que você sabe”

Autenticação baseada no conhecimento

Exemplos:
  • Usuário e senha
  • Certificado digital
  • Client ID e Client Secret

“O que você tem”

Autenticação baseada na propriedade

Exemplos:

Token ou cartão físico

Código via aplicativo

“O que você é”

Autenticação baseada na característica

Exemplos:
  • Impressão digital
  • Identificação de íris
  • Reconhecimento facial
  • Reconhecimento de voz

Autorização

É garantir que apenas usuários autorizados consumam os recursos protegidos de um sistema computacional
Fonte: Wikipedia

Autorização

Após saber quem é o cliente no processo de Autenticação, preciso agora entender o que ele pode fazer. Quais recursos ele pode consumir? Por quanto tempo?

Como efetuar autenticação e autorização?

Para autenticação, podemos utilizar o OpenID ou implementar nosso próprio sistema (por exemplo, através do banco de dados).

OAuth 2.0 é o protocolo mais conhecido de autorização. Ele, por si só, não contempla processos de autenticação.

Famosa tela de Autorização via OAuth 2

Utilizando o Grant de Authorization Code
(saiba como escolher o Grant Type certo nesse link)

Tela de exemplo do fluxo de Authorization Code do OAuth 2.0 do Google
Referência: Shotgun Debugging
Referências

Melhores práticas com access tokens

O que é um access token?

É um objeto que descreve o contexto de segurança de um processo (...)
Fonte: MSDN

E no mundo de APIs?

O processo de autenticação e autorização pode ser caro computacionalmente (consultas a banco de dados, servidores de autenticação externos, criptografia de senha etc)

E no mundo de APIs?

Ao invés de sempre termos essa carga a cada requisição a nossos servidores, podemos ter um único endpoint de autenticação / autorização que irá realizar essa etapa uma vez e gerar um access token com as informações necessárias para autenticar e autorizar mais rapidamente o cliente nas requisições seguintes

E no mundo de APIs?

Esse token deve possuir um tempo de vida limitado, tanto para diminuir efeitos colaterais caso haja um vazamento quanto porque sua informação pode ficar desatualizada

E no mundo de APIs?

Esse segundo processo pode ser automatizado através de refresh tokens

Eles são utilizados para regerar um access token automaticamente, sem necessitar da intervenção manual do usuário

Como deixar os tokens seguros?

  • Utilizando algoritmos de criptografia seguros e com assinatura;
    • AES-256-GCM, ChaCha20+Poly1305, Ed25519 entre outros
  • Tendo cuidado com implementações de bibliotecas com falhas de segurança;

Como deixar os tokens seguros?

  • Não faça commit de chaves e outros segredos, nem deixe-os hardcoded na aplicação
  • Não guarde informações muito sensíveis (como senhas e outras credenciais)
  • Se precisar armazenar do lado do cliente, utilize cookies seguros (flags httpOnly e secure) ao invés do LocalStorage

Como deixar os tokens seguros?

  • Apenas trafegue tokens no cabeçalho ou corpo da requisição, nunca na URL
  • Não exiba informações do token em seus logs

Exemplo de implementação

use Lcobucci\JWT\Builder;

$time = time();
$token = (new Builder())
    ->issuedBy('http://example.com') // Configures the issuer (iss claim)
    ->permittedFor('http://example.org') // Configures the audience (aud claim)
    ->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
    ->issuedAt($time) // Configures the time that the token was issue (iat claim)
    ->canOnlyBeUsedAfter($time + 60) // Configures the time that the token can be used (nbf claim)
    ->expiresAt($time + 3600) // Configures the expiration time of the token (exp claim)
    ->withClaim('uid', 1) // Configures a new claim, called "uid"
    ->getToken(); // Retrieves the generated token
lcobucci/jwt
Bibliotecas
JWT

Implementação mais conhecida de tokens

Bibliotecas
PASETO

Implementação com design "mais seguro" por padrão

Referências

Throttling e Rate Limiting

O que é Throttling?

É a desaceleração intencional do processamento de uma requisição para prevenir sobrecarga do servidor

O que é Throttling?

Imagine um endpoint que consuma grande recursos computacionais (como, por exemplo, o processo de autenticação e autorização descritos anteriormente)

O que é Throttling?

Se um agente malicioso identificar esse recurso, ele pode se tornar muito visado para ataques, causando sobrecarga no nosso servidor e podendo gerar paralisação e até queda total do serviço

O que é Throttling?

Nesses casos, podemos configurar que o servidor irá aceitar somente x requisições em algum(ns) endpoint(s) por um certo período de tempo

O que é Throttling?

Após esse valor x, as requisições entrarão em uma fila para serem processadas alguns instantes depois, assim que as primeiras tiverem sido liberadas

Mas então o que é Rate Limiting?

Ao contrário do Throttling, em a requisição será processada com um atraso, Rate Limiting é fazer o servidor se recusar a responder após um certo número de requisições

Mas então o que é Rate Limiting?

Nesses casos, devemos emitir um status HTTP 503 Service Unavailable ou 429 Too Many Requests

Como essas duas práticas se relacionam entre si?

Geralmente, primeiro aplicamos uma política de Throttling para desacelerar o processamento nas x primeiras requisições, e após um outro número y, iremos simplesmente parar de responder

Onde e como configurar essas políticas?

Você deve configurá-las em seu servidor Web (por exemplo, Apache, IIS ou nginx) ou no seu serviço de DNS (por exemplo, o Cloudflare)

Onde e como configurar essas políticas?

Tomando como exemplo o nginx, utilizaremos o módulo ngx_http_limit_req_module (que implementa o mais conhecido algoritmo desse tipo de prática, o Leaky Bucket)

Onde e como configurar essas políticas?


                                limit_req_zone $binary_remote_addr zone=login_zone:10m rate=10r/s;

                                server {
                                    location /login {
                                        limit_req zone=login_zone burst=5;

                                        # outras configurações padrões do meu bloco location...
                                    }
                                }
                            

Onde e como configurar essas políticas?

O código anterior irá proteger nosso endpoint /login, permitindo apenas 10 requisições por segundo por IP (ou seja, 1 a cada 100ms)

Onde e como configurar essas políticas?

Mas também permitimos um burst, permitindo que 5 requisições extras sejam colocadas na fila de processamento antes de serem enviadas para o upstream

Onde e como configurar essas políticas?

Nesse caso, se recebermos de um mesmo IP 10 requisições em um período de 100ms, o que ocorrerá?

  • A 1ª requisição será processada instantaneamente
  • As 2ª, 3ª, 4ª, 5ª e 6ª requisições serão colocadas em uma fila e serão processadas sequencialmente após o término da anterior
  • As 7ª, 8ª, 9ª e 10ª requisições serão negadas
Referências

Camuflagem de IDs sequenciais

O que é um ID sequencial e por que escondê-lo?

Ao criarmos endpoints no formato /user/1, /user/2, /user/3 etc, estamos fornecendo uma informação muito valiosa sobre a quantidade de registros que possuímos daquela entidade

O que é um ID sequencial e por que escondê-lo?

Isso pode ser utilizado tanto por alguém mal intencionado quanto por algum competidor.

Cuidado com espionagem industrial!

Versão Standard
Como resolver?

  • Crie um segundo campo na tabela com um valor único para aquele registro
    • Esse valor pode ser um GUID, um hash do ID incremental ou um valor aleatório...
  • Então, transforme o recurso /user/<IdIncremental> em /user/<Identificador>

Versão Standard
Como não resolver?

PHP

Versão Standard
Como não resolver?

PHP

Funções que não devem ser utilizadas:

  • Para gerar valores aleatórios:
    • rand()
    • mt_rand()
    • uniqid()
  • Para gerar hash:
    • sha1()
    • md5()

Versão Standard
Como resolver?

Node.JS

const { createHash } = require('crypto');

const hash = createHash('sha256');

user.lookupHash = hash.update(user.id).digest('hex');
                        

Versão Standard
Como resolver?

Python
  • Módulo crypt (built-in na versão 3)

                            from hashlib import sha256

                            sha256(user.id).hexdigest()
                        

Versão Hardcore
Como resolver?

Utilizando a biblioteca paragonie/ciphersweet, que deriva uma chave para cada coluna a partir de uma chave-mestra


Referência

Criptografando e assinando requisições e respostas

Precaução extra

Para usuários avançados e sistemas críticos, podemos adicionar uma camada extra de proteção: criptografar os dados da requisição e da resposta

Precaução extra

Mas, espera aí! Eu já uso HTTPS. Meu dados já estão sendo criptografados usando TLS.

Precaução extra

Após o estabelecimento do protocolo, realmente os dados transitados estão criptografados, mas ainda podemos sofrer um ataque de Man in the Middle

Man in the Middle

  • Existem diversos hops entre o cliente e o servidor. Quem garante que todos os gateways do caminho são seguros?
  • Se você não for o administrador da sua rede local, alguém pode instalar uma Autoridade Certificadora em sua máquina e emitir certificados próprios

Man in the Middle

  • Nem todos os sites utilizam HSTS
    • Ou seja, pode haver uma conexão HTTP antes do redirecionamento para HTTPS pelo servidor
  • Teoria da conspiração: grandes empresas possuem backdoors para bisbilhotar seu tráfego

Precaução extra

Antes de seguir nessa etapa, faça um Threat Modeling e certifique-se que realmente valhe a pena

Compensa instalar um sistema de segurança de última geração em um depósito que não possui algo de extremo valor dentro?

Precaução extra

SIM! Meu sistema é crítico e preciso disso. Ou eu não confio em grandes corporações.

Precaução extra

Você pode exigir que toda requisição tenha seu corpo criptografado pelo cliente, utilizando um segredo em que só vocês dois sabem

Precaução extra

Por exemplo, você pode utilizar uma terceira informação fora o Client ID e o Client Secret e usá-la como chave assimétrica, ou fornecer um certificado digital para seu cliente

Precaução extra

Do lado do servidor, você utiliza essa informação sigilosa para descriptografar o corpo da requisição - se falhar, você emite um erro e para o processamento

Precaução extra

Entretanto, esse processo de descriptografia pode ser muito custoso operacionalmente, podendo ser um outro ponto de ataque DoS (lembre-se do que falamos na seção de Throttling e Rate Limiting). Você pode, então, utilizar um processo de assinatura digital além da criptografia.

Fluxo de criptografia e assinatura

O cliente...
  • Autentica na sua API fornecendo as credenciais
  • Para cada requisição, assina e criptografa os dados (combinação do corpo da requisição + URL);
    • Utiliza um algoritmo de hash para assinar os dados
    • Criptografa os dados mais a assinatura
    • Envia a requisição somente com os dados criptografados
  • Para cada resposta seguinte, ele deve:
    • Verificar a assinatura
    • Descriptografar o corpo

Fluxo de criptografia e assinatura

A cada requisição recebida, o servidor deve...
  • Verificar a assinatura
  • Descriptografar o corpo
  • Ao responder à requisição, deve assinar e criptografar os dados (combinação do corpo da requisição + URL);
    • Utiliza um algoritmo de hash para assinar os dados
    • Criptografa os dados mais a assinatura
    • Envia a requisição somente com os dados criptografados
Referências

Concluindo...

  • Segurança não é trivial
    Segurança não é fácil
    Segurança não é para leigos
  • Nenhum sistema operante é invencível,
    mas você deve dificultar o trabalho de atacantes
  • Saiba equilibrar e avaliar a relação de custo-benefício entre segurança e usabilidade

Obrigado!

Slides

QR Code

Contato

GitHub e Twitter