Integrar a verificação por SMS em uma aplicação PHP é uma maneira robusta de confirmar a identidade de um usuário, proteger contas e reduzir cadastros falsos. Este guia mostra como construir um fluxo de verificação passo a passo, desde a geração do código até a validação final.

Por que usar verificação por SMS em aplicações PHP?

A verificação por SMS, frequentemente usada para Autenticação de Dois Fatores (o que é 2FA), adiciona uma camada essencial de segurança. Para aplicações PHP, os benefícios são diretos:

  • Validação de Usuário: Garante que o usuário por trás de um cadastro é real e possui acesso ao número de telefone informado.
  • Prevenção de Fraudes: Dificulta a criação de contas em massa (bots), que podem ser usadas para spam ou abuso de serviços.
  • Recuperação de Conta: Oferece um canal seguro para que usuários legítimos possam redefinir suas senhas ou recuperar o acesso à conta.
  • Conformidade: Em muitos setores, a verificação de identidade é um requisito regulatório.

Frameworks modernos como Laravel e Symfony possuem ecossistemas que facilitam a integração de serviços de terceiros, incluindo APIs de SMS.

Pré-requisitos para enviar SMS com PHP

Antes de escrever a primeira linha de código, você precisa garantir que seu ambiente está preparado. A lista de requisitos é curta e direta:

  1. Ambiente PHP: Uma versão estável do PHP instalada (versão 8.0 ou superior é recomendada).
  2. Composer: O gerenciador de dependências do PHP. Ele será usado para instalar um cliente HTTP.
  3. Cliente HTTP: Uma biblioteca para fazer requisições à API de SMS. Guzzle é a escolha mais popular e robusta.
  4. Conta em uma API de SMS: Você precisará se cadastrar em um provedor de SMS, como a SMS VOLT, para obter uma chave de API (API Key).

Com esses itens, você já pode começar a implementação. O processo de como usar uma API de SMS é padronizado e se resume a enviar requisições HTTP para um endpoint específico.

Passo a passo: Implementando o fluxo de verificação

Vamos dividir o processo em cinco etapas lógicas, cobrindo desde a configuração do projeto até a validação do código inserido pelo usuário.

Passo 1: Estrutura do projeto e dependências

Crie um diretório para o seu projeto e, dentro dele, inicialize o Composer e adicione o Guzzle.

# Crie o diretório e entre nele
mkdir verificacao-php && cd verificacao-php

# Inicie um projeto composer
composer init --quiet

# Adicione o Guzzle
composer require guzzlehttp/guzzle

Isso criará um arquivo composer.json e um diretório vendor, contendo o Guzzle e suas dependências.

Passo 2: Gerar e armazenar o código OTP

O código de verificação (OTP - One-Time Password) deve ser gerado no servidor. Um código numérico de 6 dígitos é o padrão de mercado.

Crie uma função para gerar o código e armazene-o de forma temporária, associado ao usuário. A sessão do PHP ($_SESSION) é uma forma simples de fazer isso para fins de exemplo.

<?php
session_start();

function gerarCodigoOTP(int $length = 6): string {
    return str_pad(random_int(0, 10**$length - 1), $length, '0', STR_PAD_LEFT);
}

// Gera o código
$codigo = gerarCodigoOTP();

// Armazena na sessão com um timestamp de expiração (ex: 5 minutos)
$_SESSION['otp'] = $codigo;
$_SESSION['otp_expires_at'] = time() + (5 * 60);

O armazenamento temporário é crucial. A validade do código deve ser curta para minimizar o risco de abuso. Saiba mais sobre quanto tempo dura um código OTP.

Passo 3: Enviar o SMS via API

Com o código gerado, o próximo passo é enviá-lo para o telefone do usuário usando a API de SMS. Usaremos o Guzzle para fazer a requisição POST.

<?php
require 'vendor/autoload.php';

use GuzzleHttp\Client;

// ... (código de geração do OTP) ...

$apiKey = 'SUA_API_KEY'; // Substitua pela sua chave de API
$numeroDestino = '+55119XXXXXXXX'; // Número do usuário no formato E.164
$mensagem = "Seu código de verificação é: {$codigo}";

$client = new Client();

try {
    $response = $client->request('POST', 'https://api.smsvolt.com.br/v1/send', [
        'headers' => [
            'Authorization' => 'Bearer ' . $apiKey,
            'Accept' => 'application/json',
        ],
        'json' => [
            'to' => $numeroDestino,
            'message' => $mensagem,
            // 'from' => 'SeuNome' // Opcional, dependendo da API
        ]
    ]);

    if ($response->getStatusCode() === 200) {
        echo "SMS enviado com sucesso.";
    } else {
        echo "Falha ao enviar SMS: " . $response->getBody();
    }
} catch (\GuzzleHttp\Exception\RequestException $e) {
    echo "Erro na requisição: " . $e->getMessage();
}

Passo 4: Criar o formulário de verificação

Após o envio do SMS, apresente ao usuário um formulário simples para que ele possa inserir o código recebido.

<!DOCTYPE html>
<html lang="pt-br">
<head>
    <title>Verificar Código</title>
</head>
<body>
    <form action="validar_codigo.php" method="post">
        <label for="codigo">Digite o código recebido por SMS:</label>
        <input type="text" id="codigo" name="codigo" maxlength="6" required>
        <button type="submit">Verificar</button>
    </form>
</body>
</html>

Passo 5: Validar o código inserido pelo usuário

Crie o script validar_codigo.php para processar o formulário. Ele deve comparar o código enviado pelo usuário com o que foi armazenado na sessão e verificar se não expirou.

<?php
session_start();

$codigoUsuario = $_POST['codigo'] ?? '';

// Verifica se o código da sessão e a expiração existem
if (!isset($_SESSION['otp']) || !isset($_SESSION['otp_expires_at'])) {
    die('Erro: Nenhuma verificação iniciada.');
}

// Verifica se o tempo não expirou
if (time() > $_SESSION['otp_expires_at']) {
    unset($_SESSION['otp'], $_SESSION['otp_expires_at']); // Limpa a sessão
    die('Código expirado. Por favor, solicite um novo.');
}

// Compara o código do usuário com o da sessão
if ($codigoUsuario === $_SESSION['otp']) {
    unset($_SESSION['otp'], $_SESSION['otp_expires_at']); // Limpa a sessão após o sucesso
    echo "Telefone verificado com sucesso!";
    // Aqui você marcaria o usuário como verificado no banco de dados
} else {
    // Se o código estiver errado, você pode implementar um contador de tentativas
    die('Código inválido. Tente novamente.');
}

Se o usuário digitar um código errado, é importante saber o que fazer quando o código OTP é inválido para fornecer a orientação correta.

Boas práticas de segurança

Implementar o fluxo básico é o primeiro passo. Para uma aplicação em produção, considere estas práticas de segurança:

  • Rate Limiting: Limite o número de tentativas de envio de código para um mesmo número ou endereço IP. Isso previne ataques de força bruta e o esgotamento de seus créditos de SMS. Saiba mais sobre rate limit em API de SMS.
  • Variáveis de Ambiente: Nunca armazene sua chave de API diretamente no código. Use um arquivo .env e uma biblioteca como vlucas/phpdotenv para carregar as credenciais.
  • Validação de Input: Sempre valide e sanitize o número de telefone antes de enviá-lo para a API. Utilize um formato padrão como E.164. Veja como validar o formato de telefone E.164.
  • HTTPS: Garanta que todas as comunicações entre seu servidor e a API de SMS ocorram sobre HTTPS para criptografar os dados em trânsito.
  • Feedback Genérico: Em caso de falha na validação, retorne mensagens genéricas como "Código inválido ou expirado" para não informar a um atacante qual parte da validação falhou.

Perguntas frequentes

Preciso de uma biblioteca específica para enviar SMS em PHP?

Não necessariamente. Um cliente HTTP como o Guzzle ou até mesmo as funções curl_*() nativas do PHP são suficientes para consumir a maioria das APIs de SMS baseadas em REST. Bibliotecas apenas simplificam o processo.

Como posso testar o envio de SMS sem gastar créditos?

Alguns provedores de API oferecem um ambiente de "sandbox" ou "modo de teste". Nesse modo, a API aceita suas requisições e retorna uma resposta de sucesso, mas não dispara o SMS real, permitindo testar a lógica da sua aplicação sem custos.

É seguro enviar a chave da API em cada requisição?

Sim, desde que a comunicação seja feita via HTTPS e a chave esteja armazenada de forma segura no seu servidor (em variáveis de ambiente, por exemplo), e não exposta no código do lado do cliente (HTML/JavaScript). Este é o padrão de autenticação para a maioria das APIs REST.

Posso usar cURL em vez de Guzzle?

Sim. Guzzle é um wrapper que torna as requisições HTTP mais simples e legíveis, mas você pode obter o mesmo resultado usando as funções curl_*() do PHP. O código com cURL nativo tende a ser um pouco mais verboso, mas é igualmente eficaz.