Resposta tardia a uma dúvida do Khyser [RESOLVIDO]

1. Resposta tardia a uma dúvida do Khyser [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/03/2018 - 10:03h

Prezados,

Alguns dias atrás, o usuário Khyser postou um tópico em que fazia algumas perguntas relacionadas a representação de dados de usuários num SGBD, e um dos itens sobre os quais perguntava era a respeito da validação de endereços de e-mail.

EDIT (12:14): Eis o texto original dele, de acordo com o cache do Google, referido abaixo.

Enviado em 17/03/2018 - 15:57h

Criei um sistema de login em C usando como banco de dados o MySQL e gostaria saber se ele ficou bom, ruim ou se precisa ser melhorado em certos pontos. Aqui vai o link do projeto: https://github.com/Khyser/Login-System/tree/master/Project

Alguns problemas com esse sistema que não sei como resolver:

1 - Um amigo meu disse que meu código estava vulnerável e poderia ser alvo de SQL Injection, mas eu não entendo muito disso então deixei sem nenhuma proteção para esse problema, então caso vocês queiram fazer aguma sugestões saibam que ela será bem vinda :D

2 - Não consegui "bolar" uma função que validasse o e-mail digitado, ou seja, se era Gmail, Yahoo, Hotmail ou ProtonMail (na verdade tinha criado uma, porém era uma verdadeira gambiarra cheia de loops que não funcionava corretamente).

3 - Se minha senha for Lixo_toxico123 e eu digitar lixo_toxico123 eu faço login normalmente.


Com relação a essa pergunta específica (2), eu dei uma resposta inicial, indicando uma referência que ele poderia tomar como ponto de partida para montar uma solução, e, depois de enviar tal resposta, comecei a escrever outra mensagem dando um exemplo de implementação. Infelizmente, porém, a máquina onde eu comecei a escrever tal resposta (um Raspberry Pi 3 que eu estava testando) travou por superaquecimento, e eu perdi o que já tinha começado a escrever. Depois disso, eu comecei a escrever a resposta de novo no trabalho, mas, no meio das atividades do dia-a-dia, não consegui terminá-la até a manhã de hoje. Quando finalmente a concluí e tentei enviá-la, recebi um erro, e depois vi que o tópico tinha sido apagado e que ele removera a conta no VoL.

Deixo abaixo, então, a resposta mais elaborada, que pode ser útil para outrem, juntamente com um link para a pergunta original no cache do Google (https://webcache.googleusercontent.com/search?q=cache:GETcDLh8ILUJ:https://www.vivaolinux.com.br/top...), lamentando que nosso colega tenha deixado o fórum.

Refinando um pouco minha resposta anterior, perece-me que você está menos interessado num formato de como um endereço de e-mail pode aparecer no corpo de uma mensagem, e mais na forma como ele é efetivamente associado a um usuário dentro de um determinado domínio.

Olhando a gramática da RFC 5322, isso implicaria restringir:

  • address ao caso em que ele é uma mailbox (descartando o caso em que ela poderia indicar um grupo, em vez de uma única mailbox);
  • mailbox ao caso em que ela é uma addr-spec (descartando o caso em que você tem um texto (quase) livre de identificação do endereço);
  • local-part ao caso em que ela é composta apenas por dot-atom (descartando os casos de quoted-string e de sintaxes obsoletas);
  • domain ao caso em que ele é composto apenas por dot-atom (descantando os casos de domain-literal e de sintaxes obsoletas);
  • dot-atom ao caso em que não ocorram os opcionais CFWSs, o que na prática o torna idêntico a dot-atom-text.

Uma forma de implementar essa gramática é por meio de expressões regulares, com os elementos mais genéricos se fazendo pela composição dos elementos mais básicos.

Abaixo segue um esqueleto (não-testado) de implementação da parte que nos interessa da gramática, por meio de composição de strings (usando o preprocessador) e de uma descrição bottom-up dos elementos que formam a gramática.

#define DIGIT "0123456789"
#define LOWER "abcdefghijklmnopqrstuvwxyz"
#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define ALPHA UPPER LOWER

#define ATEXT ALPHA DIGIT "!#$%&'*+/=?^_`{|}~-" // Note que o hífen está no final da lista.

#define DOT_ATOM_TEXT "[" ATEXT "]+(?:\\.[" ATEXT "]+)?" // "(?: ... )" agrupa pedaços sem criar back-references.
// "[...]" é uma forma sucinta de informar conjunto de caracteres válidos.
#define DOT_ATOM "(?:" DOT_ATOM_TEXT ")"

#define LOCAL_PART DOT_ATOM
#define DOMAIN_ DOT_ATOM

#define ADDR_SPEC "(" LOCAL_PART ")@(" DOMAIN_ ")" // Aqui eu uso agrupamento com back-references, para poder ter a opção
// de testar os valores de nome e domínio (validade, não apenas sintaxe).

#define MAILBOX "^(?:" ADDR_SPEC ")$"

#define ADDRESS "(?:" MAILBOX ")"


#include <regex>
#include <string>

#ifdef VALIDATE_DOMAIN
#include <dns.h> // Parte do pacote libowfat-dev.
#include <stralloc.h> // Parte do pacote libowfat-dev.
#endif

#ifdef VALIDATE_LOCAL_PART
#include <set>
extern std::set<std::regex> rejected_username_res;
#endif


using namespace std;


const regex address_re(ADDRESS);


// Tipo que indica o motivo de um endereço ser eventualmente inválido.
enum class invalid_addr_reason { is_valid, format, local_part, domain };


// Função que testa se um endereço de e-mail é válido ou não. Se for, a
// função retorna true; caso contrário, retorna false e, se o segundo parâmetro
// tiver sido usado com um argumento não-nulo, coloca na região apontada por
// ele um valor indicando a causa do erro.
bool is_valid_address(const string &addr, invalid_addr_reason *p_reason=nullptr){
smatch parts;

if(!regex_match(addr, parts, address_re)){
if(p_reason)
*p_reason=invalid_addr_reason::format;
return false;
}

// A sintaxe está OK. Agora examina se o domínio existe e se o nome do
// usuário remoto é suspeito.

#ifdef VALIDATE_DOMAIN
// Para o domínio ser válido, deve haver no DNS um registro do tipo MX ou
// um registro do tipo A (IPv4) ou AAAA (IPv6).
static stralloc domain={0}, response={0};

stralloc_copys(&domain, parts[2].str().c_str());
if(
(dns_mx(&response, &domain)!=0 || response.len==0) &&
(dns_ip4(&response, &domain)!=0 || response.len==0) &&
(dns_ip6(&response, &domain)!=0 || response.len==0)
){
if(p_reason)
*p_reason=invalid_addr_reason::domain;
return false;
}
#endif

#ifdef VALIDATE_LOCAL_PART
// Exemplo de tratamento: rejected_username_res é um conjunto de expressões
// regulares contendo nomes que não devam ser aceitos como parte do endere-
// ço. Esse conjunto poderia conter expressões regulares para impedir o
// uso de nomes reservados, tais como "postmaster", "mailerdaemon", "root",
// etc., palavrões (e.g. ".*\b(?:lista|de|palavrões|aqui)\b.*") e outros
// nomes embaraçosos. Alternativamente, algum outro critério ou algoritmo
// poderia ser usado.
const string local_part=parts[1];
for(const auto &invalid_name_re: rejected_username_res)
if(regex_match(local_part, invalid_name_re)){
if(p_reason)
*p_reason=invalid_addr_reason::local_part;
return false;
}
#endif

// Se chegou a este ponto, não achou falha no endereço. Assim sendo,
// consideramo-lo válido.
if(p_reason)
*p_reason=invalid_addr_reason::is_valid;
return true;
}



  


2. Re: Resposta tardia a uma dúvida do Khyser [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/03/2018 - 11:30h

Segue uma outra versão do mesmo código, que depende menos de macros (uma delas, inclusive, teve de usar o horrível nome “DOMAIN_”, porque DOMAIN já está em uso em sistemas que mantenham compatibilidade com o UNIX System V, o que inclui o Linux).

Esta versão, no fim das contas, gasta mais recursos, porque cada valor intermediário da gramática fica numa string separada, e cada string dessas é inicializada em tempo de execução (ao contrario de macros, as quais existem apenas durante a compilação, sendo que as que não forem explicitamente usadas não são embutidas no código final). Mesmo assim, por ser mais segura, talvez esta versão seja mais adequada ao uso.

#include <regex>
#include <set>
#include <string>


using namespace std;


namespace rfc5322_subset {

const string digit{"0123456789"};
const string lower{"abcdefghijklmnopqrstuvwxyz"};
const string upper{"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
const string alpha{upper+lower};

const string atext{alpha+digit+"!#$%&'*+/=?^_`{|}~-"}; // Note que o hífen está no final da lista.

const string dot_atom_text{"["+atext+"]+(?:\\.["+atext+"]+)?"}; // "(?: ... )" agrupa pedaços sem
// criar back-references.
// "[...]" é uma forma sucinta
// de informar conjunto de
// caracteres válidos.
const string dot_atom{"(?:"+dot_atom_text+")"};

const string local_part{dot_atom};
const string domain{dot_atom};

const string addr_spec{"("+local_part+")@("+domain+")"}; // Aqui eu uso agrupamento com
// back-references, para poder ter
// a opção de testar os valores de
// nome e domínio (validade, e
// não apenas sintaxe).

const string mailbox{"^(?:"+addr_spec+")$"};

const string address{"(?:"+mailbox+")"};

}


#ifdef VALIDATE_DOMAIN
#include <dns.h> // Parte do pacote libowfat-dev.
#include <stralloc.h> // Parte do pacote libowfat-dev.
#endif

#ifdef VALIDATE_LOCAL_PART
extern std::set<std::regex> rejected_username_res;
#endif


const regex address_re(rfc5322_subset::address);


// Tipo que indica o motivo de um endereço ser eventualmente inválido.
enum class invalid_addr_reason { is_valid, format, local_part, domain };


// Função que testa se um endereço de e-mail é válido ou não. Se for, a
// função retorna true; caso contrário, retorna false e, se o segundo parâmetro
// tiver sido usado com um argumento não-nulo, coloca na região apontada por
// ele um valor indicando a causa do erro.
bool is_valid_address(const string &addr, invalid_addr_reason *p_reason=nullptr){
smatch parts;

if(!regex_match(addr, parts, address_re)){
if(p_reason)
*p_reason=invalid_addr_reason::format;
return false;
}

// A sintaxe está OK. Agora examina se o domínio existe e se o nome do
// usuário remoto é suspeito.

#ifdef VALIDATE_DOMAIN
// Para o domínio ser válido, deve haver no DNS um registro do tipo MX ou
// um registro do tipo A (IPv4) ou AAAA (IPv6).
static stralloc domain={0}, response={0};

stralloc_copys(&domain, parts[2].str().c_str());
if(
(dns_mx(&response, &domain)!=0 || response.len==0) &&
(dns_ip4(&response, &domain)!=0 || response.len==0) &&
(dns_ip6(&response, &domain)!=0 || response.len==0)
){
if(p_reason)
*p_reason=invalid_addr_reason::domain;
return false;
}
#endif

#ifdef VALIDATE_LOCAL_PART
// Exemplo de tratamento: rejected_username_res é um conjunto de expressões
// regulares contendo nomes que não devam ser aceitos como parte do endere-
// ço. Esse conjunto poderia conter expressões regulares para impedir o
// uso de nomes reservados, tais como "postmaster", "mailerdaemon", "root",
// etc., palavrões (e.g. ".*\b(?:lista|de|palavrões|aqui)\b.*") e outros
// nomes embaraçosos. Alternativamente, algum outro critério ou algoritmo
// poderia ser usado.
const string local_part=parts[1];
for(const auto &invalid_name_re: rejected_username_res)
if(regex_match(local_part, invalid_name_re)){
if(p_reason)
*p_reason=invalid_addr_reason::local_part;
return false;
}
#endif

// Se chegou a este ponto, não achou falha no endereço. Assim sendo,
// consideramo-lo válido.
if(p_reason)
*p_reason=invalid_addr_reason::is_valid;
return true;
}



3. Re: Resposta tardia a uma dúvida do Khyser [RESOLVIDO]

Dravo
Dravo

(usa Gentoo)

Enviado em 20/03/2018 - 11:31h

Tenta linkar seu tópico ao tópico da duvida dele.


4. Re: Resposta tardia a uma dúvida do Khyser [RESOLVIDO]

Uilian Ries
uilianries

(usa Linux Mint)

Enviado em 20/03/2018 - 12:02h

Como sempre, uma verdadeira aula.


5. Re: Resposta tardia a uma dúvida do Khyser [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 20/03/2018 - 12:10h

Dravo escreveu:

Tenta linkar seu tópico ao tópico da duvida dele.


Como eu disse, ele apagou o tópico antes de remover a conta. O que eu fiz foi colocar um link para o cache do Google, contendo a postagem original.

Eu já sugeri ao VoL que os moderadores das comunidades possam restaurar tópicos, mesmo que sem uma ligação ao autor original. Infelizmente, porém, nunca recebi resposta. E eu — e possivelmente outros membros ativos da comunidade — já perdi material relativamente bem elaborado que tinha criado como resposta a uma pergunta porque o autor da pergunta resolveu apagá-la.

Um tanto frustrante que alguém, ao resolver se censurar ou esconder, acabe me censurando também.

De todo modo, eu editei a postagem para incluir a pergunta original como citação, além de manter o link para o cache do Google.


6. Re: Resposta tardia a uma dúvida do Khyser [RESOLVIDO]

Dravo
Dravo

(usa Gentoo)

Enviado em 20/03/2018 - 12:21h

paulo1205 escreveu:

Dravo escreveu:

Tenta linkar seu tópico ao tópico da duvida dele.


Como eu disse, ele apagou o tópico antes de remover a conta. O que eu fiz foi colocar um link para o cache do Google, contendo a postagem original.

Eu já sugeri ao VoL que os moderadores das comunidades possam restaurar tópicos, mesmo que sem uma ligação ao autor original. Infelizmente, porém, nunca recebi resposta. E eu — e possivelmente outros membros ativos da comunidade — já perdi material relativamente bem elaborado que tinha criado como resposta a uma pergunta porque o autor da pergunta resolveu apagá-la.

Um tanto frustrante que alguém, ao resolver se censurar, acabe me censurando também.

De todo modo, eu editei a postagem para incluir a pergunta original como citação, além de manter o link para o cache do Google.


Que triste, não sabia que ele tinha deletado a conta. Estou começando a notar que muitos usuários estão deletando suas contas sem mais nem menos. Alguns deletaram mais voltaram como o caso do Cabreuvas.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts