Jak uniknąć ciągów znaków w SQL Server za pomocą PHP?

89

Szukam alternatywy mysql_real_escape_string()dla SQL Server. Czy addslashes()moja najlepsza opcja, czy jest inna alternatywna funkcja, której można użyć?

Przydałaby się również alternatywa dla mysql_error().

Kliknij opcję Głos za głosem
źródło
2
Dla mnie to nie jest zduplikowane pytanie, ponieważ dotyczy konkretnego przypadku MSSQL, który nie ma powiązanego oficjalnego PDO
Pierre de LESPINAY

Odpowiedzi:

74

addslashes()nie jest w pełni adekwatna, ale pakiet mssql PHP nie zapewnia żadnej przyzwoitej alternatywy. Brzydkim, ale w pełni ogólnym rozwiązaniem jest kodowanie danych jako szesnastkowy bajt bajtowy, tj

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

W skrócie, byłoby to:

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()odpowiednik jest mssql_get_last_message().

chaos
źródło
1
Ups, jego SELECT SCOPE_IDENTITY ()!
Bryan Rehbein
4
@genio: Mmm, świetnie, z wyjątkiem tego, że tak jest. Nie sądzę, żebyś wyjaśnił, co uważasz za problem?
chaos
3
Czy próbowałeś tego z kolumnami z datą i godziną? Otrzymuję ten błąd: SQLSTATE[22007]: Invalid datetime format: 210 [Microsoft][ODBC SQL Server Driver][SQL Server]Conversion failed when converting datetime from binary/varbinary string.uważam, że ta metoda może być poprawna tylko wtedy, gdy działa z każdym typem danych MSSQL.
Alejandro García Iglesias
1
Treść mssql_escape()zwróconej funkcji nie robi tego za mnie. Tekst wyświetlany po dokonaniu wyboru wygląda tak, że jest 0x4a2761696d65206269656e206c652063686f636f6c6174nieczytelny.
Jeff Noel
3
@JeffNoel Prawdopodobnie zawijasz ciąg w pojedyncze cudzysłowy lub podwójne cudzysłowy. Ponieważ element jest zapisywany w postaci szesnastkowej, cudzysłowy nie są konieczne. SQL Server powinien przekonwertować wartość szesnastkową na coś zrozumiałego dla db.
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;
    }

Część kodu została wyrwana z CodeIgniter. Działa dobrze i jest czystym rozwiązaniem.

EDYCJA: istnieje wiele problemów z powyższym fragmentem kodu. Nie używaj tego bez czytania komentarzy, aby wiedzieć, co to jest. Jeszcze lepiej, proszę, w ogóle tego nie używaj. Sparametryzowane zapytania to Twoi przyjaciele: http://php.net/manual/en/pdo.prepared-statements.php

genio
źródło
1
Dlaczego potrzebujesz preg_replace? Czy to nie str_replacewystarczy?
Gabe
gabe: Preg_replace w tym przypadku miało pozwolić mi na użycie zakresów udostępnionych mi w klasach znaków wyrażeń regularnych. W przeciwnym razie byłoby znacznie więcej zamienników ciągów.
genio
7
-1. Funkcja cytowania nie jest odpowiedzialna za manipulowanie danymi - wystarczy upewnić się, że łańcuch jest w takim formacie, aby można go było dodać do instrukcji SQL i przetrwać bez modyfikacji.
cHao
6
Niestety, ale to jest złe od pierwszej linii kodu - empty($value)powróci truenie tylko '', ale także null, 0a '0'! We wszystkich tych przypadkach zwróciłbyś pusty ciąg.
Nux
Głosowałem za tym, myślę, że jest to całkowicie dobra funkcja, o ile jesteś w pełni świadomy powyższych problemów. Nazwałbym to jednak ms_escape_and_strip_string, więc każdy inny pracujący nad tym zobaczyłby, że wykonuje oba te zadania. Posiadanie pustego ciągu zwracanego w wielu przypadkach jest w porządku, o ile go uwzględnisz, chyba że brakuje mi tutaj ważniejszego punktu. Jeśli to nie odpowiada Twoim potrzebom, zawsze możesz wyjąć tę linię i zastąpić ją logiką, która odpowiada Twoim potrzebom.
NateDSaint
16

Dlaczego miałbyś zawracać sobie głowę ucieczką przed czymkolwiek, skoro możesz używać parametrów w zapytaniu ?!

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

Działa poprawnie w zaznaczeniach, usuwaniu i aktualizacjach, niezależnie od tego, czy parametry wartości są, nullczy nie. Kieruj się zasadą - nie łącz SQL, a zawsze będziesz bezpieczny, a Twoje zapytania będą czytane znacznie lepiej.

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

Konstantin
źródło
To jest właściwe podejście. Należy zawsze używać parametrów, a nie zapytań ad hoc. Jednak OP nie używa sterowników sqlsrv. Używa sterowników mssql. więc link do użycia dla tych z was, którzy utknęli przy sterownikach sqlsrv to http://php.net/manual/en/function.mssql-query.php .
smulholland2
1
@ smulholland2 Czy chodziło Ci o „lub tych z was, którzy utknęli przy sterownikach MSSQL
Konstantin
Jednym ze scenariuszy może być wygenerowanie pliku instrukcji INSERT do użycia w scenariuszu migracji danych.
Gary Reckard
11

Możesz zajrzeć do Biblioteki PDO . Możesz używać przygotowanych instrukcji z PDO, które automatycznie usuwają wszelkie złe znaki w łańcuchach, jeśli poprawnie wykonasz przygotowane instrukcje. Myślę, że to tylko dla PHP 5.

Alex
źródło
Z niektórymi niedocenianymi zachowaniami, jakie widziałem w PDO, musiałbym przeprowadzić poważne testy, zanim zaufałem temu, aby prawidłowo uciec przed wszystkimi danymi.
chaos
@Chaos Naprawdę? Nie jestem tego świadomy… czy masz link do artykułu?
Alex
Myślałem o problemach, jakie ten facet tutaj miał wczoraj z PDO. Niepowiązane transakcje, ale nie imponujące. Połącz to z całą historią nieadekwatnych ucieczek danych w PHP (php.net mówi ludziom, aby używali addlashes ()!) I robię się bardzo podejrzany.
chaos
2
Uwielbiam PDO i wypróbowałem to najpierw, ale ten dla MSSQL (na Unix, oparty na dblib) czasami zawodzi u mnie (błąd segmentacji), dlatego uciekłem się do mssql_escape zdefiniowanego powyżej.
Lapo
Dzięki za komentarz @Iapo. Rozważałem przejście na PDO dla projektu mssql - szczególnie w celu ucieczki - ale zaoszczędziłeś mi kłopotów.
Winfield Trail,
4

Innym sposobem obsługi pojedynczych i podwójnych cudzysłowów jest:

function mssql_escape($str)
{
    if(get_magic_quotes_gpc())
    {
        $str = stripslashes($str);
    }
    return str_replace("'", "''", $str);
}
Raja Bilal Ahmed
źródło
get_magic_quites_gpc zostało WYCOFANE z PHP 5.3.0 i USUNIĘTE od PHP 5.4.0. Zobacz php.net/manual/en/info.configuration.php#ini.magic-quotes-gpc
sty
2

Aby uniknąć apostrofów i cudzysłowów, musisz je podwoić:

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

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

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

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

itp...

i przypisanie: znak ucieczki w Microsoft SQL Server 2000

marklark
źródło
2

Po wielu godzinach zmagań się z tym, znalazłem rozwiązanie, które wydaje się prawie najlepsze.

Odpowiedź Chaosa dotycząca konwersji wartości na ciąg szesnastkowy nie działa z każdym typem danych, szczególnie z kolumnami z datą i godziną.

Używam PHP PDO::quote(), ale ponieważ pochodzi z PHP, PDO::quote()nie jest obsługiwany przez MS SQL Server i zwraca FALSE. Rozwiązaniem do jego działania było pobranie niektórych pakietów Microsoft:

Następnie możesz połączyć się w PHP z PDO używając DSN, jak w poniższym przykładzie:

sqlsrv:Server=192.168.0.25; Database=My_Database;

Używanie parametrów UIDi PWDw DSN nie zadziałało, więc nazwa użytkownika i hasło są przekazywane jako drugi i trzeci parametr w konstruktorze PDO podczas tworzenia połączenia. Teraz możesz używać PHP PDO::quote(). Cieszyć się.

Alejandro García Iglesias
źródło
0

Ostrzeżenie: ta funkcja została USUNIĘTA w PHP 7.0.0.

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

Każdy, kto nadal używa tych funkcji mssql_ *, powinien pamiętać, że zostały one usunięte z PHP w wersji 7.0.0. Oznacza to, że w końcu będziesz musiał przepisać kod modelu, aby użyć biblioteki PDO, sqlsrv_ * itd. Jeśli szukasz czegoś z metodą „cytowania / ucieczki”, polecam PDO.

Alternatywy dla tej funkcji to: PDO :: query (), sqlsrv_query () i odbc_exec ()

jjwdesign
źródło
0

Jeśli używasz PDO, możesz użyć tej PDO::quotemetody.

Fredric Yeung
źródło
0

W celu konwersji, aby uzyskać wartości szesnastkowe w SQL z powrotem do ASCII, oto rozwiązanie, które otrzymałem (używając funkcji od chaosu użytkownika do kodowania na szesnastkowy)

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
źródło
0

Lepiej jest też unikać słów zastrzeżonych SQL. Na przykład:

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
źródło
-1

Używam tego jako alternatywy dla 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));
Bezpieczniejszy Ahmed
źródło
-2

Można toczyć własną wersję mysql_real_escape_string(i poprawić na nim) za pomocą następującego wyrażenia regularnego: [\000\010\011\012\015\032\042\047\134\140]. To zajmuje się następującymi znakami: null, backspace, tabulator poziomy, nowa linia, powrót karetki, zamiana, podwójny cudzysłów, pojedynczy cudzysłów, ukośnik odwrotny, akcent słaby. Backspace i tabulator poziomy nie są obsługiwane przez mysql_real_escape_string.

Scott
źródło