Criando mecanismos de autenticação seguros

Criando mecanismos de autenticação seguros

Uma das partes mais básicas de um sistema é seu processo de login. E por ser algo que não agrega valor direto ao negócio, acabamos não gastando o tempo necessário para criar um fluxo realmente seguro, fazendo com que ele se torne o principal vetor de ataques de nossa aplicação. Iremos aprender como criar mecanismos seguros de autenticação, utilizando CSRF de forma correta, CAPTCHA, duplo fator de autenticação, prevenção à enumeração de usuários e autenticação em APIs. Veremos também diversas dicas de como implementar as arriscadas (mas muitas vezes necessárias) funções 'Permanecer logado' e 'Esqueci a senha'.

375596b28a94ecfaec5d63ff64c7f948?s=128

Vinícius Campitelli

October 19, 2019
Tweet

Transcript

  1. CRIANDO MECANISMOS CRIANDO MECANISMOS DE AUTENTICAÇÃO DE AUTENTICAÇÃO SEGUROS SEGUROS

    1
  2. QUEM SOU EU? QUEM SOU EU? Vinícius Campitelli Membro do

    Sócio-fundador do GitHub/Twitter: Slides: PHPSP Curseduca @vcampitelli viniciuscampitelli.com 2
  3. AGENDA AGENDA Protegendo CSRF de forma correta; CAPTCHA; Duplo fator

    de autenticação; Prevenção à enumeração de usuários; Funcionalidade "Permanecer logado"; Funcionalidade "Esqueci a senha". Login via terceiros; Autenticação em APIs. 3
  4. PROTEGENDO CSRF DE FORMA CORRETA PROTEGENDO CSRF DE FORMA CORRETA

    4 . 1
  5. PROTEGENDO CSRF DE FORMA CORRETA PROTEGENDO CSRF DE FORMA CORRETA

    (Cross-site request forgery) 4 . 1
  6. O QUE É NA PRÁTICA? O QUE É NA PRÁTICA?

    4 . 2
  7. O QUE É NA PRÁTICA? O QUE É NA PRÁTICA?

    Imagine que você esteja logado em um site site1.com 4 . 2
  8. O QUE É NA PRÁTICA? O QUE É NA PRÁTICA?

    Imagine que você esteja logado em um site site1.com Então acessa o site site2.com 4 . 2
  9. O QUE É NA PRÁTICA? O QUE É NA PRÁTICA?

    Imagine que você esteja logado em um site site1.com Então acessa o site site2.com E no site site2.com, há uma requisição POST site1.com/transferir? conta=1234&valor=1000 4 . 2
  10. O QUE É NA PRÁTICA? O QUE É NA PRÁTICA?

    Imagine que você esteja logado em um site site1.com Então acessa o site site2.com E no site site2.com, há uma requisição POST site1.com/transferir? conta=1234&valor=1000 Como você está autenticado no site1, essa requisição será válida 4 . 2
  11. O QUE É NA PRÁTICA? O QUE É NA PRÁTICA?

    Imagine que você esteja logado em um site site1.com Então acessa o site site2.com E no site site2.com, há uma requisição POST site1.com/transferir? conta=1234&valor=1000 Como você está autenticado no site1, essa requisição será válida E você irá perder seu precioso dinheiro! 4 . 2
  12. Mas por que eu preciso disso na tela de login?

    4 . 3
  13. Para garantir que um atacante não faça um usuário passar-se

    por ele Mas por que eu preciso disso na tela de login? 4 . 3
  14. COMO SE PROTEGER? COMO SE PROTEGER? 4 . 4

  15. COMO SE PROTEGER? COMO SE PROTEGER? Crie uma sessão "falsa"

    4 . 4
  16. COMO SE PROTEGER? COMO SE PROTEGER? Crie uma sessão "falsa"

    Gere um token para ele e guarde-o nessa sessão 4 . 4
  17. COMO SE PROTEGER? COMO SE PROTEGER? Crie uma sessão "falsa"

    Gere um token para ele e guarde-o nessa sessão Coloque esse token junto da requisição (em campos do formulário ou cabeçalhos do AJAX) 4 . 4
  18. COMO SE PROTEGER? COMO SE PROTEGER? Crie uma sessão "falsa"

    Gere um token para ele e guarde-o nessa sessão Coloque esse token junto da requisição (em campos do formulário ou cabeçalhos do AJAX) Na página que recebe a ação de login, verifique se o token enviado é o mesmo da sessão 4 . 4
  19. COMO SE PROTEGER? COMO SE PROTEGER? Crie uma sessão "falsa"

    Gere um token para ele e guarde-o nessa sessão Coloque esse token junto da requisição (em campos do formulário ou cabeçalhos do AJAX) Na página que recebe a ação de login, verifique se o token enviado é o mesmo da sessão Se quiser, verifique também o cabeçalho Referer (ou Origin) da requisição para garantir que a origem é do seu site 4 . 4
  20. PREMISSAS E RECOMENDAÇÕES PREMISSAS E RECOMENDAÇÕES 4 . 5

  21. PREMISSAS E RECOMENDAÇÕES PREMISSAS E RECOMENDAÇÕES O token não pode

    se repetir entre usuários diferentes, então é importante utilizar um (Cryptographically Secure Pseudo-Random Number Generator) CSPRNG 4 . 5
  22. PREMISSAS E RECOMENDAÇÕES PREMISSAS E RECOMENDAÇÕES O token não pode

    se repetir entre usuários diferentes, então é importante utilizar um (Cryptographically Secure Pseudo-Random Number Generator) Não pode ser muito pequeno (para evitar brute force) CSPRNG 4 . 5
  23. PREMISSAS E RECOMENDAÇÕES PREMISSAS E RECOMENDAÇÕES O token não pode

    se repetir entre usuários diferentes, então é importante utilizar um (Cryptographically Secure Pseudo-Random Number Generator) Não pode ser muito pequeno (para evitar brute force) Não pode aparecer nos logs do servidor CSPRNG 4 . 5
  24. PREMISSAS E RECOMENDAÇÕES PREMISSAS E RECOMENDAÇÕES O token não pode

    se repetir entre usuários diferentes, então é importante utilizar um (Cryptographically Secure Pseudo-Random Number Generator) Não pode ser muito pequeno (para evitar brute force) Não pode aparecer nos logs do servidor Não pode estar na URL CSPRNG 4 . 5
  25. PREMISSAS E RECOMENDAÇÕES PREMISSAS E RECOMENDAÇÕES (CONTINUAÇÃO) (CONTINUAÇÃO) 4 .

    6
  26. PREMISSAS E RECOMENDAÇÕES PREMISSAS E RECOMENDAÇÕES (CONTINUAÇÃO) (CONTINUAÇÃO) Você pode

    gerar tokens a cada requisição ao invés de um por sessão, mas isso interfere na usabilidade do sistema (por exemplo, o botão "Voltar" do navegador irá causar uma falha de CSRF) 4 . 6
  27. PREMISSAS E RECOMENDAÇÕES PREMISSAS E RECOMENDAÇÕES (CONTINUAÇÃO) (CONTINUAÇÃO) Você pode

    gerar tokens a cada requisição ao invés de um por sessão, mas isso interfere na usabilidade do sistema (por exemplo, o botão "Voltar" do navegador irá causar uma falha de CSRF) Se você quiser uma abordagem stateless, pode gerar um token criptografado com o Session ID do usuário e um timestamp, por exemplo 4 . 6
  28. REFERÊNCIAS REFERÊNCIAS BIBLIOTECAS BIBLIOTECAS OWASP CheatSheet de prevenção de CSRF

    Paper "Robust Defenses for Cross-Site Request Forgery" de Stanford symfony/security-csrf paragonie/anti-csrf 4 . 7
  29. CAPTCHA CAPTCHA 5 . 1

  30. 5 . 2

  31. 5 . 3

  32. DEFINIÇÃO DEFINIÇÃO Completely Automated Public Turing test to tell Computers

    and Humans Apart 5 . 4
  33. REFERÊNCIAS REFERÊNCIAS reCAPTCHA v3 Securimage PHP CAPTCHA 5 . 5

  34. DUPLO FATOR DE AUTENTICAÇÃO DUPLO FATOR DE AUTENTICAÇÃO 6 .

    1
  35. HOTP HOTP é um algoritmo baseado em que gera uma

    senha a partir de uma chave HMAC-based One-Time Password HMAC 6 . 2
  36. HOTP HOTP é um algoritmo baseado em que gera uma

    senha a partir de uma chave Para garantir a unicidade (afinal, há One-Time no nome), é incrementado um contador a cada geração/checagem do valor HMAC-based One-Time Password HMAC 6 . 2
  37. TOTP TOTP é uma extensão do HOTP, que utiliza o

    horário atual ao invés do contador para a unicidade Time-based One-Time Password 6 . 3
  38. TOTP TOTP é uma extensão do HOTP, que utiliza o

    horário atual ao invés do contador para a unicidade Geralmente, há uma janela de 30 segundos de validade da senha Time-based One-Time Password 6 . 3
  39. None
  40. 6 . 4

  41. public function __construct() { $this->authenticator = new \Sonata\GoogleAuthenticator\G $this->secret =

    getAuthenticatorSecretFromSomewhere(); } public function check(string $code) : bool { return $this->authenticator->checkCode($this->secret, $c } public fuction generateSecret() : string { return $this->authenticator->generateSecret() 6 . 5
  42. BLOQUEIE O USUÁRIO APÓS ALGUMAS TENTATIVAS BLOQUEIE O USUÁRIO APÓS

    ALGUMAS TENTATIVAS INVÁLIDAS DO TOKEN DO 2FA! INVÁLIDAS DO TOKEN DO 2FA! 6 . 6
  43. Tokens de 6 dígitos possuem 1.000.000 de combinações (000000 -

    999999) 6 . 7
  44. Tokens de 6 dígitos possuem 1.000.000 de combinações (000000 -

    999999) Se um atacante fizer 1 requisição por segundo ao seu sistema, ele irá demorar 1 milhão de segundos para testar todas as combinações possíveis 6 . 7
  45. Tokens de 6 dígitos possuem 1.000.000 de combinações (000000 -

    999999) Se um atacante fizer 1 requisição por segundo ao seu sistema, ele irá demorar 1 milhão de segundos para testar todas as combinações possíveis 1000000 / 86400 = 11.574074074 6 . 7
  46. Tokens de 6 dígitos possuem 1.000.000 de combinações (000000 -

    999999) Se um atacante fizer 1 requisição por segundo ao seu sistema, ele irá demorar 1 milhão de segundos para testar todas as combinações possíveis 1000000 / 86400 = 11.574074074 Ou seja, o token será quebrado em menos de 12 dias 6 . 7
  47. Tokens de 6 dígitos possuem 1.000.000 de combinações (000000 -

    999999) Se um atacante fizer 1 requisição por segundo ao seu sistema, ele irá demorar 1 milhão de segundos para testar todas as combinações possíveis 1000000 / 86400 = 11.574074074 Ou seja, o token será quebrado em menos de 12 dias Mas que atacante só faz 1 requisição por segundo? 6 . 7
  48. REFERÊNCIAS, BIBLIOTECAS E SOLUÇÕES REFERÊNCIAS, BIBLIOTECAS E SOLUÇÕES Why You

    Don't Need 2 Factor Authentication SMS-based two-factor authentication is not safe sonata-project/google-authenticator twilio/authy-php Twilio SMS Nexmo SMS 6 . 8
  49. PREVENÇÃO À ENUMERAÇÃO DE USUÁRIOS PREVENÇÃO À ENUMERAÇÃO DE USUÁRIOS

    7 . 1
  50. O que um sistema que rode o código a seguir

    pode dar de informação a um atacante? 7 . 2
  51. public function login(string $username, string $password) : { $user =

    $this->findByUsername($username); if (! $user) { return false; } return $this->verifyPassword($password, $user->getPasswo } 7 . 3
  52. É possível saber que usuários existem em sua aplicação através

    de timing attack 7 . 4
  53. É possível saber que usuários existem em sua aplicação através

    de timing attack Modifique o código para que a custosa função verifyPassword seja sempre invocada! 7 . 4
  54. public function login(string $username, string $password) : { $storedPassword =

    $this->generateFakePassword(); $user = $this->findByUsername($username); if ($user) { $storedPassword = $user->getPassword(); } return ($this->verifyPassword($password, $storedPassword && ($user !== null); } 7 . 5
  55. Será que não estamos exagerando? É realmente possível ver essa

    diferença? 7 . 6
  56. Será que não estamos exagerando? É realmente possível ver essa

    diferença? Vamos testar, então! Executando os scripts login-user-enumeration.php e login-user-enumeration-fixed.php 7 . 6
  57. 0:00 / 0:58 7 . 7

  58. FUNCIONALIDADE "PERMANECER LOGADO" FUNCIONALIDADE "PERMANECER LOGADO" 8 . 1

  59. SOLUÇÃO MAIS SIMPLES SOLUÇÃO MAIS SIMPLES 8 . 2

  60. SOLUÇÃO MAIS SIMPLES SOLUÇÃO MAIS SIMPLES Gerar um token único

    8 . 2
  61. SOLUÇÃO MAIS SIMPLES SOLUÇÃO MAIS SIMPLES Gerar um token único

    Guardá-lo no cookie do browser do usuário 8 . 2
  62. SOLUÇÃO MAIS SIMPLES SOLUÇÃO MAIS SIMPLES Gerar um token único

    Guardá-lo no cookie do browser do usuário Salvar esse token em um banco de dados relacionando ao usuário 8 . 2
  63. SOLUÇÃO MAIS SIMPLES SOLUÇÃO MAIS SIMPLES Gerar um token único

    Guardá-lo no cookie do browser do usuário Salvar esse token em um banco de dados relacionando ao usuário Sempre que é feito o acesso à tela de login, comparar os tokens 8 . 2
  64. CUIDADOS CUIDADOS 8 . 3

  65. CUIDADOS CUIDADOS Garanta que o token será único para o

    usuário e não previsível (novamente, use ) CSPRNG 8 . 3
  66. CUIDADOS CUIDADOS Garanta que o token será único para o

    usuário e não previsível (novamente, use ) Faça comparações que tenham tempo fixo para prevenir timing attacks (por exemplo, hash_equals()) CSPRNG 8 . 3
  67. CUIDADOS CUIDADOS Garanta que o token será único para o

    usuário e não previsível (novamente, use ) Faça comparações que tenham tempo fixo para prevenir timing attacks (por exemplo, hash_equals()) Precisa ser eficiente, para não permitir CSPRNG DoS 8 . 3
  68. SOLUÇÃO MAIS SEGURA SOLUÇÃO MAIS SEGURA 8 . 4

  69. 1. Prefixe o token com um identificador (NÃO use diretamente

    o ID do usuário) $identifier = hash('sha256', $userId); // 64 bytes $token = bin2hex(random_bytes(32)); // 64 bytes $cookie = $identifier . $token; 8 . 5
  70. 2. Crie uma tabela no banco de dados com o

    identificador acima e somente o hash do token ao invés dele CREATE TABLE `rememberme_tokens` ( `id` int unsigned AUTO_INCREMENT, `identifier` char(64) NOT NULL, `token_hash` char(128) NOT NULL, PRIMARY KEY (`id`) ) $hashedToken = hash('sha512', $token); $pdo->prepare('INSERT INTO rememberme_tokens (NULL, ?, ?)') ->execute([$identifier, $hashedToken]); 8 . 6
  71. 3. Ao consultar o banco, utilize apenas o identificador $query

    = $pdo->prepare( 'SELECT `token_hash` FROM `rememberme_tokens` WHERE `identifier` = ? LIMIT 1' ); $hashFromDatabase = $query->fetch(PDO::FETCH_COLUMN); 8 . 7
  72. 4. Faça o hash do token informado no cookie do

    usuário e compare-o com o que está no banco (com hash_equals()) $hashedToken = hash('sha512', substr($cookie, 64)); if (hash_equals($hashedToken, $hashFromDatabase)) { redirectToHome(); } else { redirectToLogin(); } 8 . 8
  73. REFERÊNCIA E BIBLIOTECA REFERÊNCIA E BIBLIOTECA "Remember Me" - Long-Term

    Persistent Authentication (Paragon IE) psecio/gatekeeper 8 . 9
  74. FUNCIONALIDADE "ESQUECI A SENHA" FUNCIONALIDADE "ESQUECI A SENHA" 9 .

    1
  75. PRECAUÇÕES PRECAUÇÕES 9 . 2

  76. PRECAUÇÕES PRECAUÇÕES Perguntas secretas estão cada vez menos secretas por

    causa das redes sociais 9 . 2
  77. PRECAUÇÕES PRECAUÇÕES Perguntas secretas estão cada vez menos secretas por

    causa das redes sociais Emails não são sempre encriptados 9 . 2
  78. PRECAUÇÕES PRECAUÇÕES Perguntas secretas estão cada vez menos secretas por

    causa das redes sociais Emails não são sempre encriptados Emails podem não chegar 9 . 2
  79. PRECAUÇÕES PRECAUÇÕES Perguntas secretas estão cada vez menos secretas por

    causa das redes sociais Emails não são sempre encriptados Emails podem não chegar SMS não são confiáveis 9 . 2
  80. PREPARE-SE! PREPARE-SE! 9 . 3

  81. PREPARE-SE! PREPARE-SE! Realmente avalie se seu sistema precisa dessa funcionalidade

    automática 9 . 3
  82. PREPARE-SE! PREPARE-SE! Realmente avalie se seu sistema precisa dessa funcionalidade

    automática NÃO crie uma nova senha e envie por email 9 . 3
  83. PREPARE-SE! PREPARE-SE! Realmente avalie se seu sistema precisa dessa funcionalidade

    automática NÃO crie uma nova senha e envie por email NÃO mande a senha em texto plano por email 9 . 3
  84. PREPARE-SE! PREPARE-SE! Realmente avalie se seu sistema precisa dessa funcionalidade

    automática NÃO crie uma nova senha e envie por email NÃO mande a senha em texto plano por email (o quêêê?????) 9 . 3
  85. PREPARE-SE! PREPARE-SE! Realmente avalie se seu sistema precisa dessa funcionalidade

    automática NÃO crie uma nova senha e envie por email NÃO mande a senha em texto plano por email (o quêêê?????) Se for fornecido um usuário que não existe na base, não o avise! 9 . 3
  86. SOLUÇÃO SOLUÇÃO 9 . 4

  87. TENTATIVA DE TENTATIVA DE SOLUÇÃO SOLUÇÃO 9 . 4

  88. TENTATIVA DE TENTATIVA DE SOLUÇÃO SOLUÇÃO Utilize a do token

    descrita no slide de "Permanecer logado" mesma abordagem 9 . 4
  89. TENTATIVA DE TENTATIVA DE SOLUÇÃO SOLUÇÃO Utilize a do token

    descrita no slide de "Permanecer logado" Coloque um tempo limite para expiração do token mesma abordagem 9 . 4
  90. TENTATIVA DE TENTATIVA DE SOLUÇÃO SOLUÇÃO Utilize a do token

    descrita no slide de "Permanecer logado" Coloque um tempo limite para expiração do token Envie o código para o usuário (se for por email, criptografe!) mesma abordagem 9 . 4
  91. REFERÊNCIAS E BIBLIOTECA REFERÊNCIAS E BIBLIOTECA Account Recovery (Paragon IE)

    Untangling the Forget-Me Knot: Secure Account Recovery Made Simple paragonie/gpg-mailer 9 . 5
  92. LOGIN VIA TERCEIROS LOGIN VIA TERCEIROS 10 . 1

  93. 10 . 2

  94. REFERÊNCIAS E BIBLIOTECAS REFERÊNCIAS E BIBLIOTECAS Facebook SDK - Login

    Google Login TwitterOAuth Login GitHub Login openid/php-openid Auth0 10 . 3
  95. AUTENTICAÇÃO EM APIS AUTENTICAÇÃO EM APIS 11 . 1

  96. DICAS DICAS 11 . 2

  97. DICAS DICAS Use HTTPS sempre (♥ ) Let's Encrypt 11

    . 2
  98. DICAS DICAS Use HTTPS sempre (♥ ) Não crie seu

    próprio sistema de autenticação, utilize um protocolo já conhecido ( , , entre outros) Let's Encrypt OpenID OAuth 2.0 11 . 2
  99. DICAS DICAS Use HTTPS sempre (♥ ) Não crie seu

    próprio sistema de autenticação, utilize um protocolo já conhecido ( , , entre outros) Para geração dos identificadores do usuário (por exemplo, client secret no OAuth 2) utilize Let's Encrypt OpenID OAuth 2.0 CSPRNG 11 . 2
  100. DICAS DICAS (CONTINUAÇÃO) (CONTINUAÇÃO) 11 . 3

  101. DICAS DICAS (CONTINUAÇÃO) (CONTINUAÇÃO) Para tokens, utilize ou JWT Paseto

    11 . 3
  102. DICAS DICAS (CONTINUAÇÃO) (CONTINUAÇÃO) Para tokens, utilize ou Se for

    usar JWT, garanta que seu sistema não permita um algorithm=none JWT Paseto 11 . 3
  103. DICAS DICAS (CONTINUAÇÃO) (CONTINUAÇÃO) Para tokens, utilize ou Se for

    usar JWT, garanta que seu sistema não permita um algorithm=none Mesmo se usar um , não deixe seus microserviços sem autenticação. Repasse o token para todos! JWT Paseto API Gateway 11 . 3
  104. REFERÊNCIAS REFERÊNCIAS BIBLIOTECAS BIBLIOTECAS Critical vulnerabilities in JSON Web Token

    libraries Token Based Authentication Made Easy lcobucci/jwt thephpleague/oauth2-server 11 . 4
  105. RECAPITULANDO... RECAPITULANDO... 12

  106. RECAPITULANDO... RECAPITULANDO... Login seguro não é trivial 12

  107. RECAPITULANDO... RECAPITULANDO... Login seguro não é trivial fácil 12

  108. RECAPITULANDO... RECAPITULANDO... Login seguro não é trivial fácil para leigos

    12
  109. RECAPITULANDO... RECAPITULANDO... Login seguro não é trivial fácil para leigos

    Mas é possível! 12
  110. SLIDES SLIDES viniciuscampitelli.com 13