cacovsky

atualizado: php, curl, cookies, sessions, cpf e site da receita federal

Edit: este código provavelmente não funciona mais, mas o princípio por trás dele deve funcionar sim. então, sinta-se à vontade para discutir nos comentários, mas não tenho planos de atualizá-lo tão cedo.

não é nem necessário informar mas, antes de qualquer coisa, vou dizer: aqui você não vai encontrar nada a respeito de cpfs falsos, burlagem de captcha, captura automática de cpfs ou quaisquer outras práticas consideradas anti-éticas ou mesmo ilegais. o propósito é puramente de estudo. se alguma informação aqui estiver em desacordo com as leis, peço que me informe e o texto será modificado/removido. entretanto, observando os termos de utilização do site da receita federal, não foi encontrado nada que estivesse em desacordo com o que foi exposto aqui.

update (23/11/2011): se você quer somente baixar os fontes, vai aqui:
http://cacovsky.googlecode.com/files/receita_atualizado.zip

esses dias estive brincando com o php-curl, uma extensão do php que permite fazer requisições http, https e ftp, suportando proxies, cookies e autenticação por usuário e senha. essa extensão do php é derivada de um programa em linha de comando chamado curl. o php-curl (assim como o curl) oferece muitos recursos mas, obviamente, abordarei aqui apenas os que me interessa; a referência completa você vai encontrar aqui, é tudo muito bem documentado.

the answer lies within (the source code!)

do outro lado da idéia, o site da receita federal fornece um serviço público de consulta de cpf; com esse serviço, é possível obter informações sobre a regularidade do documento, bem como uma assinatura eletrônica, nome completo, essas coisas. no rodapé do site tem informando que, para utilização correta, é necessário ter habilitada a gravação de cookies.

hum…

pode ser uma necessidade de alguns criar cadastros vinculados aos cpfs com algum grau de confiabilidade. entretanto, de forma gratuita na internet eu só encontrei funções que calculam a validade do cpf, através de dígitos verificadores. basicamente, é feito um cálculo matemático que assegura que os números digitados fazem sentido entre si. no caso do cpf, um cálculo sobre os 9 primeiros dígitos determinam quais seriam os dois dígitos finais. assim, se tiver algum número errado, é fácil verificar que tá errado, pois os dígitos verificadores não vão conferir.

assim, se uma pessoa informa um cpf num cadastro, é difícil para você saber se aquele cpf pertence realmente àquele que se diz dono dele. eu mesmo não encontrei (ainda bem) nenhum site em que você informe o nome e obtenha o cpf (ou vice-versa). já ouvi dizer de sites com webservices ou formulários que fornecem esse serviço de busca, mas cobrando por isso; entretanto, procurando rapidamente, não encontrei nenhum.

então, imaginei que fosse possível utilizar o php-curl para 1) acessar o site da receita para exibir na sua própria página o captcha que é exibido para confirmação no site da receita; 2) fazer com que o usuário digite o cpf e o captcha corretamente; 3) devolver para o site da receita os dados informados pelo usuário e 4) recuperar o nome do indivíduo. tudo isso de forma transparente para o usuário. vou mostrar a seguir como fiz isso.

como funciona a consulta pública do site da receita

primeiro e mais importante, o captcha é uma imagem gerada dinamicamente, com o objetivo de evitar requisições automáticas. o endereço da imagem é gerado na hora em que você o acessa, através de uma função javascript semelhante à seguinte:

function Image()
	{
	    var date = new Date();    	    document.write('<img id="imgCaptcha" border="0" alt="Imagem com os caracteres anti robô" src="http://www.receita.fazenda.gov.br/scripts/srf/intercepta/captcha.aspx?opt=image&v=' + date.getTime() + '" />')
	}

observe o parâmetro v (que recupera o valor de date.getTime()); eu não sei pra que ele serve. omitindo-o, não percebi diferenças, se descobrir, me avise 🙂 . entretanto, eu optei por mater ele lá. o colega thiago ornellas informou que esse macete serve para que o navegador, a cada acesso que se faça na página, reconheça que se trata de uma nova imagem e, assim, realize forçosamente a atualização da imagem. (em php,  é possível definir a validade da imagem através da função header(), para prevenir caching; veja mais aqui)

investigando o formulário do site da receita, foi fácil perceber três informações do formulário que são enviadas via post;

  1. um campo de texto de nome “txtCPF”, com o cpf;
  2. um campo de texto de nome “idLetra”, com os dados do captcha;
  3. um botão do tipo submit, de nome “Enviar” e com valor “Consultar”;

cookie do site da receita federal; o valor foi omitido por privacidade

esse cookie é definido na hora em que você, no seu navegador, acessa o site da receita.

exibindo o captcha da receita através do seu site

pense comigo: se você simplesmente exibisse a imagem diretamente no navegador do seu usuário, de quem seria o cookie definido pelo site da receita? seria do seu usuário! para que seu site, através do php-curl, pudesse recuperar o resultado após o usuário digitar as informações do captcha e do cpf, seria necessário que seu site tivesse os cookies para isso, right?

então temos nosso primeiro ponto: seu site php tem que ter os cookies guardados. o local recomendado para isso é a pasta temporária do sistema. em php, você pode obter essa pasta com a função sys_get_temp_dir() e pode criar um arquivo de nome único com a função tempnam(); seguindo recomendações dessa página, vi que uma forma recomendada para obter a pasta temporária é via “realpath(sys_get_temp_dir())”. é importante salientar que você precisa utilizar um arquivo temporário de nome único, uma vez que vários usuários poderão estar acessando o seu site ao mesmo tempo; então, para cada usuário, um cookie.

bom, então devemos utilizar a pasta temporária para armazenar os cookies. entretanto, observe que existem, pelo menos, duas situações em que deveremos “saber” o nome do nosso arquivo de cookie: 1) quando exibirmos o formulário com captcha recuperado da receita (nosso cookie deve estar guardadinho!) e; 2) quando formos consultar os dados com o cpf e o captcha informados pelo usuário. se seu cenário não for esse, é algo parecido a isso (mesmo que seja via ajax, etc, você ainda vai ter que saber a mesma informação em dois momentos distintos), para isso utilizaremos sessions.

está começando a complicar, mas vamos já acabar com isso! até agora, teremos a geração de um cookie por usuário que acessar a página e armazenaremos o nome desse cookie em uma variável de sessão. um último comentário: em sessão, vamos armazenar apenas o nome do arquivo. se armazenássemos o endereço completo do arquivo, um usuário experiente teria acesso a informações do seu sistema de arquivos, e isso pode representar uma falha de segurança; nós, server-side, já sabemos que é na pasta temporária.
segue como eu fiz isso, no início do script formulario.php:

<?php
	session_start();

	$dir_sep = DIRECTORY_SEPARATOR;

	$cookie_file = tempnam (realpath(sys_get_temp_dir()), "CURLCOOKIE");
	$_SESSION['cookie_file'] = substr($cookie_file, strrpos (tempnam (realpath(sys_get_temp_dir()), "CURLCOOKIE"), '/'));
?>
<html>
<head>
    <title>cpf hack</title>
</head>
...

seguindo adiante, as coisas ficam mais fáceis. apenas vamos pegar a imagem do site da receita, armazenar numa pasta local do servidor e exibi-la na página do usuário. para isso, é imprescindível ter permissão de escrita numa subpasta “images”:

...
<head>
 <title>cpf hack</title>
</head>
 <body>
 <?php

 $my_site = "http://localhost/receita/";

 //parte 1: acessar o site da receita e obter o cookie do captcha apropriadamente

 $url_imagem = "http://www.receita.fazenda.gov.br/scripts/srf/intercepta/captcha.aspx?opt=image&v=".time();
 $ch = curl_init ();
 curl_setopt ($ch, CURLOPT_URL, $url_imagem);
 curl_setopt ($ch, CURLOPT_COOKIEJAR, $cookie_file);
 curl_setopt ($ch, CURLOPT_RETURNTRANSFER, true);
 $output = curl_exec ($ch); // the real thing

 $img_file_name = tempnam ("images/", "CURLGIF_");
 $img_data = imagecreatefromstring($output);
 imagegif($img_data, $img_file_name);

 $img_link = $my_site."images/".substr( $img_file_name, strrpos ($img_file_name , '/'));

 //detectando sistema operacional
 $os = strtoupper(php_uname('s'));
 //eh windows
 if (strpos($os, 'WIN')===TRUE) {
    $img_link = str_replace('\\','/',$img_link);
 }
 ?>

 <form method="post" action="consultar.php">
 CPF: <input type="text" name="txtCPF"/> <br />
 Captcha: <input type="text" name="idLetra"/> <br />
 <img src="<?php echo $img_link ?>" />  <br/>
 <input id="id_submit" name="Enviar" type="submit" value="Consultar"/>
 </form>
 </body>
</html>

veja que aqui eu gerei o link para a imagem, defini algumas opções do curl e fiz a requisição. depois criei a imagem, salvei-a num arquivo com um nome qualquer e depois exibi essa imagem no formulário do meu usuário.

não havia necessidade de se dividir o código php nesses três trechos, foi só pra falar uma parte de cada vez. entretanto, o session_start() tem que vir antes de qualquer informação ser enviada para o navegador do usuário.

voilá!

realizando a consulta no site da receita

depois dessa etapa inicial, essa segunda parte fica bem mais fácil. após o usuário inserir as informações e enviá-las, é só o seu site realizar o post no site da receita utilizando o mesmo cookie (que tá na minha variável de sessão). depois, neste exemplo, é feita uma pesquisa por strings específicas para capturar o nome da pessoa física, além de uma conversão de codificação. segue o código do arquivo consultar.php, conforme modificações sugeridas pelo leonardo felipe:

<?php
	session_start();

	//parte 2: realizar a requisição utilizando o mesmo cookie
	$url_requisicao = "http://www.receita.fazenda.gov.br/aplicacoes/atcta/cpf/ConsultaPublicaExibir.asp";

	$campos = $_POST;
	$campos_concat = '';
	$cookie_file = realpath(sys_get_temp_dir()).$_SESSION['cookie_file'];
	//die($cookie_file);
	$campos_concat = 'txtCPF='.$_POST['txtCPF'].'&idLetra='.$_POST['idLetra']."&Enviar".$_POST['Enviar'];

	$ch = curl_init();
	curl_setopt($ch,CURLOPT_URL,$url_requisicao);
	curl_setopt($ch,CURLOPT_POST, count($_POST));
	curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch,CURLOPT_POSTFIELDS,$campos_concat);
	curl_setopt ($ch, CURLOPT_COOKIEFILE, $cookie_file); //a-ha!
        curl_setopt($ch,CURLOPT_FOLLOWLOCATION, true);  // valeu, felipe!
	$result = curl_exec($ch);
	$result = iconv("ISO-8859-1","UTF-8",$result);

	//texto para localizar o nome
	$texto_antes = 'Nome da Pessoa Física:';
	$texto_depois = '</span>';

	if (!strpos($result, $texto_antes)){
		die('erro na consulta');
	}
	else{
		$result = substr($result,  strpos($result, $texto_antes));
		$result = substr($result, strlen($texto_antes) , strpos($result, $texto_depois));
		$result = str_replace ( $texto_depois , '' , $result);
		$result = trim($result);

		//close connection
		curl_close($ch);
		echo "<pre>";
		echo $result;
		echo "</pre>";
	}

?>

espero que tenham gostado! sugestões, críticas e comentários são muito bem-vindos!

algumas outras referências consultadas:

execute post with curl
php cookies example

update: se vocês estão atrás de um proxy, em ambas as utilizações do curl, vocês devem definir, além das opções anteriores, as seguintes opções:

        curl_setopt ($ch, CURLOPT_PROXY, "http://ip_do_servidor_proxy");
        curl_setopt ($ch, CURLOPT_PROXYPORT, porta_do_servidor_proxy);

update 2: para baixar os fontes, vai aqui:
http://cacovsky.googlecode.com/files/receita.zip

update 3: o código não estava funcionando no windows, mas já foi corrigido. era um problema com o separador de diretório / no linux e \ no windows.