94

I'm looking for the alternative of mysql_real_escape_string() for SQL Server. Is addslashes() my best option or there is another alternative function that can be used?

An alternative for mysql_error() would also be useful.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ali
  • 261,656
  • 265
  • 575
  • 769
  • 3
    For me it's not a duplicate question because it concerns the specific MSSQL case that doesn't have a related official PDO – Pierre de LESPINAY Mar 04 '13 at 16:26
  • [Function *mysql_real_escape_string()* was deprecated in PHP 5.5.0 and was removed in PHP 7.0.0](https://www.php.net/manual/en/function.mysql-real-escape-string.php). – Peter Mortensen Jul 15 '19 at 15:07

14 Answers14

75

addslashes() isn't fully adequate, but PHP's mssql package doesn't provide any decent alternative. The ugly but fully general solution is encoding the data as a hex bytestring, i.e.

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

Abstracted, that would be:

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() equivalent is mssql_get_last_message().

chaos
  • 122,029
  • 33
  • 303
  • 309
  • Do you know the equivalent for mysql_insert_id() by any chance? – Ali Feb 22 '09 at 12:20
  • Yeah, you do a SELECT @@IDENTITY. – chaos Feb 22 '09 at 12:24
  • Chaos, can you please give an example of an 'escape' function which would take a string as argument and return the hex alternative as a result using the method you gave? I'll accept your answer then – Ali Feb 22 '09 at 12:26
  • At the moment I'm not sure about the double/quote requirement for hex values and how numeric values would be handled by this method. An example of using the function to escape a value and putting that in a query will be ideal. Thanks in advance – Ali Feb 22 '09 at 12:27
  • 1 question, the value doesn't need to be enclosed by quotes? Also... have you done any testing on how this works with all character encoding types etc? – Ali Feb 22 '09 at 12:33
  • Nope. It's a hex-string literal; if you enclosed it in quotes, you would be inserting the string '0xf00ba44' or whatever into your database instead of the original data. I haven't tested it with different encodings and I suggest anyone using this code do so before doing anything important with it. – chaos Feb 22 '09 at 12:37
  • You should use SELECT IDENTITY_SCOPE(); from within your same connection to retrieve your ID field instead of @@IDENTITY - it is more reliable. – Astra Mar 02 '09 at 19:47
  • 1
    Oops, its SELECT SCOPE_IDENTITY()! – Astra Mar 02 '09 at 19:47
  • 4
    @genio: Mmm, great, except it actually is. I don't suppose you'd explain what you consider to be the problem? – chaos Mar 28 '10 at 01:50
  • @chaos I'm late to the party, but I think Genio was of the impression he wanted to strip dangerous characters, thus his solution. I think yours fits what mysql_real_escape_string does better (at least as I understand it) – NateDSaint Nov 10 '11 at 14:58
  • @chaos Should this work with all SQL Server versions? I'm using SQL Server 2005 (Microsoft SQL Server Management Studio Express 9.00.2047.00) and it doesn't seem to be working. – Nalum Jan 31 '12 at 11:34
  • @Nalum: I don't know for certain, but the hexstring literal syntax is the only even slightly interesting thing this solution is using, and that comes from standard SQL, so I'd think they would've implemented it fairly early. Try doing like `SELECT 0x6869` or something to see if the bsaic concept is working, I guess. – chaos Feb 01 '12 at 03:35
  • @chaos Thanks for the response, it doesn't work, I just get the hexstring literal back. Will keep this in mind though. – Nalum Feb 01 '12 at 11:54
  • @Nalum: The only way that makes sense is if you're putting quotes around it. Is that the case? – chaos Feb 01 '12 at 14:20
  • @chaos No, I copied and pasted `SELECT 0x6869` into the code and also in SQL Server Management Studio Express and got the same result in both. So unless there is some setting in SQL Server that doesn't allow it I don't know why it's not working as you describe. – Nalum Feb 01 '12 at 16:42
  • @Nalum: Turns out that's just the GUI outsmarting itself. It's actually preserving your original specification format through the query. Make a table Foo with one nvarchar column in it and `INSERT INTO Foo VALUES (0x6869)`, then `SELECT * FROM Foo` and you'll see different results. – chaos Feb 01 '12 at 18:37
  • @chaos Thanks for all your help on this. When I do that I get a box character which I guess is to do with character encoding. I don't have any control over the database so don't think there is anything I can do about that. Again thanks for your help :) – Nalum Feb 03 '12 at 12:49
  • 3
    Have you tried this with datetime columns? I'm getting this error: `SQLSTATE[22007]: Invalid datetime format: 210 [Microsoft][ODBC SQL Server Driver][SQL Server]Conversion failed when converting datetime from binary/varbinary string.` I believe this method can be correct only if it works with every MSSQL datatype. – Alejandro García Iglesias Jun 19 '12 at 17:38
  • 1
    The content of the `mssql_escape()` function returned is not doing it for me. The text display after doing a select is looking like this `0x4a2761696d65206269656e206c652063686f636f6c6174` thus unreadable. – Jeff Noel Aug 14 '12 at 18:09
  • this does not work for date, datetime, or text format types. Any ideas on how to add those items to this funciton? I have a work aroudn fro date and datetime but no idea how to do text. – danielson317 Sep 18 '14 at 16:42
  • 3
    @JeffNoel Your probably wrapping the string single quotes or double quotes. Since the item is escaped into hex the quotes are not neccesary. SQL Server is supposed to convert the hex value to something the db understands. – danielson317 Sep 18 '14 at 16:44
  • I think better example is `SELECT cast(0x6869 as varchar(2)) as SayHI` – Saic Siquot Apr 21 '16 at 03:02
  • This can get you into trouble, and it does not work on all data types. Consider using parameterized queries. If you are looking to prevent SQL injection, @Konstantin has a more secure answer below. – smulholland2 Feb 28 '17 at 22:21
  • There is some critique of this approach in comments to [an answer to *How can I prevent SQL injection in PHP?*](https://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php/12710285#12710285) – Peter Mortensen Jul 15 '19 at 15:11
42
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;
    }

Some of the code here was ripped off from CodeIgniter. Works well and is a clean solution.

EDIT: There are plenty of issues with that code snippet above. Please don't use this without reading the comments to know what those are. Better yet, please don't use this at all. Parameterized queries are your friends: http://php.net/manual/en/pdo.prepared-statements.php

genio
  • 874
  • 6
  • 7
  • 1
    Why do you need the `preg_replace`? Isn't the `str_replace` sufficient? – Gabe Mar 26 '10 at 21:05
  • gabe: The preg_replace in this case was to allow me to use the ranges afforded to me in regular expressions character classes. There would be a lot more string replaces in this one otherwise. – genio Mar 26 '10 at 21:07
  • 8
    -1. It is not the responsibility of a quoting function to mangle data -- all it should do is make sure the string is in such a format that it can be added to an SQL statement and survive unmodified. – cHao Jul 15 '10 at 06:38
  • 7
    Sorry, but this is wrong from the first line of code - `empty($value)` will return `true` not only for `''`, but also for `null`, `0` and `'0'`! You would return an empty string in all those cases. – Nux Dec 25 '10 at 13:45
  • 1
    I upvoted this, I think it's a perfectly fine function as long as you're fully aware of the issues above. I would call it ms_escape_and_strip_string though, so anyone else working on it would see it does both those tasks. Having an empty string returned in multiple cases is fine as long as you account for it, unless I'm just missing a greater point here. If this doesn't fit your needs you can always take that line out and replace it with logic that suits your needs. – NateDSaint Nov 10 '11 at 14:40
  • This doesn't cover all the url codes like the space = %20. – patricia Mar 30 '16 at 11:32
17

Why would you bother escaping anything when you can use parameters in your query?!

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

It works right in selects, deletes, updates regardless whether your values parameters are null or not. Make a matter of principle - Don't concatenate SQL and you are always safe and your queries read much better.

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

Konstantin
  • 3,294
  • 21
  • 23
  • This is the correct approach. You should always use parameters as opposed to ad hoc queries. However, the OP is not using the sqlsrv drivers. He is using mssql drivers. so the link to use for those of you stuck using sqlsrv drivers is [http://php.net/manual/en/function.mssql-query.php](http://php.net/manual/en/function.mssql-query.php). – smulholland2 Feb 28 '17 at 22:19
  • 2
    @smulholland2 Did you mean "or those of you stuck using _MSSQL_ drivers" – Konstantin Mar 01 '17 at 09:51
  • One scenario might be if you want to generate a file of INSERT statements to use in a data migration scenario. – Gary Reckard Apr 26 '19 at 18:42
11

You could look into the PDO Library. You can use prepared statements with PDO, which will automatically escape any bad characters in your strings if you do the prepared statements correctly. This is for PHP 5 only I think.

alex
  • 479,566
  • 201
  • 878
  • 984
  • With some of the halfassed behavior I've seen out of PDO, I'd have to do some serious testing before I trusted it to escape all data correctly. – chaos Feb 22 '09 at 12:11
  • @Chaos Really ? I'm unaware of this.. do you have a link to an article? – alex Feb 22 '09 at 12:14
  • What I was thinking of was the trouble this guy on here was having yesterday with PDO. Unrelated transaction stuff, but unimpressive. Combine that with all the history of inadequate data escaping in PHP (php.net telling people to use addslashes()!) and I get very suspicious. – chaos Feb 22 '09 at 12:22
  • 2
    I love PDO and tried that first, but the one for MSSQL (on Unix, based on dblib) sometimes fails on me (segmentation fault), that's why I resorted to the mssql_escape defined above. – lapo Sep 06 '11 at 20:58
  • Thanks for your comment, @Iapo. I was considering switching to PDO for a mssql project - specifically to get escaping - but you've saved me the trouble. – Winfield Trail Jul 24 '13 at 02:34
5

Another way to handle single and double quotes is:

function mssql_escape($str)
{
    if(get_magic_quotes_gpc())
    {
        $str = stripslashes($str);
    }
    return str_replace("'", "''", $str);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • get_magic_quites_gpc has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0. See http://php.net/manual/en/info.configuration.php#ini.magic-quotes-gpc – Jan Jan 19 '18 at 14:09
4

In order to escape single- and double-quotes, you have to double them up:

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

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

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

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

etc...

and attribution: Escape Character In Microsoft SQL Server 2000

marklark
  • 860
  • 1
  • 8
  • 18
2

After struggling with this for hours, I've come up with a solution that feels almost the best.

Chaos' answer of converting values to hexstring doesn't work with every datatype, specifically with datetime columns.

I use PHP's PDO::quote(), but as it comes with PHP, PDO::quote() is not supported for MS SQL Server and returns FALSE. The solution for it to work was to download some Microsoft bundles:

After that you can connect in PHP with PDO using a DSN like the following example:

sqlsrv:Server=192.168.0.25; Database=My_Database;

Using the UID and PWD parameters in the DSN didn't worked, so username and password are passed as the second and third parameters on the PDO constructor when creating the connection. Now you can use PHP's PDO::quote(). Enjoy.

Lance Roberts
  • 22,383
  • 32
  • 112
  • 130
Alejandro García Iglesias
  • 16,222
  • 11
  • 51
  • 64
1

Warning: This function was REMOVED in PHP 7.0.0.

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

For anyone still using these mssql_* functions, keep in mind that they have been removed from PHP as of v7.0.0. So, that means you eventually have to rewrite your model code to either use the PDO library, sqlsrv_* etc. If you're looking for something with a "quoting/escaping" method, I would recommend PDO.

Alternatives to this function include: PDO::query(), sqlsrv_query() and odbc_exec()

jjwdesign
  • 3,272
  • 8
  • 41
  • 66
1

An answer from 2009-02-22T121000 by user chaos doesn't fit all queries.

For example, "CREATE LOGIN [0x6f6c6f6c6f] FROM WINDOWS" will give you an exception.

PS: look at the SQL Server driver for PHP, http://msdn.microsoft.com/library/cc296181%28v=sql.90%29.aspx and the sqlsrv_prepare function, which can binds parameters.

PSS: Which also didn't help you with the query above ;)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
danechkin
  • 1,306
  • 8
  • 15
0

For the conversion to get the hexadecimal values in SQL back into ASCII, here is the solution I got on this (using the function from user chaos to encode into 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);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bim
  • 87
  • 1
  • 4
0

If you are using PDO, you can use the PDO::quote method.

Chris Forrence
  • 10,042
  • 11
  • 48
  • 64
0

It is better to also escape SQL reserved words. For example:

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;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
-1

I have been using this as an alternative of 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
  • 587
  • 6
  • 14
-2

You could roll your own version of mysql_real_escape_string, (and improve upon it) with the following regular expression: [\000\010\011\012\015\032\042\047\134\140]. That takes care of the following characters: null, backspace, horizontal tab, new line, carriage return, substitute, double quote, single quote, backslash, grave accent. Backspace and horizontal tab are not supported by mysql_real_escape_string.

Scott
  • 6,411
  • 6
  • 39
  • 43