Pix: A falsa premissa de privacidade das chaves aleatórias

O método de pagamento Pix é inegavelmente uma das maiores inovações em nosso sistema bancário em anos. Prático, rápido... seguro? Nesse texto, discutimos a questão de privacidade com as chaves Pix. Afinal, optar por uma chave aleatória é o suficiente para protegermos dados sensíveis como nossos CPFs?

O sistema de pagamentos Pix, que vem funcionando integralmente desde o dia 16 de novembro de 2020, é, sem sombra de dúvidas, uma das novidades de maior sucesso do Banco Central nos últimos anos.​

As vantagens para nós, pessoas físicas, são diversas e evidentes: simplicidade, rapidez, gratuidade, maior disponibilidade... e essas são apenas algumas! Por tudo isso, eu posso confessar que muito rapidamente me tornei um grande adepto desse novo meio de pagamentos, que de fato facilitou bastante vários aspectos da minha vida. Há um ponto, porém, que sempre se destacou para mim como um dos maiores benefícios do Pix - as chaves.

Como um ferrenho defensor e entusiasta da privacidade e segurança digital, as chaves Pix me atraíram rapidamente. Enquanto nas transfêrencias monetárias via TED e DOC nós sempre precisamos saber o nome completo e o CPF do destinatário, com o Pix basta apenas uma chave pré-cadastrada, que pode ser o CPF, um email, um número de telefone ou, ainda, um código aleatório! Mas, afinal, por que isso importa tanto?

CPFs não são documentos abertos

É de absoluta necessidade entendermos a relevância em tomarmos cuidados na publicização de nossos dados pessoais. Nosso nome completo e, acima de tudo, nosso CPF são informações (cada vez mais) sensíveis. Não é de hoje que venho destacando a importância de protegermos nossa privacidade, mas nunca é demais lembrarmos dos riscos que o acesso aberto a dados como CPF, número de telefone, nome completo e outros documentos podem trazer.

Para isso, sugiro um exercício simples. Em época de declarações de Imposto de Renda, tente prestar atenção em quais documentos são solicitados para sua autenticação nesse processo tão importante. Procure como abrir empresas e verifique quais documentos são necessários. Repare em como usamos o CPF para tantas atividades tão importantes e tente não se desesperar com a falta de privacidade sobre ele.

Roubo de identidade não é uma piada, mas às vezes parece que é tratado como tal. Hoje em dia, não é difícil nem incomum o uso de nomes e dados de terceiros para a realização de crimes pela Internet, por exemplo. Por tudo isso, a possibilidade de usarmos dados não-sensíveis para realizarmos transferências monetárias é, inegavelmente, fenomenal.

Claro, a possibilidade não significa garantia - sobre isso nunca houve dúvida. Estatísticas de fevereiro apontam que as chaves de CPF continuam sendo as mais utilizadas, dentre todas as modalidades. Mais bizarro que isso foi ver os montes de CPFs sendo divulgados abertamente em redes sociais como o Twitter, em pedidos de doações e divulgações de crowdfundings. Isso tudo, entretanto, não me desanimou. Pelo contrário - entendo o movimento inicial de uso massivo do CPF como chave Pix e acredito na possibilidade de reversão disso. Não é à toa que continuo mantendo esse blog, mesmo em momentos que o trabalho remunerado me atola.

Agora, será que podemos dizer que o único problema na privacidade dos dados do Pix se dá na parte dos usuários? Se for assim, basta apenas a conscientização da população acerca desse grande problema? Antes de passarmos para a investigação disso mais a fundo, vamos entender o que nos foi prometido.

A promessa de privacidade do Pix

Se parece que eu exagero na importância da privacidade e proteção de dados no Pix, cabe aqui deixar claro que isso, obviamente, não partiu de mim. Há toda uma seção na página de perguntas e respostas do Pix, no site do Banco Central, que garante a proteção das informações trafegadas nas transações Pix tanto pelo sigilo bancário quanto pelas disposições da Lei Geral de Proteção de Dados.

Também não fui o único que entrou na expectativa de usar chaves aleatórias para proteger meus dados pessoais. Em matéria da UOL, o diretor de cibersegurança da Psafe, Emilio Simoni, destaca que "para compras em sites, lojas e para pessoas que você não conhece, o ideal é usar uma chave aleatória"

Na mesma matéria, o advogado Luis Fernando Prado, especializado em direito digital, declara que "O CPF é um dado pessoal que nos identificará por toda a vida. O ideal para garantir privacidade é usar uma chave aleatória."

A expectativa, portanto, cresceu em cima da ideia que o uso de uma chave aleatória garantiria o sigilo dos nossos dados privados, já que dispensaria a necessidade de passarmos informações sensíveis, como nosso CPF, para um pagador.

Cada vez mais, a ideia de usar a chave aleatória com o Pix vem sendo divulgada como uma alternativa segura, especialmente em comparação às transferências TED e DOC, cuja divulgação do CPF é obrigatória. Eu mesmo venho defendendo isso, até que recentemente, em um trabalho de OSINT, percebi meu erro. E, camarada, foi um grande erro. Uma chave aleatória pode esconder nossos dados pessoais à primeira vista, de fato. Entretanto, descobri que através de qualquer chave Pix é extremamente fácil descobrir o CPF do utilizador. De fato, muito mais fácil do que eu poderia imaginar.

Diz-me sua chave Pix e dir-te-ei seu CPF

Digamos que você venda um produto e combine com o comprador que o pagamento será realizado via Pix. Para isso, você passa ao comprador uma chave aleatória, com a intenção de proteger seus dados pessoais. Ao digitar sua chave no aplicativo bancário, três informações sobre o proprietário da chave aparecem ao pagador: o nome completo, a instituição bancária e o CPF incompleto. Ok, vamos por partes.

Em primeiro lugar, temos o nome completo. Tudo bem, talvez possa não parecer o ideal, mas não temos muitas opções para fugir disso pelo nosso sistema bancário. O próprio Banco Central dá uma justificativa para isso:

"Há um entendimento geral de que o compartilhamento dessas informações não representa risco significativo para os usuários. Pelo contrário: as informações ajudam o pagador (que normalmente é a parte mais vulnerável da transação) a confirmar a identidade do recebedor, tornando o processo mais seguro"

De fato, o pagador precisa ter alguma confirmação acerca da identidade do destinatário da transação antes de ela ser efetuada. Se não houvesse nenhuma informação do tipo, o risco de fraudes acaba sendo muito alto.

A mesma justificativa pode ser usada para a instituição bancária - é importante sabermos para que banco nosso dinheiro está indo. Caso haja algum problema, já temos alguma referência para solicitar ajuda. Mas e o CPF?

Não posso ser desonesto: o CPF do destinatário não aparece por completo, a não ser quando o próprio documento é usado como chave Pix. Nos outros casos, o pagador tem acesso apenas aos 6 dígitos do meio do CPF. Para o Banco Central, a omissão dos outros números é suficiente para a redução de riscos de quebra de privacidade. A verdade é que não é bem assim...

Todo CPF é formado por 11 dígitos, no formato 999.999.999-99. Ao exibir apenas os 6 dígitos do meio, outros 5 dígitos são omitidos. Para termos um exemplo concreto, vamos supor que o CPF incompleto exibido pra gente é ***.123.456-**. Se considerarmos que cada dígito tem 10 possibilidades (de 0 a 9), podemos concluir que existem 105, ou 100.000 CPFs possíveis cujos 6 dígitos do meio são aqueles exibidos. Seguindo nosso exemplo, teríamos 000.123.456-00, 000.123.456-01 e assim em diante, certo? Aí é que tá - errado!

Acontece que, como deve ser esperado de um documento desse porte, o CPF não é simplesmente o conjunto de 11 dígitos aleatórios. A estrutura é a seguinte:

  • Os 8 primeiros dígitos são arbritariamente designados no momento da inscrição;
  • O 9º dígito indica uma das 10 Regiões Fiscais do Brasil;
  • Os 2 últimos dígitos são verificadores, calculados a partir dos outros dígitos

Sendo assim, a situação é diferente. Sabendo que os 2 últimos dígitos são sempre calculados considerando os outros 9, podemos desconsiderá-los da conta - para cada combinação de 9 dígitos, há 2 números exatos que caracterizarão o conjunto como um CPF. O que importa pra gente, portanto, é exatamente essa combinação dos 9 primeiros dígitos. Agora, se temos acesso a 6 desses dígitos, faltam 3. Usando a mesma conta de antes, concluímos que existem 103, ou 1.000 CPFs possíveis cujos 6 dígitos do meio são aqueles exibidos. Seguindo nosso exemplo: 000.123.456-**, 001.123.456-**, e assim em diante.

É aí que as coisas complicam pra nossa privacidade. Mil possibilidades é um número muito, muito baixo. Mas calma, ainda não temos essas possibilidades. Como eu disse, ainda estamos considerando apenas os 9 primeiros dígitos. Para compilarmos de fato uma lista de mil possibilidades de CPFs, precisamos calcular os dígitos verificadores para cada combinação possível dos 9 primeiros dígitos. Para isso, precisamos entender o algoritmo de cálculo desses dígitos verificadores, conhecido como "módulo 11".

O algoritmo não é muito complicado. Considerando a primeira possibilidade de combinação de 9 dígitos do nosso exemplo, 000.123.456, fazemos o seguinte para calcular o primeiro dígito verificador:

  • Entendemos os 9 dígitos como uma lista: [0, 0, 0, 1, 2, 3, 4, 5, 6];
  • Invertemos a lista: [6, 5, 4, 3, 2, 1, 0, 0, 0];
  • Multiplicamos cada elemento da lista pela posição do elemento + 1: [6 x 2, 5 x 3, 4 x 4, 3 x 5, 2 x 6, 1 x 7, 0 x 8, 0 x 9, 0 x 10] => [12, 15, 16, 15, 12, 7, 0, 0, 0];
  • Somamos todos os elementos da lista: 12 + 15 + 16 + 15 + 12 + 7 + 0 + 0 + 0 = 77;
  • Calculamos o resto da divisão desse número por 11: 77 / 11 = 7 com resto = 0;
  • Se o resto for maior que 2, o dígito verificador é 11 - resto. Caso contrário, é 0. Nesse caso: resto = 0; 0 < 2 ∴ verificador = 0.

Sendo assim, agora sabemos os 10 primeiros dígitos do CPF: 000.123.456-0. Para calcularmos o segundo dígito verificador e chegarmos em um CPF válido, basta seguir o mesmo algoritmo, mas agora considerando todos os 10 primeiros dígitos:

  • Entendemos os 10 dígitos como uma lista: [0, 0, 0, 1, 2, 3, 4, 5, 6, 0];
  • Invertemos a lista: [0, 6, 5, 4, 3, 2, 1, 0, 0, 0];
  • Multiplicamos cada elemento da lista pela posição do elemento + 1: [0 x 2, 6 x 3, 5 x 4, 4 x 5, 3 x 6, 2 x 7, 1 x 8, 0 x 9, 0 x 10, 0 x 11] => [0, 18, 20, 20, 18, 14, 8, 0, 0, 0];
  • Somamos todos os elementos da lista: 0 + 18 + 20 + 20 + 18 + 14 + 8 + 0 + 0 + 0 = 98;
  • Calculamos o resto da divisão desse número por 11: 98 / 11 = 8 com resto = 10;
  • Se o resto for maior que 2, o dígito verificador é 11 - resto. Caso contrário, é 0. Nesse caso: resto = 10; 10 > 2 ∴ verificador = 11 - 10 = 1.

Agora sim, temos o CPF completo, calculado com base nos 9 primeiros dígitos: 000.123.456-01. Você pode até usar um validador de CPF para confirmar que o resultado corresponde a um CPF válido.

Entendendo isso, basta aplicarmos esse algoritmo para as mil combinações de 9 dígitos possíveis para compilarmos uma lista definitiva dos mil CPFs possíveis do destinatário do nosso Pix. Ou seja:

  • Para os dígitos 000.123.456, o CPF válido é 000.123.456-01;
  • Para os dígitos 001.123.456, o CPF válido é 001.123.456-38;
  • Para os dígitos 002.123.456, o CPF válido é 002.123.456-64;
  • ...
  • Para os dígitos 999.123.456, o CPF válido é 999.123.456-06

Apenas para termos uma prova de conceito, deixo aqui o código Python para calcularmos todas as mil possibilidades de CPF a partir dos 6 dígitos do meio expostos na transação Pix:

from itertools import product
import string

def calcula_digito_verificador(cpf):
    # calcula_digito_verificador("000123456") --> 0
    # calcula_digito_verificador("0001234560") --> 1
    soma = sum(posicao * int(digito) for posicao, digito in enumerate(cpf[::-1], start=2))
    resto = soma % 11
    if resto < 2:
        return 0
    return 11 - resto

def calcula_cpf_completo(cpf):
    # calcula_cpf_completo("000123456") --> "00012345601"
    for n in range(2):
        cpf += str(calcula_digito_verificador(cpf))
    return cpf

def calcula_possibilidades_do_cpf(seis_digitos_do_meio):
    # calcula_possibilidades_do_cpf("123456") --> ["00012345601", "00112345638", "00212345664", ..., "99912345606"]
    cpfs_possiveis = []
    for nove_primeiros_digitos in product(*[string.digits] * 3, *seis_digitos_do_meio):
        cpfs_possiveis.append(calcula_cpf_completo("".join(nove_primeiros_digitos)))
    return cpfs_possiveis

Ok, compilamos uma lista de mil CPFs válidos que podem corresponder ao CPF cujos seis dígitos do meio tivemos acesso através da transação Pix. Mas como podemos descobrir qual desses CPFs é o correto? Considerando que já temos o nome completo do destinatário da transação, podemos até fazer manualmente, se tivermos paciência - testando cada CPF no Pix até que o nome exibido corresponda ao nome que já sabemos ser da pessoa. Claro, isso pode demorar bastante.

Podemos, portanto, pensar em automatizar isso. Uma forma é através da própria API do Pix, mas ela traz dois problemas. O primeiro diz respeito à API, que possui mecanismos que tentam impedir raspagem de dados desse tipo, limitando o número de consultas por tempo. O segundo problema está na abordagem através do Pix: só vamos conseguir descobrir qual CPF é da pessoa se ela tiver cadastrado o CPF como chave Pix. Isso, é claro, não é uma garantia.

A segunda forma que pensei foi através do sistema de consulta de comprovante de situação cadastral de CPF da Receita Federal. Através dela, não precisaríamos torcer para que a pessoa tenha o CPF cadastrado como chave Pix, o que já é uma vantagem. Entretanto, mais dois outros problemas aparecem nesse método. O primeiro é que a plataforma da Receita Federal utiliza um Captcha para limitar as consultas, o que dificultaria uma automatização. O segundo é que a consulta exige também a data de nascimento da pessoa, dado que não temos acesso.

Seguindo a linha de consulta de situação cadastral na Receita Federal, encontrei a solução em sites não-oficiais que disponibilizam esse tipo de consulta sem exigir data de nascimento nem captcha, como o situacao-cadastral.com [archive .org / archive.is]. Basta digitarmos um número de CPF no formulário que o site retorna o nome correspondente ao CPF, e a situação cadastral dele na Receita Federal.

Automatizando essa checagem com um software de simulação de navegação web como o Selenium e uma biblioteca de extração de dados de HTML como o Beautiful Soup, em poucas horas conseguimos checar todas as mil possibilidades de CPFs e descobrir qual deles corresponde ao nome que tivemos acesso ao realizar a transação Pix. Novamente, para termos uma prova de conceito, segue aqui o código Python capaz de realizar isso:

from bs4 import BeautifulSoup
from selenium.common.exceptions import TimeoutException
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


def salva_dados_em_arquivo(nome_do_arquivo, linha):
    with open(nome_do_arquivo, "a") as arquivo_aberto:
        arquivo_aberto.write(f"{linha}\n")


navegador = Chrome()
for cpf in cpfs_possiveis:
    navegador.get("https://www.situacao-cadastral.com")

    cpf_input = WebDriverWait(navegador, 10).until(
        EC.visibility_of_element_located((By.ID, "doc"))
    )
    cpf_input.send_keys(cpf)
    cpf_input.submit()

    try:
        WebDriverWait(navegador, 20).until(
            EC.visibility_of_element_located((By.ID, "resultado"))
        )
    except TimeoutException:
        continue

    linha = cpf
    soup = BeautifulSoup(navegador.page_source)
    nome_span = soup.find("span", {"class": "nome"})
    if nome_span is not None:
        linha += f" - {nome_span.text}"
    situacao_span = soup.find("span", {"class": "situacao"})
    if situacao_span is not None:
        linha += f" - {situacao_span.text}"
    texto_span = soup.find("span", {"class": "texto"})
    if texto_span is not None:
        linha += f" - {texto_span.text}"

    salva_dados_em_arquivo("cpfs.log", linha)

navegador.quit()

Executando esse script, em relativamente pouco tempo teremos o arquivo cpfs.log repletos de CPFs e nomes correspondentes a cada um desses CPFs. Agora, basta procurar pelo nome que já sabemos e, finalmente, descobrimos o CPF do destinatário do nosso Pix. Tudo isso através de uma chave aleatória.

E agora?

O intuito desse post não é de forma alguma desestimular o uso do Pix como método de transferência monetária. Ele continua sendo, afinal, mais prático e seguro que os outros métodos convencionais.

Compartilho as informações aqui presentes porque, de verdade, as descobri quase que por acaso e considerei ter bastante relevância. Eu acreditava que o uso de chaves aleatórias no Pix era justamente uma forma efetiva de não permitir que descobrissem nossos CPFs pelo sistema de pagamentos, e tenho certeza que vários leitores aqui pensavam da mesma forma. Pudemos ver, entretanto, que esse não é o caso - podemos facilmente descobrir um CPF através de qualquer chave Pix.

Novamente, isso não significa que o Pix não é seguro, apenas que a garantia de privacidade que o Banco Central ou uma das milhares de matérias sobre chaves Pix possa ter dado a entender é mentira.

Por fim, agradeço a você leitor por ter chegado até o fim desse post (depois de tanto tempo sem atualizações no blog). Se alguma parte ficou confusa, seja pela linguagem ou pela matemática, pode entrar em contato comigo. Também já deixo registrado que estou disposto a provar que esse método funciona caso algum jornalista ou autoridade se interesse - basta me passar sua chave PIX que eu te mando, no privado, seu CPF.