Quais são as razões técnicas pelas quais não se deve usar mysql_*
funções? (por exemplo mysql_query()
, mysql_connect()
ou mysql_real_escape_string()
)?
Por que devo usar outra coisa, mesmo que ela funcione no meu site?
Se eles não funcionam no meu site, por que recebo erros como
Aviso: mysql_connect (): Esse arquivo ou diretório não existe
Respostas:
A extensão do MySQL:
Como está obsoleto, usá-lo torna seu código menos prova de futuro.
A falta de suporte para instruções preparadas é particularmente importante, pois elas fornecem um método mais claro e menos propenso a erros de escapar e citar dados externos do que escapar manualmente com uma chamada de função separada.
Veja a comparação de extensões SQL .
fonte
O PHP oferece três APIs diferentes para se conectar ao MySQL. Estes são os
mysql
(removidos a partir do PHP 7)mysqli
e asPDO
extensões.As
mysql_*
funções costumavam ser muito populares, mas seu uso não é mais incentivado. A equipe de documentação está discutindo a situação de segurança do banco de dados e educar os usuários a se afastarem da extensão ext / mysql comumente usada faz parte disso (verifique em php.internals: deprecating ext / mysql ).E a equipe de desenvolvedores PHP mais tarde tomou a decisão para gerar
E_DEPRECATED
erros quando os usuários se conectar ao MySQL, seja por meio demysql_connect()
,mysql_pconnect()
ou a funcionalidade de conexão implícita incorporadoext/mysql
.ext/mysql
foi oficialmente descontinuado a partir do PHP 5.5 e foi removido a partir do PHP 7 .Veja a caixa vermelha?
Quando você acessa qualquer
mysql_*
página do manual de funções, vê uma caixa vermelha explicando que não deve mais ser usada.Por quê
Afastar-
ext/mysql
se não é apenas segurança, mas também acesso a todos os recursos do banco de dados MySQL.ext/mysql
foi desenvolvido para o MySQL 3.23 e só recebeu muito poucas adições desde então, mantendo principalmente a compatibilidade com esta versão antiga, o que dificulta a manutenção do código. Recursos ausentes que não são suportados porext/mysql
incluem: ( do manual do PHP ).Razão para não usar a
mysql_*
função :Ponto acima citado na resposta de Quentin
A falta de suporte para instruções preparadas é particularmente importante, pois elas fornecem um método mais claro e menos propenso a erros de escapar e citar dados externos do que escapar manualmente com uma chamada de função separada.
Veja a comparação de extensões SQL .
Suprimindo avisos de descontinuação
Enquanto o código está sendo convertido para
MySQLi
/PDO
, osE_DEPRECATED
erros podem ser suprimidos configurando-seerror_reporting
no php.ini para excluirE_DEPRECATED:
Observe que isso também ocultará outros avisos de descontinuação , que, no entanto, podem ser para outras coisas que não o MySQL. ( do manual do PHP )
O artigo DOP vs. MySQLi: Qual você deve usar? por Dejan Marjanovic irá ajudá-lo a escolher.
E uma maneira melhor é
PDO
, e agora estou escrevendo umPDO
tutorial simples .Um tutorial DOP simples e curto
P. A primeira pergunta em minha mente foi: o que é DOP?
R. “O PDO - PHP Data Objects - é uma camada de acesso ao banco de dados que fornece um método uniforme de acesso a vários bancos de dados.”
Conectando ao MySQL
Com
mysql_*
function ou podemos dizer da maneira antiga (descontinuado no PHP 5.5 e superior)Com
PDO
: Tudo que você precisa fazer é criar um novoPDO
objeto. O construtor aceita parâmetros para especificar oPDO
construtor da fonte de banco de dados geralmente usa quatro parâmetros que sãoDSN
(nome da fonte de dados) eusername
, opcionalmentepassword
,.Aqui eu acho que você está familiarizado com tudo, exceto
DSN
; isso é novo noPDO
. ADSN
é basicamente uma série de opções que informamPDO
qual driver usar e detalhes da conexão. Para referência adicional, consulte o PDO MySQL DSN .Nota: você também pode usar
charset=UTF-8
, mas às vezes causa um erro, por isso é melhor usá-loutf8
.Se houver algum erro de conexão, ele lançará um
PDOException
objeto que pode ser capturado para ser manuseadoException
ainda mais.Boa leitura : Gerenciamento de conexões e conexões ¶
Você também pode passar várias opções de driver como uma matriz para o quarto parâmetro. Eu recomendo passar o parâmetro que coloca
PDO
no modo de exceção. Como algunsPDO
drivers não suportam instruções preparadas nativas, tambémPDO
executa a emulação da preparação. Ele também permite que você ative manualmente essa emulação. Para usar as instruções preparadas do lado do servidor nativo, você deve defini-lo explicitamentefalse
.O outro é desativar a emulação de preparação, que é ativada no
MySQL
driver por padrão, mas a emulação de preparação deve ser desativada para usoPDO
com segurança.Mais tarde explicarei por que a emulação de preparação deve ser desativada. Para encontrar o motivo, verifique este post .
Só é utilizável se você estiver usando uma versão antiga da
MySQL
qual eu não recomendo.Abaixo está um exemplo de como você pode fazer isso:
Podemos definir atributos após a construção da DOP?
Sim , também podemos definir alguns atributos após a construção do PDO com o
setAttribute
método:Manipulação de erros
O tratamento de erros é muito mais fácil do
PDO
quemysql_*
.Uma prática comum ao usar
mysql_*
é:OR die()
não é uma boa maneira de lidar com o erro, pois não podemos lidar com issodie
. Ele terminará abruptamente o script e ecoará o erro na tela que você normalmente NÃO deseja mostrar aos usuários finais, e permitirá que hackers descubram o seu esquema. Como alternativa, os valores de retorno dasmysql_*
funções geralmente podem ser usados em conjunto com mysql_error () para manipular erros.PDO
oferece uma solução melhor: exceções. Tudo o que fazemos comPDO
deve ser envolto em umtry
-catch
bloco. Podemos forçarPDO
um dos três modos de erro, definindo o atributo do modo de erro. Três modos de tratamento de erros estão abaixo.PDO::ERRMODE_SILENT
. É apenas definir códigos de erro e agir da mesma maneiramysql_*
que você deve verificar cada resultado e depois ver$db->errorInfo();
os detalhes do erro.PDO::ERRMODE_WARNING
LevanteE_WARNING
. (Avisos em tempo de execução (erros não fatais). A execução do script não é interrompida.)PDO::ERRMODE_EXCEPTION
: Lança exceções. Representa um erro gerado pelo PDO. Você não deve lançar umPDOException
do seu próprio código. Veja Exceções para obter mais informações sobre exceções no PHP. Ele age muito parecidoor die(mysql_error());
quando não é pego. Masor die()
, diferentemente , oPDOException
pode ser capturado e manuseado normalmente, se você optar por fazê-lo.Boa leitura :
Gostar:
E você pode envolvê-lo
try
-catch
como abaixo:Você não tem que lidar com
try
-catch
agora. Você pode capturá-lo a qualquer momento apropriado, mas eu recomendo fortemente que você usetry
-catch
. Também pode fazer mais sentido capturá-lo fora da função que chama oPDO
material:Além disso, você pode lidar com
or die()
isso ou podemos dizer o mesmomysql_*
, mas será realmente variado. Você pode ocultar as mensagens de erro perigosas na produção girandodisplay_errors off
e apenas lendo seu log de erros.Agora, depois de ler todas as coisas acima, você provavelmente está pensando: o que diabos é que, quando eu só quero começar inclinando-se simples
SELECT
,INSERT
,UPDATE
, ouDELETE
declarações? Não se preocupe, aqui vamos nós:Selecionando dados
Então, o que você está fazendo
mysql_*
é:Agora
PDO
, você pode fazer o seguinte:Ou
Nota : Se você estiver usando o método como abaixo (
query()
), esse método retornará umPDOStatement
objeto. Então, se você deseja buscar o resultado, use-o como acima.No PDO Data, ele é obtido através do
->fetch()
método de manipulação de instruções. Antes de chamar a busca, a melhor abordagem seria dizer à DOP como você deseja que os dados sejam buscados. Na seção abaixo, estou explicando isso.Modos de busca
Observe o uso de
PDO::FETCH_ASSOC
no códigofetch()
efetchAll()
acima. Isso informaPDO
para retornar as linhas como uma matriz associativa com os nomes dos campos como chaves. Também existem muitos outros modos de busca, os quais explicarei um por um.Antes de tudo, explico como selecionar o modo de busca:
No acima, eu tenho usado
fetch()
. Você também pode usar:PDOStatement::fetchAll()
- Retorna uma matriz que contém todas as linhas do conjunto de resultadosPDOStatement::fetchColumn()
- Retorna uma única coluna da próxima linha de um conjunto de resultadosPDOStatement::fetchObject()
- Busca a próxima linha e a retorna como um objeto.PDOStatement::setFetchMode()
- Defina o modo de busca padrão para esta instruçãoAgora chego ao modo de busca:
PDO::FETCH_ASSOC
: retorna uma matriz indexada pelo nome da coluna, conforme retornado no seu conjunto de resultadosPDO::FETCH_BOTH
(padrão): retorna uma matriz indexada pelo nome da coluna e pelo número da coluna indexada 0, conforme retornado no seu conjunto de resultadosExistem ainda mais opções! Leia sobre todos eles na
PDOStatement
documentação do Fetch. .Obtendo a contagem de linhas :
Em vez de usar
mysql_num_rows
para obter o número de linhas retornadas, você pode obter umPDOStatement
e façarowCount()
, como:Obtendo o último ID inserido
Inserir e atualizar ou excluir instruções
O que estamos fazendo em
mysql_*
função é:E no pdo, a mesma coisa pode ser feita por:
Na consulta acima,
PDO::exec
execute uma instrução SQL e retorne o número de linhas afetadas.Inserir e excluir serão abordados mais tarde.
O método acima é útil apenas quando você não está usando variáveis na consulta. Mas quando você precisar usar uma variável em uma consulta, nunca tente como o descrito acima e existe a instrução preparada ou a instrução parametrizada .
Declarações Preparadas
P. O que é uma declaração preparada e por que preciso deles?
R. Uma instrução preparada é uma instrução SQL pré-compilada que pode ser executada várias vezes enviando apenas os dados para o servidor.
O fluxo de trabalho típico do uso de uma instrução preparada é o seguinte ( citado na Wikipedia em três pontos ):
Preparar : o modelo de instrução é criado pelo aplicativo e enviado ao sistema de gerenciamento de banco de dados (DBMS). Certos valores são deixados não especificados, chamados parâmetros, espaços reservados ou variáveis de ligação (identificadas
?
abaixo):INSERT INTO PRODUCT (name, price) VALUES (?, ?)
O DBMS analisa, compila e executa a otimização de consulta no modelo de instrução e armazena o resultado sem executá-lo.
1.00
para o segundo parâmetro.Você pode usar uma instrução preparada incluindo espaços reservados no seu SQL. Existem basicamente três sem espaços reservados (não tente isso com a variável acima de um), um com espaços reservados sem nome e outro com espaços reservados nomeados.
P. Então, agora, quais são os espaços reservados nomeados e como os uso?
A. Espaços reservados nomeados. Use nomes descritivos precedidos por dois pontos, em vez de pontos de interrogação. Não nos preocupamos com a posição / ordem do valor no nome do local:
bindParam(parameter,variable,data_type,length,driver_options)
Você também pode vincular usando uma matriz de execução:
Outro recurso interessante para os
OOP
amigos é que os espaços reservados nomeados têm a capacidade de inserir objetos diretamente no seu banco de dados, assumindo que as propriedades correspondam aos campos nomeados. Por exemplo:P. Então, agora, o que são espaços reservados sem nome e como os uso?
A. Vamos dar um exemplo:
e
No acima, você pode vê-los em
?
vez de um nome, como em um marcador de lugar. Agora, no primeiro exemplo, atribuímos variáveis aos vários espaços reservados ($stmt->bindValue(1, $name, PDO::PARAM_STR);
). Em seguida, atribuímos valores a esses espaços reservados e executamos a declaração. No segundo exemplo, o primeiro elemento da matriz vai para o primeiro?
e o segundo para o segundo?
.NOTA : Nos espaços reservados não nomeados , devemos cuidar da ordem correta dos elementos na matriz que estamos passando para o
PDOStatement::execute()
método.SELECT
,INSERT
,UPDATE
,DELETE
Preparado consultasSELECT
:INSERT
:DELETE
:UPDATE
:NOTA:
No entanto
PDO
e / ouMySQLi
não são completamente seguros. Verifique a resposta As instruções preparadas pelo DOP são suficientes para impedir a injeção de SQL? por ircmaxell . Além disso, estou citando parte da resposta dele:fonte
IN (...) construct
.function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();
Ele funciona para lançar exceções.Doesn't support non-blocking, asynchronous queries
como um motivo para não usar o mysql_ - você também deve listá-lo como um motivo para não usar o DOP, porque o DOP também não suporta. (mas MySQLi suporta)Primeiro, vamos começar com o comentário padrão que damos a todos:
Vamos passar por isso, sentença por sentença, e explicar:
Eles não são mais mantidos e são oficialmente descontinuados
Isso significa que a comunidade PHP está gradualmente dispensando suporte para essas funções muito antigas. É provável que eles não existam em uma versão futura (recente) do PHP! O uso continuado dessas funções pode quebrar seu código em um futuro não muito distante.
NOVO! - ext / mysql agora está oficialmente obsoleto a partir do PHP 5.5!
Mais novo! ext / mysql foi removido no PHP 7 .
Em vez disso, você deve aprender sobre declarações preparadas
mysql_*
A extensão não suporta instruções preparadas , o que é (entre outras coisas) uma contramedida muito eficaz contra a injeção de SQL . Ele corrigiu uma vulnerabilidade muito séria nos aplicativos dependentes do MySQL, que permite que os invasores tenham acesso ao seu script e realizem qualquer consulta possível no seu banco de dados.Para obter mais informações, consulte Como impedir a injeção de SQL no PHP?
Veja a caixa vermelha?
Quando você acessa qualquer
mysql
página do manual de funções, vê uma caixa vermelha explicando que não deve mais ser usada.Use DOP ou MySQLi
Existem alternativas melhores, mais robustas e bem construídas, o PDO - PHP Database Object , que oferece uma abordagem OOP completa à interação com o banco de dados, e o MySQLi , que é uma melhoria específica do MySQL.
fonte
IN (...) construct
.Fácil de usar
Os motivos analíticos e sintéticos já foram mencionados. Para os novatos, há um incentivo mais significativo para parar de usar as funções mysql_ datadas.
APIs de banco de dados contemporâneas são apenas mais fáceis de usar.
São principalmente os parâmetros vinculados que podem simplificar o código. E com excelentes tutoriais (como visto acima), a transição para o DOP não é excessivamente árdua.
Reescrever uma base de código maior de uma só vez, no entanto, leva tempo. Raison d'être para esta alternativa intermediária:
Pdo_ * equivalente funciona no lugar do
mysql_ *Usando o < pdo_mysql.php > você pode alternar das funções antigas do mysql_ com o mínimo esforço . Ele adiciona
pdo_
wrappers de funções que substituem seusmysql_
equivalentes.Simplesmente em cada script de chamada que precisa interagir com o banco de dados.
include_once(
"pdo_mysql.php"
);
Remova o
prefixo da função em qualquer lugar e substitua-o pormysql_
pdo_
.mysql_
connect()
torna-sepdo_
connect()
mysql_
query()
torna-sepdo_
query()
mysql_
num_rows()
torna-sepdo_
num_rows()
mysql_
insert_id()
torna-sepdo_
insert_id()
mysql_
fetch_array()
torna-sepdo_
fetch_array()
mysql_
fetch_assoc()
torna-sepdo_
fetch_assoc()
mysql_
real_escape_string()
torna-sepdo_
real_escape_string()
Seu código funcionará da mesma forma e ainda terá a mesma aparência:
Et voilà.
Seu código está usando o DOP.
Agora é hora de realmente utilizá- lo.
Parâmetros vinculados podem ser fáceis de usar
Você só precisa de uma API menos pesada.
pdo_query()
adiciona suporte muito fácil a parâmetros vinculados. A conversão de código antigo é simples:Mova suas variáveis para fora da string SQL.
pdo_query()
.?
como espaços reservados onde as variáveis estavam antes.'
aspas simples que incluíam anteriormente valores / variáveis de string.A vantagem se torna mais óbvia para códigos mais longos.
Frequentemente, as variáveis de sequência não são apenas interpoladas para o SQL, mas concatenadas com chamadas de escape no meio.
Com os
?
marcadores aplicados, você não precisa se preocupar com isso:Lembre-se de que pdo_ * ainda permite um ou outro .
Apenas não escape de uma variável e ligue-a na mesma consulta.
:named
listas de marcadores posteriores.Mais importante, você pode passar as variáveis $ _REQUEST [] com segurança para trás de qualquer consulta. Quando os
<form>
campos enviados correspondem exatamente à estrutura do banco de dados, é ainda mais curto:Tanta simplicidade. Mas vamos voltar a mais algumas recomendações de reescrita e razões técnicas sobre por que você pode se livrar
e escapar.mysql_
Corrija ou remova qualquer
sanitize()
função oldschoolDepois de converter todas as
chamadas paramysql_
pdo_query
com parâmetros associados, remova todas aspdo_real_escape_string
chamadas redundantes .Em particular, você deve corrigir qualquer
sanitize
ouclean
oufilterThis
ouclean_data
funções como anunciado por tutoriais datadas de uma forma ou de outra:O bug mais flagrante aqui é a falta de documentação. Mais significativamente, a ordem da filtragem estava exatamente na ordem errada.
A ordem correta teria sido: descontinuada
stripslashes
como a chamada mais interna, depoistrim
, posteriormentestrip_tags
,htmlentities
para o contexto de saída e, por último, apenas_escape_string
como sua aplicação deve preceder diretamente a interseção SQL.Mas como primeiro passo, basta se livrar da
_real_escape_string
chamada.Pode ser necessário manter o restante de sua
sanitize()
função por enquanto, se o fluxo de banco de dados e aplicativo esperar seqüências de caracteres seguras ao contexto HTML. Adicione um comentário que aplique apenas o escape HTML a partir de agora.A manipulação de string / valor é delegada ao PDO e suas instruções parametrizadas.
Se houver alguma menção
stripslashes()
em sua função de higienização, isso pode indicar uma supervisão de nível superior.Isso era comum para desfazer danos (escape duplo) dos objetos obsoletos
magic_quotes
. Que, no entanto, é melhor corrigido centralmente , não sequência por sequência.Use uma das abordagens de reversão da terra do usuário . Em seguida, remova o
stripslashes()
nasanitize
função.Como as declarações preparadas diferem
Quando você embaralha variáveis de seqüência de caracteres nas consultas SQL, isso não fica mais complicado para você seguir. Também é um esforço estranho para o MySQL segregar código e dados novamente.
As injeções de SQL simplesmente são quando os dados sangram no contexto do código . Um servidor de banco de dados não pode mais tarde identificar onde o PHP originalmente colou variáveis entre as cláusulas de consulta.
Com parâmetros vinculados, você separa o código SQL e os valores de contexto SQL no seu código PHP. Mas ele não é embaralhado novamente nos bastidores (exceto com DOP :: EMULATE_PREPARES). Seu banco de dados recebe os comandos SQL não variados e os valores das variáveis 1: 1.
Embora essa resposta enfatize que você deve se preocupar com as vantagens de legibilidade da queda
. Ocasionalmente, há também uma vantagem de desempenho (INSERTs repetidos com apenas valores diferentes) devido a essa separação visível e técnica de dados / códigos.mysql_
Cuidado que a ligação de parâmetro ainda não é uma solução completa e mágica contra todas as injeções de SQL. Ele lida com o uso mais comum de dados / valores. Mas não é possível colocar na lista de permissões os identificadores de nome / tabela da coluna, ajudar na construção de cláusulas dinâmicas ou apenas listas de valores de matriz simples.
Uso de DOP híbrido
Essas
pdo_*
funções de invólucro fazem uma API stop-gap amigável para codificação. (É praticamente o queMYSQLI
poderia ter sido se não fosse a mudança de assinatura da função idiossincrática). Eles também expõem o DOP real na maioria das vezes.A reescrita não precisa parar de usar os novos nomes de função pdo_. Você pode fazer uma transição de cada pdo_query () para uma chamada $ pdo-> prepare () -> execute () simples.
É melhor começar a simplificar novamente. Por exemplo, a busca comum de resultados:
Pode ser substituído por apenas uma iteração foreach:
Ou melhor ainda, uma recuperação direta e completa da matriz:
Na maioria dos casos, você receberá avisos mais úteis do que o PDO ou o mysql_ geralmente fornecem após consultas com falha.
Outras opções
Esperamos que isso tenha visualizado algumas razões práticas e um caminho que vale a pena abandonar
.mysql_
Apenas mudando para pdonão é suficiente.
pdo_query()
também é apenas uma interface para isso.A menos que você também introduza a ligação de parâmetro ou possa utilizar outra coisa da API mais agradável, é uma opção inútil. Espero que seja retratado suficientemente simples para não promover o desânimo para os recém-chegados. (A educação geralmente funciona melhor que a proibição.)
Embora se qualifique para a categoria de coisa mais simples que poderia funcionar, também é um código muito experimental. Acabei de escrever no fim de semana. Há uma infinidade de alternativas no entanto. Basta pesquisar no Google para abstração de banco de dados PHP e navegar um pouco. Sempre houve e haverá muitas bibliotecas excelentes para essas tarefas.
Se você deseja simplificar ainda mais sua interação com o banco de dados, vale a pena tentar mapeadores como Paris / Idiorm . Assim como ninguém mais usa o DOM agradável em JavaScript, você não precisa cuidar de uma interface de banco de dados bruta hoje em dia.
fonte
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
função - ie:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
pdo_real_escape_string()
<- Isso é mesmo uma função real, não consigo encontrar nenhuma documentação para isso? Por favor, poste uma fonte para isso.As
mysql_
funções:fonte
mysqli_
mysql_*
função é um shell nas funções mysqlnd para versões mais recentes do PHP. Assim, mesmo que a biblioteca cliente antigo não é mantida mais, mysqlnd é mantida :)Falando de razões técnicas , existem apenas algumas, extremamente específicas e raramente usadas. Muito provavelmente você nunca os usará em sua vida.
Talvez eu seja muito ignorante, mas nunca tive a oportunidade de usá-las como
Se você precisar deles - essas são, sem dúvida, razões técnicas para deixar a extensão mysql em direção a algo mais elegante e com aparência moderna.
No entanto, existem também alguns problemas não técnicos, que podem tornar sua experiência um pouco mais difícil
Esta última questão é um problema.
Mas, na minha opinião, a solução proposta também não é melhor.
Parece-me um sonho idealista demais que todos esses usuários de PHP aprendam a lidar com consultas SQL corretamente de uma só vez. Muito provavelmente eles mudariam o mysql_ * para mysqli_ * mecanicamente, deixando a abordagem da mesma forma . Especialmente porque o mysqli faz uso de declarações preparadas inacreditavelmente doloroso e problemático.
Sem mencionar que instruções nativas preparadas não são suficientes para proteger contra injeções de SQL e nem o mysqli nem o PDO oferecem uma solução.
Portanto, em vez de combater essa extensão honesta, prefiro combater práticas erradas e educar as pessoas da maneira certa.
Além disso, existem alguns motivos falsos ou não significativos, como
mysql_query("CALL my_proc");
por idades)O último é um ponto interessante. Embora o mysql ext não suporte instruções preparadas nativamente , elas não são necessárias para a segurança. Podemos facilmente falsificar declarações preparadas usando espaços reservados manipulados manualmente (assim como a DOP):
voila , tudo é parametrizado e seguro.
Mas tudo bem, se você não gosta da caixa vermelha no manual, surge um problema de escolha: mysqli ou DOP?
Bem, a resposta seria a seguinte:
Se, como a grande maioria das pessoas em PHP, você estiver usando chamadas brutas de API diretamente no código do aplicativo (que é uma prática essencialmente errada) - a DOP é a única opção , pois essa extensão finge não ser apenas API, mas um semi-DAL, ainda incompleto, mas oferece muitos recursos importantes, com dois deles distingue criticamente o DOP do mysqli:
Portanto, se você é um usuário PHP médio e deseja poupar muitas dores de cabeça ao usar instruções nativas, o DOP - novamente - é a única opção.
No entanto, a DOP também não é uma bala de prata e tem suas dificuldades.
Então, eu escrevi soluções para todas as armadilhas comuns e casos complexos no wiki da tag DOP
No entanto, todo mundo falando sobre extensões sempre perde os 2 fatos importantes sobre Mysqli e DOP:
Declaração preparada não é uma bala de prata . Existem identificadores dinâmicos que não podem ser vinculados usando instruções preparadas. Existem consultas dinâmicas com um número desconhecido de parâmetros, o que dificulta a criação de consultas.
Nem as funções mysqli_ * nem PDO deveriam aparecer no código do aplicativo.
Deve haver uma camada de abstração entre eles e o código do aplicativo, que fará todo o trabalho sujo de vinculação, loop, manipulação de erros etc. no interior, tornando o código do aplicativo SECO e limpo. Especialmente para casos complexos, como criação de consultas dinâmicas.
Portanto, apenas mudar para DOP ou mysqli não é suficiente. É preciso usar um ORM, um construtor de consultas ou qualquer classe de abstração de banco de dados em vez de chamar funções de API brutas em seu código.
E pelo contrário - se você tem uma camada de abstração entre o código do aplicativo e a API do mysql - na verdade, não importa qual mecanismo é usado. Você pode usar o mysql ext até que fique obsoleto e reescreva facilmente sua classe de abstração para outro mecanismo, mantendo todo o código do aplicativo intacto.
Aqui estão alguns exemplos baseados na minha classe safemysql para mostrar como essa classe de abstração deve ser:
Compare esta única linha com a quantidade de código necessária com o DOP .
Em seguida, compare com a quantidade louca de código que você precisará com instruções preparadas cruas do Mysqli. Observe que o tratamento de erros, a criação de perfil e o log de consultas já estão integrados e em execução.
Compare-o com as inserções usuais do PDO, quando cada nome de campo é repetido seis a dez vezes - em todos esses numerosos espaços reservados, ligações e definições de consulta.
Outro exemplo:
Você dificilmente encontra um exemplo para a DOP lidar com esse caso prático.
E será muito prolixo e provavelmente inseguro.
Portanto, mais uma vez - não é apenas um driver bruto que deve ser sua preocupação, mas também uma classe de abstração, útil não apenas para exemplos tolos do manual do iniciante, mas para resolver quaisquer problemas da vida real.
fonte
mysql_*
torna as vulnerabilidades muito fáceis de encontrar. Como o PHP é usado por muitos usuários iniciantes,mysql_*
é ativamente prejudicial na prática, mesmo que em teoria possa ser usado sem problemas.everything is parameterized and safe
- pode ser parametrizado, mas sua função não usa instruções reais preparadas.Not under active development
apenas para esse '0,01%' inventado? Se você criar algo com essa função parada, atualizar sua versão do mysql em um ano e acabar com um sistema não operacional, tenho certeza de que muitas pessoas repentinamente estão '0,01%'. Eu diria issodeprecated
enot under active development
estão intimamente relacionados. Você pode dizer que "não há uma razão [digna]]", mas o fato é que, quando é oferecida uma escolha entre as opções,no active development
é quase tão ruim quantodeprecated
eu diria?Há muitas razões, mas talvez a mais importante seja que essas funções incentivem práticas de programação inseguras porque não suportam declarações preparadas. Instruções preparadas ajudam a impedir ataques de injeção SQL.
Ao usar
mysql_*
funções, lembre-se de executar os parâmetros fornecidos pelo usuáriomysql_real_escape_string()
. Se você esquecer apenas um local ou se escapar de apenas parte da entrada, seu banco de dados poderá estar sujeito a ataque.O uso de instruções preparadas
PDO
oumysqli
fará com que seja mais difícil cometer esses tipos de erros de programação.fonte
Porque (entre outras razões) é muito mais difícil garantir a limpeza dos dados de entrada. Se você usa consultas parametrizadas, como se faz com o DOP ou o mysqli, você pode evitar completamente o risco.
Como exemplo, alguém poderia usar
"enhzflep); drop table users"
como um nome de usuário. As funções antigas permitirão executar várias instruções por consulta, para que algo assim desagradável possa excluir uma tabela inteira.Se alguém usasse o PDO do mysqli, o nome do usuário acabaria sendo
"enhzflep); drop table users"
.Veja bobby-tables.com .
fonte
The old functions will allow executing of multiple statements per query
- não, eles não vão. Esse tipo de injeção não é possível com o ext / mysql - a única maneira que esse tipo de injeção é possível com PHP e MySQL é ao usar o MySQLi e amysqli_multi_query()
função. A injeção de tipo possível com ext / mysql e strings sem escape é como' OR '1' = '1
extrair dados do banco de dados que não deveriam estar acessíveis. Em certas situações, é possível injetar subconsultas, no entanto, ainda não é possível modificar o banco de dados dessa maneira.Esta resposta foi escrita para mostrar o quão trivial é ignorar o código de validação do usuário PHP mal escrito, como (e usando o que) esses ataques funcionam e como substituir as funções antigas do MySQL por uma declaração segura preparada - e, basicamente, por que os usuários do StackOverflow (provavelmente com muitos representantes) estão latindo para novos usuários fazendo perguntas para melhorar seu código.
Primeiramente, sinta-se à vontade para criar este banco de dados mysql de teste (chamei minha preparação):
Com isso feito, podemos mudar para o nosso código PHP.
Vamos supor que o script a seguir seja o processo de verificação para um administrador em um site (simplificado, mas funcionando se você o copiar e usar para teste):
Parece bastante legítimo à primeira vista.
O usuário precisa digitar um login e senha, certo?
Brilhante, não entre no seguinte:
e envie.
A saída é a seguinte:
Super! Trabalhando como esperado, agora vamos tentar o nome de usuário e a senha reais:
Surpreendente! Olá, cinco anos, o código verificou corretamente um administrador. É perfeito!
Bem, na verdade não. Vamos dizer que o usuário é uma pessoa pequena e inteligente. Vamos dizer que a pessoa sou eu.
Digite o seguinte:
E a saída é:
Parabéns, você acabou de me permitir inserir sua seção apenas de administradores superprotegidos, digitando um nome de usuário falso e uma senha falsa. Sério, se você não acredita em mim, crie o banco de dados com o código que forneci e execute esse código PHP - que, à primeira vista, REALMENTE parece verificar o nome de usuário e a senha de maneira bastante agradável.
Então, em resposta, é por isso que você está sendo gritado.
Então, vamos dar uma olhada no que deu errado e por que eu acabei de entrar na sua caverna de morcego super-administrador. Peguei um palpite e presumi que você não estava sendo cuidadoso com suas entradas e simplesmente as transmitiu diretamente ao banco de dados. Eu construí a entrada de uma maneira que alteraria a consulta que você estava realmente executando. Então, o que deveria ser e o que acabou sendo?
Essa é a consulta, mas quando substituímos as variáveis pelas entradas reais que usamos, obtemos o seguinte:
Veja como eu construí minha "senha" para fechar primeiro as aspas simples em torno da senha e introduzir uma comparação completamente nova? Então, por questão de segurança, adicionei outra "string" para que a citação única ficasse fechada conforme o esperado no código que tínhamos originalmente.
No entanto, não se trata de pessoas gritando com você agora, mas sim de mostrar como tornar seu código mais seguro.
Ok, então o que deu errado e como podemos corrigi-lo?
Este é um ataque clássico de injeção de SQL. Um dos mais simples para esse assunto. Na escala dos vetores de ataque, esta é uma criança atacando um tanque - e vencendo.
Então, como protegemos sua seção de administrador sagrado e a tornamos agradável e segura? A primeira coisa a fazer será parar de usar essas
mysql_*
funções realmente antigas e obsoletas . Eu sei, você seguiu um tutorial que encontrou on-line e funciona, mas é antigo, está desatualizado e, no espaço de alguns minutos, acabei de ultrapassá-lo sem nem mesmo suar a camisa.Agora, você tem as melhores opções para usar mysqli_ ou PDO . Pessoalmente, sou um grande fã da DOP, portanto usarei a DOP no restante desta resposta. Existem prós e contras, mas pessoalmente acho que os profissionais superam os contras. É portátil em vários mecanismos de banco de dados - esteja você usando o MySQL ou Oracle ou qualquer coisa sangrenta - apenas alterando a cadeia de conexão, possui todos os recursos sofisticados que queremos usar e é agradável e limpo. Eu gosto de limpar.
Agora, vamos dar uma olhada nesse código novamente, desta vez escrito usando um objeto PDO:
As principais diferenças são que não há mais
mysql_*
funções. Tudo é feito por meio de um objeto DOP; em segundo lugar, ele está usando uma instrução preparada. Agora, o que é uma declaração pré-preparada que você pergunta? É uma maneira de informar ao banco de dados antes da execução de uma consulta qual é a consulta que vamos executar. Nesse caso, dizemos ao banco de dados: "Oi, vou executar uma instrução select que deseja id, userid e pass dos usuários da tabela em que o userid é uma variável e o pass também é uma variável".Em seguida, na instrução execute, passamos ao banco de dados uma matriz com todas as variáveis que ele espera agora.
Os resultados são fantásticos. Vamos tentar as combinações de nome de usuário e senha anteriores:
O usuário não foi verificado. Impressionante.
E se:
Ah, fiquei um pouco empolgado, funcionou: o cheque passou. Temos um administrador verificado!
Agora, vamos tentar os dados que um sujeito inteligente digitaria para tentar superar nosso pequeno sistema de verificação:
Desta vez, temos o seguinte:
É por isso que você está sendo criticado ao postar perguntas - é porque as pessoas podem ver que seu código pode ser ignorado sem sequer tentar. Por favor, use esta pergunta e resposta para melhorar seu código, torná-lo mais seguro e usar as funções atuais.
Por fim, isso não quer dizer que esse seja um código PERFEITO. Você pode fazer muito mais para aprimorá-lo, usar senhas com hash, por exemplo, garantir que, ao armazenar informações sensíveis no banco de dados, não as armazene em texto simples, tenha vários níveis de verificação - mas, na verdade, se você acabou de alterar seu código antigo propenso a injeção para isso, estará bem no caminho de escrever um bom código - e o fato de ter chegado tão longe e ainda estar lendo me dá uma sensação de esperança de que você não apenas implementará esse tipo de código ao escrever seus sites e aplicativos, mas você pode pesquisar e pesquisar outras coisas que acabei de mencionar - e mais. Escreva o melhor código possível, não o mais básico que mal funciona.
fonte
mysql_*
por si só não é inseguro, mas promove código inseguro por meio de tutoriais ruins e a falta de uma API adequada prepara a API.A extensão MySQL é a mais antiga das três e era a maneira original que os desenvolvedores costumavam se comunicar com o MySQL. Essa extensão agora está sendo preterida em favor das outras duas alternativas devido a melhorias feitas nas versões mais recentes do PHP e do MySQL.
O MySQLi é a extensão 'aprimorada' para trabalhar com bancos de dados MySQL. Ele tira proveito dos recursos que estão disponíveis nas versões mais recentes do servidor MySQL, expõe ao desenvolvedor uma interface orientada a funções e uma interface orientada a objetos e faz algumas outras coisas bacanas.
O PDO oferece uma API que consolida a maior parte da funcionalidade anteriormente espalhada pelas principais extensões de acesso ao banco de dados, como MySQL, PostgreSQL, SQLite, MSSQL, etc. A interface expõe objetos de alto nível para o programador trabalhar com conexões, consultas e bancos de dados. conjuntos de resultados e drivers de baixo nível executam comunicação e manipulação de recursos com o servidor de banco de dados. Muita discussão e trabalho estão entrando no DOP e é considerado o método apropriado de trabalhar com bancos de dados em código profissional moderno.
fonte
Acho as respostas acima muito longas, para resumir:
Fonte: Visão geral do MySQLi
Como explicado nas respostas acima, as alternativas ao mysql são mysqli e PDO (PHP Data Objects).
O MySQLi e o PDO foram introduzidos no PHP 5.0, enquanto o MySQL foi introduzido antes do PHP 3.0. Um ponto a ser observado é que o MySQL está incluído no PHP5.x, mas foi descontinuado em versões posteriores.
fonte
É possível definir quase todas as
mysql_*
funções usando mysqli ou PDO. Apenas inclua-os no topo do seu aplicativo PHP antigo, e ele funcionará no PHP7. Minha solução aqui .fonte
As funções que são tão semelhantes a este
mysql_connect()
,mysql_query()
tipo são a versão anterior PHP ou seja (PHP 4) funções e agora não em utilização.Eles são substituídos por
mysqli_connect()
, damysqli_query()
mesma forma no PHP5 mais recente.Esta é a razão por trás do erro.
fonte
O MySQL foi descontinuado no PHP 5.5.0 e removido no PHP 7.0.0. Para um aplicativo grande e antigo, é difícil pesquisar e substituir cada função.
Podemos usar as funções do MySQL criando uma função de wrapper para cada código abaixo em execução. Clique aqui
fonte
As funções mysql_ * foram descontinuadas (a partir do PHP 5.5 ), devido ao fato de que melhores funções e estruturas de código foram desenvolvidas. O fato de a função ter sido descontinuada significa que não será necessário mais esforço para aprimorá-la em termos de desempenho e segurança, o que significa que é menos prova de futuro .
Se você precisar de mais motivos:
fonte