Como escapar strings no SQL Server usando PHP?

89

Estou procurando uma alternativa mysql_real_escape_string()para o SQL Server. É a addslashes()minha melhor opção ou existe outra função alternativa que pode ser usada?

Uma alternativa para mysql_error()também seria útil.

Clique em votar positivamente
fonte
2
Para mim, não é uma pergunta duplicada porque se refere ao caso específico de MSSQL que não tem um PDO oficial relacionado
Pierre de LESPINAY

Respostas:

74

addslashes()não é totalmente adequado, mas o pacote mssql do PHP não oferece nenhuma alternativa decente. A solução feia, mas totalmente geral, é codificar os dados como um bytestring hexadecimal, ou seja,

$unpacked = unpack('H*hex', $data);
mssql_query('
    INSERT INTO sometable (somecolumn)
    VALUES (0x' . $unpacked['hex'] . ')
');

Abstrato, isso seria:

function mssql_escape($data) {
    if(is_numeric($data))
        return $data;
    $unpacked = unpack('H*hex', $data);
    return '0x' . $unpacked['hex'];
}

mssql_query('
    INSERT INTO sometable (somecolumn)
    VALUES (' . mssql_escape($somevalue) . ')
');

mysql_error()equivalente é mssql_get_last_message().

caos
fonte
1
Ops, é SELECT SCOPE_IDENTITY ()!
Bryan Rehbein,
4
@genio: Mmm, ótimo, exceto que realmente é. Suponho que você não explicaria o que considera ser o problema.
caos,
3
Você já tentou isso com colunas datetime? Estou recebendo este erro: SQLSTATE[22007]: Invalid datetime format: 210 [Microsoft][ODBC SQL Server Driver][SQL Server]Conversion failed when converting datetime from binary/varbinary string.acredito que este método pode ser correto apenas se funcionar com todos os tipos de dados MSSQL.
Alejandro García Iglesias
1
O conteúdo da mssql_escape()função retornada não está fazendo isso para mim. A exibição de texto depois de fazer uma seleção fica 0x4a2761696d65206269656e206c652063686f636f6c6174assim ilegível.
Jeff Noel
3
@JeffNoel Você provavelmente está envolvendo a string com aspas simples ou duplas. Como o item é escapado em hexadecimal, as aspas não são necessárias. O SQL Server deve converter o valor hexadecimal em algo que o banco de dados entenda.
danielson317,
40
function ms_escape_string($data) {
        if ( !isset($data) or empty($data) ) return '';
        if ( is_numeric($data) ) return $data;

        $non_displayables = array(
            '/%0[0-8bcef]/',            // url encoded 00-08, 11, 12, 14, 15
            '/%1[0-9a-f]/',             // url encoded 16-31
            '/[\x00-\x08]/',            // 00-08
            '/\x0b/',                   // 11
            '/\x0c/',                   // 12
            '/[\x0e-\x1f]/'             // 14-31
        );
        foreach ( $non_displayables as $regex )
            $data = preg_replace( $regex, '', $data );
        $data = str_replace("'", "''", $data );
        return $data;
    }

Parte do código aqui foi roubado do CodeIgniter. Funciona bem e é uma solução limpa.

EDIT: Existem muitos problemas com esse trecho de código acima. Por favor, não use isso sem ler os comentários para saber o que são. Melhor ainda, por favor, não use isso. As consultas parametrizadas são seus amigos: http://php.net/manual/en/pdo.prepared-statements.php

gênio
fonte
1
Por que você precisa do preg_replace? Não é o str_replacesuficiente?
Gabe
gabe: O preg_replace neste caso era para me permitir usar os intervalos fornecidos para mim em classes de caracteres de expressões regulares. Haveria muito mais substituições de string neste caso contrário.
genio
7
-1. Não é responsabilidade de uma função de cotação mutilar os dados - tudo o que ela deve fazer é garantir que a string esteja em um formato que possa ser adicionado a uma instrução SQL e sobreviver sem modificações.
cHao de
6
Desculpe, mas isso é errado a partir da primeira linha de código - empty($value)vai voltar truenão só para '', mas também para null, 0e '0'! Você retornaria uma string vazia em todos esses casos.
Nux
Eu votei a favor, acho que é uma função perfeitamente adequada, contanto que você esteja totalmente ciente dos problemas acima. No entanto, eu o chamaria de ms_escape_and_strip_string, para que qualquer outra pessoa que trabalhasse nele veria que ele executa as duas tarefas. Ter uma string vazia retornada em vários casos é bom, contanto que você considere isso, a menos que eu esteja apenas perdendo um ponto importante aqui. Se isso não atender às suas necessidades, você sempre pode pegar essa linha e substituí-la por uma lógica que se adapte às suas necessidades.
NateDSaint
16

Por que você se incomodaria em escapar de qualquer coisa quando você pode usar parâmetros em sua consulta ?!

sqlsrv_query(
    $connection, 
    'UPDATE some_table SET some_field = ? WHERE other_field = ?', 
    array($_REQUEST['some_field'], $_REQUEST['id'])
)

Ele funciona bem em seleções, exclusões e atualizações, independentemente de seus parâmetros de valores serem nullou não. Faça uma questão de princípio - Não concatene SQL e você estará sempre seguro e suas consultas serão lidas muito melhor.

http://php.net/manual/en/function.sqlsrv-query.php

Konstantin
fonte
Esta é a abordagem correta. Você sempre deve usar parâmetros em vez de consultas ad hoc. No entanto, o OP não está usando os drivers sqlsrv. Ele está usando drivers mssql. portanto, o link a ser usado por aqueles que estão presos aos drivers sqlsrv é http://php.net/manual/en/function.mssql-query.php .
smulholland2
1
@ smulholland2 Você quis dizer "ou aqueles de vocês travaram usando drivers MSSQL "
Konstantin
Um cenário pode ser se você deseja gerar um arquivo de instruções INSERT para usar em um cenário de migração de dados.
Gary Reckard
11

Você pode dar uma olhada na Biblioteca PDO . Você pode usar instruções preparadas com PDO, que automaticamente escapará de quaisquer caracteres inválidos em suas strings se você fizer as instruções preparadas corretamente. Isso é apenas para PHP 5, eu acho.

alex
fonte
Com alguns dos comportamentos mesquinhos que vi fora do PDO, eu teria que fazer alguns testes sérios antes de confiar nele para escapar todos os dados corretamente.
caos
@Chaos realmente? Não estou ciente disso .. você tem um link para um artigo?
alex
O que eu estava pensando era no problema que esse cara estava tendo ontem com o PDO. Coisas de transações não relacionadas, mas inexpressivas. Combine isso com todo o histórico de dados inadequados escapando no PHP (php.net dizendo às pessoas para usar addslashes ()!) E eu fico muito desconfiado.
caos
2
Eu amo PDO e tentei isso primeiro, mas aquele para MSSQL (no Unix, baseado em dblib) às vezes falha em mim (falha de segmentação), é por isso que recorri ao mssql_escape definido acima.
lapo
Obrigado pelo seu comentário, @Iapo. Eu estava pensando em mudar para PDO para um projeto mssql - especificamente para conseguir escapar - mas você me salvou do problema.
Winfield Trail
4

Outra maneira de lidar com aspas simples e duplas é:

function mssql_escape($str)
{
    if(get_magic_quotes_gpc())
    {
        $str = stripslashes($str);
    }
    return str_replace("'", "''", $str);
}
Raja Bilal Ahmed
fonte
get_magic_quites_gpc foi DESCONTINUADO a partir do PHP 5.3.0 e REMOVIDO a partir do PHP 5.4.0. Veja php.net/manual/en/info.configuration.php#ini.magic-quotes-gpc
Jan
2

Para escapar das aspas simples e duplas, você precisa dobrá-las:

$value = 'This is a quote, "I said, 'Hi'"';

$value = str_replace( "'", "''", $value ); 

$value = str_replace( '"', '""', $value );

$query = "INSERT INTO TableName ( TextFieldName ) VALUES ( '$value' ) ";

etc ...

e atribuição: caractere de escape no Microsoft SQL Server 2000

Marklark
fonte
2

Depois de lutar com isso por horas, eu encontrei uma solução que parece quase a melhor.

A resposta do caos de converter valores em hexstring não funciona com todos os tipos de dados, especificamente com colunas datetime.

Eu uso PHP's PDO::quote(), mas como vem com PHP, PDO::quote()não tem suporte para MS SQL Server e retorna FALSE. A solução para que funcionasse era baixar alguns pacotes da Microsoft:

Depois disso, você pode se conectar em PHP com PDO usando um DSN como o exemplo a seguir:

sqlsrv:Server=192.168.0.25; Database=My_Database;

O uso dos parâmetros UIDe PWDno DSN não funcionou, então o nome de usuário e a senha são passados ​​como o segundo e o terceiro parâmetros no construtor PDO ao criar a conexão. Agora você pode usar PHP PDO::quote(). Aproveitar.

Alejandro García Iglesias
fonte
0

Aviso: Esta função foi REMOVIDA no PHP 7.0.0.

http://php.net/manual/en/function.mssql-query.php

Para quem ainda usa essas funções mssql_ *, lembre-se de que elas foram removidas do PHP a partir da v7.0.0. Então, isso significa que você eventualmente terá que reescrever seu código de modelo para usar a biblioteca PDO, sqlsrv_ * etc. Se você estiver procurando por algo com um método "citando / escapando", eu recomendaria PDO.

Alternativas para esta função incluem: PDO :: query (), sqlsrv_query () e odbc_exec ()

jjwdesign
fonte
0

Se você estiver usando PDO, você pode usar o PDO::quotemétodo.

Fredric Yeung
fonte
0

Para a conversão para obter os valores hexadecimais em SQL de volta para ASCII, aqui está a solução que encontrei (usando a função do caos do usuário para codificar em hexadecimal)

function hexEncode($data) {
    if(is_numeric($data))
        return $data;
    $unpacked = unpack('H*hex', $data);
    return '0x' . $unpacked['hex'];
}

function hexDecode($hex) {
    $str = '';
    for ($i=0; $i<strlen($hex); $i += 2)
        $str .= chr(hexdec(substr($hex, $i, 2)));
    return $str;
}

$stringHex = hexEncode('Test String');
var_dump($stringHex);
$stringAscii = hexDecode($stringHex);
var_dump($stringAscii);
Bim
fonte
0

É melhor também escapar das palavras reservadas do SQL. Por exemplo:

function ms_escape_string($data) {
    if (!isset($data) or empty($data))
        return '';

    if (is_numeric($data))
        return $data;

    $non_displayables = array(
        '/%0[0-8bcef]/',        // URL encoded 00-08, 11, 12, 14, 15
        '/%1[0-9a-f]/',         // url encoded 16-31
        '/[\x00-\x08]/',        // 00-08
        '/\x0b/',               // 11
        '/\x0c/',               // 12
        '/[\x0e-\x1f]/',        // 14-31
        '/\27/'
    );
    foreach ($non_displayables as $regex)
        $data = preg_replace( $regex, '', $data);
    $reemplazar = array('"', "'", '=');
    $data = str_replace($reemplazar, "*", $data);
    return $data;
}
Alex360
fonte
-1

Tenho usado isso como uma alternativa de mysql_real_escape_string():

function htmlsan($htmlsanitize){
    return $htmlsanitize = htmlspecialchars($htmlsanitize, ENT_QUOTES, 'UTF-8');
}
$data = "Whatever the value's is";
$data = stripslashes(htmlsan($data));
Safeer Ahmed
fonte
-2

Você poderia rolar sua própria versão mysql_real_escape_string, (e melhorá-lo) com a seguinte expressão regular: [\000\010\011\012\015\032\042\047\134\140]. Isso cuida dos seguintes caracteres: nulo, backspace, tabulação horizontal, nova linha, retorno de carro, substituto, aspas duplas, aspas simples, barra invertida, acento grave. Backspace e tabulação horizontal não são suportados por mysql_real_escape_string.

Scott
fonte