Evitando injeção de SQL sem parâmetros


109

Estamos tendo outra discussão aqui no trabalho sobre o uso de consultas sql parametrizadas em nosso código. Temos dois lados na discussão: eu e alguns outros que dizem que devemos sempre usar parâmetros para proteção contra injeções de sql e os outros caras que acham que não é necessário. Em vez disso, eles desejam substituir apóstrofos únicos por dois apóstrofos em todas as strings para evitar injeções de sql. Nossos bancos de dados estão todos executando Sql Server 2005 ou 2008 e nossa base de código está executando em .NET framework 2.0.

Deixe-me dar um exemplo simples em C #:

Eu quero que usemos isso:

string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe

Enquanto os outros caras querem fazer isso:

string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?

Onde a função SafeDBString é definida da seguinte forma:

string SafeDBString(string inputValue) 
{
    return "'" + inputValue.Replace("'", "''") + "'";
}

Agora, enquanto usarmos SafeDBString em todos os valores de string em nossas consultas, devemos estar seguros. Certo?

Existem dois motivos para usar a função SafeDBString. Primeiro, é a maneira como tem sido feito desde a idade da pedra e, segundo, é mais fácil depurar as instruções sql, pois você vê a consulta exata que é executada no banco de dados.

Então. Minha dúvida é se realmente é suficiente usar a função SafeDBString para evitar ataques de injeção de sql. Tenho tentado encontrar exemplos de código que violem essa medida de segurança, mas não consigo encontrar nenhum exemplo disso.

Há alguém aí que pode quebrar isso? Como você faria?

EDITAR: Para resumir as respostas até agora:

  • Ninguém encontrou uma maneira de contornar o SafeDBString no Sql Server 2005 ou 2008 ainda. Isso é bom, eu acho?
  • Várias respostas indicaram que você obtém um ganho de desempenho ao usar consultas parametrizadas. O motivo é que os planos de consulta podem ser reutilizados.
  • Também concordamos que o uso de consultas parametrizadas fornece um código mais legível e mais fácil de manter
  • Além disso, é mais fácil sempre usar parâmetros do que usar várias versões de SafeDBString, strings para conversões de números e strings para conversões de datas.
  • Usando parâmetros, você obtém conversão automática de tipo, algo que é especialmente útil quando estamos trabalhando com datas ou números decimais.
  • E, finalmente: não tente fazer a segurança sozinho, como escreveu JulianR. Os fornecedores de banco de dados gastam muito tempo e dinheiro com segurança. Não há como fazermos melhor e não há razão para tentarmos fazer o trabalho deles.

Portanto, embora ninguém tenha conseguido quebrar a segurança simples da função SafeDBString, recebi muitos outros bons argumentos. Obrigado!


16
Seus colegas estão muito, muito, fora da base. Desafie-os a encontrar uma única literatura que apóie sua posição. O argumento ex neolithos é ridículo, as coisas mudam, apenas uma pessoa presa na idade da pedra não conseguiria se adaptar.
annakata

1
Bem, pelo menos seus colegas protegem contra UMA das diferentes formas de hack ... Eles têm certeza de que todas as consultas parametrizadas fazem? (Eu não sou ...)
Arjan Einbu

1
Qualquer vulnerabilidade não os convencerá. Se você trouxer várias vulnerabilidades (que é o que você está pedindo) e outros problemas e apontar um por um que os parâmetros resolverão esse problema e que sua equipe teria que escrever montanhas de código para fornecer uma fração da funcionalidade, você pode ganhe-os. Boa sorte.
Robert Gowland

3
Mesmo sem as aspas simples, você ainda pode quebrar seu código com lógica. Tente usar o nome de usuário "test OR 1 = 1" - você obtém todas as linhas retornadas em vez de apenas aquela com o teste de nome de usuário!
Ponte

1
Suspiro. Eu realmente não entendo como nós, como uma indústria, conseguimos continuar tolerando esse tipo de comportamento não profissional.
jeroenh

Respostas:


83

Acho que a resposta correta é:

Não tente fazer a segurança sozinho . Use qualquer biblioteca confiável padrão da indústria disponível para o que você está tentando fazer, em vez de tentar fazer você mesmo. Quaisquer suposições que você fizer sobre segurança, podem estar incorretas. Por mais segura que sua abordagem possa parecer (e, na melhor das hipóteses, parece duvidosa), há o risco de você estar negligenciando algo e você realmente quer arriscar quando se trata de segurança?

Use parâmetros.


Re "Use qualquer biblioteca confiável padrão do setor" - você pode recomendar uma para .NET? Talvez mais de um dependendo do banco de dados: SQLServer, MySQL, PostgreSQL? Procurei o sanitizer SQL, mas sem muita sorte, então fui forçado a implementar o meu, da melhor maneira que pude (o que sem dúvida está longe de ser infalível).
PSU

72

E então alguém usa "em vez de". Os parâmetros são, IMO, o único caminho seguro a seguir.

Também evita muitos problemas de i18n com datas / números; que data é 01/02/03? Quanto é 123.456? Seus servidores (app-server e db-server) concordam uns com os outros?

Se o fator de risco não os convence, que tal o desempenho? O RDBMS pode reutilizar o plano de consulta se você usar parâmetros, ajudando no desempenho. Não pode fazer isso apenas com a corda.


Eu tentei os argumentos de formatação e desempenho, mas eles ainda não estão convencidos.
Rune Grimstad

5
Na verdade, o sql server pode reutilizar o plano de consulta, quer você use parâmetros ou não. Eu concordo com os outros argumentos, mas na maioria dos casos, o argumento de desempenho para sql parametrizado não funciona mais.
tnyfst

1
@tnyfst: ele pode reutilizar o plano de execução quando a string de consulta muda para cada combinação de valores de parâmetro? Eu não pensei que isso fosse possível.
John Saunders

4
O plano de consulta será reutilizado se o texto da consulta for IDÊNTICO a um texto de consulta anterior. Portanto, se você enviar a MESMA consulta EXATA duas vezes, ela será reutilizada. No entanto, se você alterar apenas um espaço ou uma vírgula ou algo assim, um novo plano de consulta terá que ser determinado.
marc_s

1
@Marc: Não tenho certeza se você está totalmente correto. As estatísticas de cache de servidores SQL são um pouco estranhas. O analisador é capaz de identificar constantes no texto e pode converter a string SQL em um que usa parâmetros artificialmente. Ele pode então inserir no cache o texto desta nova consulta parametrizada. O SQL semelhante subsequente pode encontrar sua versão parametrizada correspondente no cache. No entanto, as versões parametrizadas nem sempre são usadas com as versões SQL originais sendo armazenadas em cache, eu suspeito que o SQL tem um zilhão de razões relacionadas ao desempenho para escolher entre as duas abordagens.
AnthonyWJones

27

O argumento é impossível. Se você conseguir encontrar uma vulnerabilidade, seus colegas de trabalho apenas alterarão a função SafeDBString para contabilizá-la e, em seguida, pedirão que você prove que não é seguro novamente.

Visto que as consultas parametrizadas são uma prática recomendada de programação indiscutível, o ônus da prova deve recair sobre eles para declarar por que não estão usando um método que seja mais seguro e com melhor desempenho.

Se o problema for reescrever todo o código legado, o compromisso mais fácil seria usar consultas parametrizadas em todo o código novo e refatorar o código antigo para usá-los ao trabalhar nesse código.

Meu palpite é que a questão real é orgulho e teimosia, e não há muito mais que você possa fazer a respeito.


19

Em primeiro lugar, sua amostra para a versão "Substituir" está errada. Você precisa colocar apóstrofos ao redor do texto:

string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
SqlCommand getUser = new SqlCommand(sql, connection);

Portanto, essa é outra coisa que os parâmetros fazem por você: você não precisa se preocupar se um valor precisa ou não ser colocado entre aspas. Claro, você pode construir isso na função, mas então você precisa adicionar muita complexidade à função: como saber a diferença entre 'NULL' como nulo e 'NULL' como apenas uma string, ou entre um número e uma string que contém muitos dígitos. É apenas mais uma fonte de bugs.

Outra coisa é o desempenho: os planos de consulta parametrizados costumam ser melhor armazenados em cache do que os planos concatenados, portanto, talvez economizando uma etapa do servidor ao executar a consulta.

Além disso, escapar aspas simples não é bom o suficiente. Muitos produtos de banco de dados permitem métodos alternativos para caracteres de escape dos quais um invasor pode tirar vantagem. No MySQL, por exemplo, você também pode escapar de uma aspa simples com uma barra invertida. E então o seguinte valor de "nome" explodiria o MySQL apenas com a SafeDBString()função, porque quando você duplica a aspa simples, o primeiro ainda é escapado pela barra invertida, deixando o segundo "ativo":

x \ 'OR 1 = 1; -


Além disso, JulianR traz um bom ponto abaixo: NUNCA tente fazer o trabalho de segurança sozinho. É tão fácil errar na programação de segurança de maneiras sutis que parecem funcionar, mesmo com testes completos. Então o tempo passa e um ano depois você descobre que seu sistema foi quebrado há seis meses e você nem sabia disso até então.

Sempre confie o máximo possível nas bibliotecas de segurança fornecidas para sua plataforma. Eles serão escritos por pessoas que ganham a vida com códigos de segurança, muito mais bem testados do que o que você pode gerenciar e atendidos pelo fornecedor se uma vulnerabilidade for encontrada.


5
A função de substituição adiciona os apóstrofos
Rune Grimstad

5
Então é apenas mais uma fonte de bugs. Como ele sabe a diferença entre NULL como valor nulo e NULL como string de texto? Ou entre uma entrada de número e uma string que simplesmente contém dígitos?
Joel Coehoorn

Bom ponto. Você só deve usar a função para strings e, possivelmente, datas, portanto, tome cuidado. Essa é mais uma razão para usar parâmetros! Yay!
Rune Grimstad

10

Então eu diria:

1) Por que você está tentando reimplementar algo que está embutido? está lá, prontamente disponível, fácil de usar e já depurado em escala global. Se futuros bugs forem encontrados nele, eles serão corrigidos e disponibilizados para todos muito rapidamente, sem que você precise fazer nada.

2) Quais processos existem para garantir que você nunca perca uma chamada para SafeDBString? Perdê-lo em apenas um lugar pode abrir uma série de problemas. Quanto você vai observar essas coisas e considerar o quanto é desperdiçado esse esforço quando a resposta correta aceita é tão fácil de alcançar.

3) Você tem certeza de que cobriu todos os vetores de ataque que a Microsoft (a autora do banco de dados e da biblioteca de acesso) conhece em sua implementação de SafeDBString ...

4) Quão fácil é ler a estrutura do sql? O exemplo usa concatenação +, os parâmetros são muito parecidos com string.Format, que é mais legível.

Além disso, existem 2 maneiras de descobrir o que foi realmente executado - role sua própria função LogCommand, uma função simples sem preocupações de segurança , ou até mesmo olhe para um rastreio sql para descobrir o que o banco de dados pensa que está realmente acontecendo.

Nossa função LogCommand é simplesmente:

    string LogCommand(SqlCommand cmd)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(cmd.CommandText);
        foreach (SqlParameter param in cmd.Parameters)
        {
            sb.Append(param.ToString());
            sb.Append(" = \"");
            sb.Append(param.Value.ToString());
            sb.AppendLine("\"");
        }
        return sb.ToString();
    }

Certo ou errado, ele nos fornece as informações de que precisamos sem problemas de segurança.


1
Ele provavelmente terá que lidar com um bando de velhos programadores de VBSCRIPT que estão acostumados a fazer tudo, incluindo XML e SQL, por meio de concatenação de strings. São pessoas que ficam assustadas com o uso de uma API. Não há muito o que fazer com eles, pelo menos nada humano.
John Saunders

1
+1 para o item 2, com a exceção de que também não há como impor parâmetros reais.
Joel Coehoorn

7

Com consultas parametrizadas, você obtém mais do que proteção contra injeção de sql. Você também obtém um melhor potencial de cache do plano de execução. Se você usar o gerador de perfil de consulta do sql server, ainda poderá ver o 'sql exato que é executado no banco de dados', portanto, não está realmente perdendo nada em termos de depuração de suas instruções sql.


O MySQL também registra consultas parametrizadas com valores de parâmetros interpolados nelas.
Bill Karwin

5

Usei ambas as abordagens para evitar ataques de injeção de SQL e definitivamente prefiro consultas parametrizadas. Quando usei consultas concatenadas, usei uma função de biblioteca para escapar das variáveis ​​(como mysql_real_escape_string) e não teria certeza de ter coberto tudo em uma implementação proprietária (como parece que você também).


2
+1 porque mysql_real_escape_string () escapa \ x00, \ x1a, \ n \ r 'e ". Ele também lida com problemas de conjunto de caracteres. A função ingênua de colegas de trabalho do OP não faz nada disso!
Bill Karwin

4

Você não pode fazer facilmente qualquer tipo de verificação da entrada do usuário sem usar parâmetros.

Se você usar as classes SQLCommand e SQLParameter para fazer suas chamadas de banco de dados, ainda poderá ver a consulta SQL que está sendo executada. Observe a propriedade CommandText do SQLCommand.

Eu sempre sou um pouco suspeito da abordagem "faça você mesmo" para prevenir a injeção de SQL quando as consultas parametrizadas são tão fáceis de usar. Em segundo lugar, só porque "sempre foi feito assim" não significa que seja a maneira certa de fazer isso.


3

Isso só é seguro se você tiver a garantia de que vai passar uma string.

E se você não estiver passando uma string em algum ponto? E se você passar apenas um número?

http://www.mywebsite.com/profile/?id=7;DROP DATABASE DB

No final das contas se tornaria:

SELECT * FROM DB WHERE Id = 7;DROP DATABASE DB

É uma string ou um número. Uma string é escapada com SafeDbString. Um número é Int32 e não pode descartar bancos de dados.
Andomar

Os números são mais fáceis de manusear. Você apenas converte o parâmetro em int / float / qualquer coisa antes de usá-lo na consulta. O problema é quando você deve aceitar dados de string.
Rune Grimstad

Andomar - se você está apenas construindo uma instrução SQL à mão, então o "tipo" pretendido não importa, você pode injetar um número em SQL muito, muito facilmente. Rune - Eu acho que isso depende muito do desenvolvedor individual para lembrar todas as nuances de resolver manualmente a injeção de SQL. Se você apenas disser "usar parâmetros", é muito simples e eles não podem dar errado.
joshcomley

@Andomar: E o NULL? Ou strings que parecem números?
Joel Coehoorn

2

Eu usaria stored procedures ou funções para tudo, então a questão não surgiria.

Onde tenho que colocar SQL no código, uso parâmetros, que é a única coisa que faz sentido. Lembre aos dissidentes que existem hackers mais espertos do que eles e com melhor incentivo para quebrar o código que está tentando enganá-los. Usar parâmetros simplesmente não é possível e não é difícil.


Ok, como fazer injeção de SQL usando parâmetros?
John Saunders

@Saunders: a etapa 1 é encontrar um bug de estouro de buffer na funcionalidade de manipulação de parâmetros de seu banco de dados.
Brian

2
Já encontrou um? Em um banco de dados comercial que está sendo atacado por centenas de milhares de hackers diariamente? Um feito por uma empresa de software conhecida por ter bolsos muito fundos? Você poderia citar o processo pelo nome, se isso fosse possível.
John Saunders

1
Claro, se o SPROC usa concatenação e EXEC (em vez de sp_ExecuteSQL), você está em apuros ... (Já vi isso ser feito errado muitas vezes para descontar ...)
Marc Gravell

2

Concordo amplamente nas questões de segurança.
Outra razão para usar parâmetros é para eficiência.

Os bancos de dados sempre compilarão sua consulta e a armazenarão em cache e, em seguida, reutilizarão a consulta armazenada em cache (que é obviamente mais rápida para as solicitações subsequentes). Se você usar parâmetros, mesmo se você usar parâmetros diferentes, o banco de dados reutilizará sua consulta em cache, uma vez que corresponde com base na string SQL antes de vincular os parâmetros.

No entanto, se você não vincular parâmetros, a string SQL muda a cada solicitação (que tem parâmetros diferentes) e nunca corresponderá ao que está em seu cache.


2

Pelas razões já indicadas, os parâmetros são uma ideia muito boa. Mas odiamos usá-los porque criar o parâmetro e atribuir seu nome a uma variável para uso posterior em uma consulta é uma destruição de cabeça tripla.

A classe a seguir envolve o construtor de strings que você normalmente usará para construir solicitações SQL. Ele permite que você escreva consultas paramaterizadas sem nunca ter que criar um parâmetro , para que você possa se concentrar no SQL. Seu código ficará assim ...

var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId, SqlDbType.Int);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName, SqlDbType.NVarChar);
myCommand.CommandText = bldr.ToString();

A legibilidade do código, espero que concorde, foi bastante aprimorada e a saída é uma consulta parametrizada adequada.

A classe é assim ...

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

namespace myNamespace
{
    /// <summary>
    /// Pour le confort et le bonheur, cette classe remplace StringBuilder pour la construction
    /// des requêtes SQL, avec l'avantage qu'elle gère la création des paramètres via la méthode
    /// Value().
    /// </summary>
    public class SqlBuilder
    {
        private StringBuilder _rq;
        private SqlCommand _cmd;
        private int _seq;
        public SqlBuilder(SqlCommand cmd)
        {
            _rq = new StringBuilder();
            _cmd = cmd;
            _seq = 0;
        }
        //Les autres surcharges de StringBuilder peuvent être implémenté ici de la même façon, au besoin.
        public SqlBuilder Append(String str)
        {
            _rq.Append(str);
            return this;
        }
        /// <summary>
        /// Ajoute une valeur runtime à la requête, via un paramètre.
        /// </summary>
        /// <param name="value">La valeur à renseigner dans la requête</param>
        /// <param name="type">Le DBType à utiliser pour la création du paramètre. Se référer au type de la colonne cible.</param>
        public SqlBuilder Value(Object value, SqlDbType type)
        {
            //get param name
            string paramName = "@SqlBuilderParam" + _seq++;
            //append condition to query
            _rq.Append(paramName);
            _cmd.Parameters.Add(paramName, type).Value = value;
            return this;
        }
        public SqlBuilder FuzzyValue(Object value, SqlDbType type)
        {
            //get param name
            string paramName = "@SqlBuilderParam" + _seq++;
            //append condition to query
            _rq.Append("'%' + " + paramName + " + '%'");
            _cmd.Parameters.Add(paramName, type).Value = value;
            return this; 
        }

        public override string ToString()
        {
            return _rq.ToString();
        }
    }
}

1

Pelo pouco tempo que tive para investigar problemas de injeção de SQL, posso ver que tornar um valor 'seguro' também significa que você está fechando a porta para situações em que pode realmente querer apóstrofos em seus dados - e o nome de alguém , por exemplo, O'Reilly.

Isso deixa os parâmetros e procedimentos armazenados.

E sim, você deve sempre tentar implementar o código da melhor maneira que sabe agora - não apenas como sempre foi feito.


Os apóstrofos duplos serão traduzidos pelo sql server em um único apóstrofo, então O'Reilly seria traduzido para Name = 'O''Reilly'
Rune Grimstad

Então, há uma função correspondente para remover apóstrofos quando o usuário deseja ver seus dados?
quamrana

Não há necessidade. A sequência de escape permite que o analisador veja uma aspa simples em vez do final da string. À medida que é analisado, ele é visto ''como um literal ', então sua string será vista internamente como a sequência de caracteres O'Reilly. Isso é o que o banco de dados armazenará, recuperará, comparará etc. Se você quiser mostrar ao usuário seus dados depois de escapar deles, mantenha uma cópia do lado do aplicativo de string sem escape.
cHao

1

Aqui estão alguns artigos que você pode achar úteis para convencer seus colegas de trabalho.

http://www.sommarskog.se/dynamic_sql.html

http://unixwiz.net/techtips/sql-injection.html

Pessoalmente, prefiro nunca permitir que nenhum código dinâmico toque meu banco de dados, exigindo que todos os contatos sejam por meio de sps (e não um que use SQl dinâmico). Isso significa que nada além do que eu dei permissão aos usuários para fazer pode ser feito e que os usuários internos (exceto os poucos com acesso de produção para fins administrativos) não podem acessar diretamente minhas tabelas e criar confusão, roubar dados ou cometer fraudes. Se você executa um aplicativo financeiro, esse é o caminho mais seguro.


1

Pode ser quebrado, no entanto, os meios dependem das versões / patches exatos, etc.

Um que já foi mencionado é o bug de estouro / truncamento que pode ser explorado.

Outro meio futuro seria encontrar bugs semelhantes a outros bancos de dados - por exemplo, a pilha MySQL / PHP sofreu um problema de escape porque certas sequências UTF8 poderiam ser usadas para manipular a função de substituição - a função de substituição seria enganada para introduzir os caracteres de injeção.

No final do dia, o mecanismo de segurança substituto depende da funcionalidade esperada, mas não pretendida . Uma vez que a funcionalidade não era a finalidade pretendida do código, existe uma grande probabilidade de que alguma peculiaridade descoberta interrompa a funcionalidade esperada.

Se você tiver muito código legado, o método replace pode ser usado como um paliativo para evitar reescrita e testes demorados. Se você está escrevendo um novo código, não há desculpa.


1

Sempre use consultas parametrizadas sempre que possível. Às vezes, mesmo uma entrada simples sem o uso de quaisquer caracteres estranhos já pode criar uma injeção SQL se não for identificada como uma entrada para um campo no banco de dados.

Portanto, deixe o banco de dados fazer o seu trabalho de identificar a entrada em si, sem mencionar que também economiza muitos problemas quando você precisa inserir caracteres estranhos que, de outra forma, seriam escapados ou alterados. Ele pode até mesmo economizar um tempo de execução valioso no final, por não ter que calcular a entrada.


1

Não vi nenhuma outra resposta abordar esse lado do 'por que fazer você mesmo é ruim', mas considere um ataque de truncamento SQL .

Também existe a QUOTENAMEfunção T-SQL que pode ser útil se você não conseguir convencê-los a usar parâmetros. Ele captura muitas (todas?) Das preocupações de qoute escapadas.


1

2 anos depois , eu recidiveu ... Qualquer um que considere os parâmetros uma dor pode experimentar minha extensão VS, QueryFirst . Você edita sua solicitação em um arquivo .sql real (Validação, Intellisense). Para adicionar um parâmetro, basta digitá-lo diretamente em seu SQL, começando com o '@'. Ao salvar o arquivo, o QueryFirst gerará classes de wrapper para permitir que você execute a consulta e acesse os resultados. Ele pesquisará o tipo de banco de dados de seu parâmetro e o mapeará para um tipo .net, que você encontrará como uma entrada para os métodos Execute () gerados. Não poderia ser mais simples. Fazer isso da maneira certa é radicalmente mais rápido e fácil do que fazer de qualquer outra forma, e criar uma vulnerabilidade de injeção de sql se torna impossível, ou pelo menos perversamente difícil. Existem outras vantagens matadoras, como ser capaz de deletar colunas em seu banco de dados e ver imediatamente erros de compilação em seu aplicativo.

isenção de responsabilidade legal: eu escrevi QueryFirst


0

Aqui estão alguns motivos para usar consultas parametrizadas:

  1. Segurança - a camada de acesso ao banco de dados sabe como remover ou escapar itens que não são permitidos nos dados.
  2. Separação de interesses - Meu código não é responsável por transformar os dados em um formato que o banco de dados goste.
  3. Sem redundância - não preciso incluir um assembly ou classe em cada projeto que formate / escape esse banco de dados; está embutido na biblioteca de classes.

0

Havia poucas vulnerabilidades (não me lembro qual era o banco de dados) relacionadas ao estouro de buffer da instrução SQL.

O que eu quero dizer é que SQL-Injection é mais do que apenas "escapar da citação", e você não tem ideia do que virá a seguir.


0

Outra consideração importante é manter o controle de dados com e sem escape. Existem toneladas e toneladas de aplicativos, da Web e outros, que não parecem controlar adequadamente quando os dados estão em formato Unicode, e codificados, HTML formatados etc. É óbvio que se tornará difícil manter o controle de quais strings estão ''codificadas e quais não estão.

Também é um problema quando você acaba mudando o tipo de alguma variável - talvez fosse um número inteiro, mas agora é uma string. Agora você tem um problema.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.