I understand that the QUOTENAME function can be used to add square brackets ([], the default behaviour) or some other character wrapper to strings. It doesn't work for longer strings (over 128 characters).
So... Why/ when would you use it instead of the more conventional and far more easily readable string concatenation?
Why would you not just concatenate a single quote or a square bracket at the beginning and end of a term and use this function instead?
It is/was specifically designed for quoting column/table/database names – sysnames. For example, this: SELECT QUOTENAME('abc[]def') returns [abc[]]def] while SELECT '[' + 'abc[]def' + ']' returns [abc[]def] which is invalid to use as a column/table/database name.
Additionally, the SQL-99 standard is to quote by using a single quote character, and while current versions of Sql Server continue to use brackets, it may in the future (or be configurable to be) use the SQL-99 standard. In which case, all code using QUOTENAME will continue to function correctly, while code that tries to do its own escaping will fail.
There is more subtle implications as well. Since QUOTENAME has the exact same limitations as sysname, should Microsoft ever decide to change sysname to be longer than 128 characters (256 maybe? 32767 maybe?), it would be assumed that QUOTENAME would then also be able to handle these increased sizes. Using QUOTENAME is a safe(r) way of taking a column name from a possibly untrusted source and using it as a sysname – no matter the current/future database settings without having to worry about the edge cases (like ] or ' inside the input) and whether it will allow the string to break out of the column name to create SQL injection attacks. I probably wouldn't depend solely on this feature for security, but to be used in one of many layers of protection.
You use QUOTENAME() mainly when you need to build dynamic sql. Dynamic SQL is something you should avoid as much as possible, but in rare situations it can be the best way to solve a problem. When building dynamic SQL, QUOTENAME() is the correct way to ensure table and column name variables that may include unwanted characters like spaces won't cause problems with your final SQL statement.
QuoteName is primarily designed for SYSNAME like datatypes. This sysname datatype is unicode of 128 characters which is NVARCHAR(128). Hence if it is more than 128 characters you need to use traditional way of concatenating. But we can use QUOTENAME for varchar, nvarchar and sysname datatypes..
declare #test NVARCHAR(1000) = replicate('a',500)
declare #testsysname sysname = replicate('a',500)
select QUOTENAME(#test) -- this returns null
select QUOTENAME(#testsysname) --this displays only for 128 character with brackets
Related
I'm trying to declare a variable like #wi-fi in SQL Server but dash character in not allowed. There are other characters like / * - ( ) with same problem. I know for table or column name we can put it in the [] but what about declaring variables? is there a solution?
I have already searched the web but couldn't find any way.
Local variable names must comply with the rules for identifiers. MSDN says that :
When identifiers are used in Transact-SQL statements, the identifiers that do not comply with these rules must be delimited by double quotation marks or brackets.
But this rule is not valid for variables naming. In SQL Server there is a restriction on variable names. All variable names must begin with a single # sign. After that the variable name must follow the rules for identifiers and can contain a total of 128 characters.When we say characters, we mean that the name can contain letters, numbers, the # sign, the pound sign, the dollar sign and the underscore character. The variable name can not contain any dash or spaces.
READ MORE HERE
Some context to answer people such as #Alex who need to know the "why" for questions like this:
I ran into a similar problem, I needed to use just a small piece of a URL saved in my db where the front and ends were irrelevant.
I first attempted to use:
declare #variable varchar(250) = %x%;
SELECT * FROM tblone WHERE column1 LIKE '#variable'
however this returned error: Arithmetic overflow error converting numeric to data type varchar
My working query was formatted:
declare #variable varchar(1000) = x;
SELECT * FROM tblone WHERE column1 LIKE '%'+#variable+'%'
I have a case statement that will set a varchar variable #stored_proc_name to the name of a procedure that I want to call later on. One of the procedures I want to call takes a varchar as an argument (and also an int). How do I do this? Currently what I have is this
SELECT #stored_proc_name = CASE #p_process_name
WHEN 'misdate' THEN 'findb..sp_cycle_date_daily'
WHEN 'balpremmis' THEN 'findb..pc_bal_writ_prem'
WHEN 'ursctl' THEN 'MIS_feeds..pc_mis_update_feed_control "URSPO", 1'
ELSE NULL
END
EXECUTE #stored_proc_name
The last one is my procedure that takes arguments, where "URSPO" should be the first, and 1 is the int. It is running on SQL server 2k8 (I think I remember some difference with single vs. double quotes between this and older versions). Is what I have correct? I admit that I haven't tested it yet, but it is a part of a huge process that I really don't want to kick off right now.
Thank for any help!
To escape a single quote inside a string literal, use a double quote like below. See the Constants section in the BOL for more information:
select ' '' ' -- creates a string containing a space, a single quote, and another space
In your example, this would be the string:
'MIS_feeds..pc_mis_update_feed_control ''URSPO'', 1'
If you were going to use this string in a LIKE expression, you might want to refer to this question.
And if you were going to build a string to be a SQL identifier, you might want to escape it using the QUOTENAME function.
You can use the code below:
set quoted_identifier off;
Select "'Hi'"
quoted_identifiere will enable you to use ", instead of ';
I have recently taken on a project in which I need to integrate with PHP/SQL Server. I am looking for the quickest and easiest function to prevent SQL injection on SQL Server as I prefer MySQL and do not anticipate many more SQL Server related projects.
Is this function sufficient?
$someVal = mssql_escape($_POST['someVal']);
$query = "INSERT INTO tblName SET field = $someVal";
mssql_execute($query);
function mssql_escape($str) {
return str_replace("'", "''", $str);
}
If not, what additional steps should I take?
EDIT:
I am running on a Linux server - sqlsrv_query() only works if your hosting environment is windows
The best option: do not use SQL statements that get concatenated together - use parametrized queries.
E.g. do not create something like
string stmt = "INSERT INTO dbo.MyTable(field1,field2) VALUES(" + value1 + ", " + value2 + ")"
or something like that and then try to "sanitize" it by replacing single quotes or something - you'll never catch everything, someone will always find a way around your "safe guarding".
Instead, use:
string stmt = "INSERT INTO dbo.MyTable(field1,field2) VALUES(#value1, #value2)";
and then set the parameter values before executing this INSERT statement. This is really the only reliable way to avoid SQL injection - use it!
UPDATE: how to use parametrized queries from PHP - I found something here - does that help at all?
$tsql = "INSERT INTO DateTimeTable (myDate, myTime,
myDateTimeOffset, myDatetime2)
VALUES (?, ?, ?, ?)";
$params = array(
date("Y-m-d"), // Current date in Y-m-d format.
"15:30:41.987", // Time as a string.
date("c"), // Current date in ISO 8601 format.
date("Y-m-d H:i:s.u") // Current date and time.
);
$stmt = sqlsrv_query($conn, $tsql, $params);
So it seems you can't use "named" parameters like #value1, #value2, but instead you just use question marks ? for each parameter, and you basically just create a parameter array which you then pass into the query.
This article Accessing SQL Server Databases with PHP might also help - it has a similar sample of how to insert data using the parametrized queries.
UPDATE: after you've revealed that you're on Linux, this approach doesn't work anymore. Instead, you need to use an alternate library in PHP to call a database - something like PDO.
PDO should work both on any *nix type operating system, and against all sorts of databases, including SQL Server, and it supports parametrized queries, too:
$db = new PDO('your-connection-string-here');
$stmt = $db->prepare("SELECT priv FROM testUsers WHERE username=:username AND password=:password");
$stmt->bindParam(':username', $user);
$stmt->bindParam(':password', $pass);
$stmt->execute();
No, it's not sufficient. To my knowledge, string replacement can never really be sufficient in general (on any platform).
To prevent SQL injection, all queries need to be parameterized - either as parameterized queries or as stored procedures with parameters.
In these cases, the database calling library (i.e. ADO.NET and SQL Command) sends the parameters separately from the query and the server applies them, which eliminates the ability for the actual SQL to be altered in any way. This has numerous benefits besides injection, which include code page issues and date conversion issues - for that matter any conversions to string can be problematic if the server does not expect them done the way the client does them.
I partially disagree with other posters. If you run all your parameters through a function that double the quotes, this should prevent any possible injection attack. Actually in practice the more frequent problem is not deliberate sabotague but queries that break because a value legitimately includes a single quote, like a customer named "O'Hara" or a comment field of "Don't call Sally before 9:00". Anyway, I do escapes like this all the time and have never had a problem.
One caveat: On some database engines, there could be other dangerous characters besides a single quote. The only example I know is Postgres, where the backslash is magic. In this case your escape function must also double backslashes. Check the documentation.
I have nothing against using prepared statements, and for simple cases, where the only thing that changes is the value of the parameter, they are an excellent solution. But I routinely find that I have to build queries in pieces based on conditions in the program, like if parameter X is not null then not only do I need to add it to the where clause but I also need an additional join to get to the value I really need to test. Prepared statements can't handle this. You could, of course, build the SQL in pieces, turn it into a prepared statement, and then supply the parameters. But this is just a pain for no clear gain.
These days I mostly code in Java that allows functions to be overloaded, that is, have multiple implementations depending on the type of the passed in parameter. So I routine write a set of functions that I normally name simply "q" for "quote", that return the given type, suitably quoted. For strings, it doubles any quote marks, then slaps quote marks around the whole thing. For integers it just returns the string representation of the integer. For dates it converts to the JDBC (Java SQL) standard date format, which the driver is then supposed to convert to whatever is needed for the specific database being used. Etc. (On my current project I even included array as a passed in type, which I convert to a format suitable for use in an IN clause.) Then every time I want to include a field in a SQL statement, I just write "q(x)". As this is slapping quotes on when necessary, I don't need the extra string manipulation to put on quotes, so it's probably just as easy as not doing the escape.
For example, vulnerable way:
String myquery="select name from customer where customercode='"+custcode+"'";
Safe way:
String myquery="select name from customer where customercode="+q(custcode);
The right way is not particularly more to type than the wrong way, so it's easy to get in a good habit.
String replacement to escape quotes IS sufficient to prevent SQL injection attack vectors.
This only applies to SQL Server when QUOTED_IDENTIFIER is ON, and when you don't do something stoopid to your escaped string, such as truncating it or translating your Unicode string to an 8-bit string after escaping. In particular, you need to make sure QUOTED_IDENTIFIER is set to ON. Usually that's the default, but it may depend on the library you are using in PHP to access MSSQL.
Parameterization is a best practice, but there is nothing inherently insecure about escaping quotes to prevent SQL injection, with due care.
The rel issue with escaping strings is not the efficacy of the replacement, it is the potential for forgetting to do the replacement every time.
That said, your code escapes the value, but does not wrap the value in quotes. You need something like this instead:
function mssql_escape($str) {
return "N'" + str_replace("'", "''", $str) + "'";
}
The N above allows you to pass higher Unicode characters. If that's not a concern (i.e., your text fields are varchar rather than nvarchar), you can remove the N.
Now, if you do this, there are some caveats:
You need to make DAMNED SURE you call mssql_escape for every string value. And therein lies the rub.
Dates and GUID values also need escaping in the same manner.
You should validate numeric values, or at least escape them as well using the same function (MSSQL will cast the string to the appropriate numeric type).
Again, like others have said, parameterized queries are safer--not because escaping quotes doesn't work (it does except as noted above), but because it's easier to visually make sure you didn't forget to escape something.
I remember when I was working with PHP several years back I could blow up my application by naming a MySQL column 'desc' or any other term that was used as an operator.
So, in general are there names I should avoid giving my table columns?
As long as you surround every column name with '[' and ']', it really doesn't matter what you use. Even a space works (try it: [ ]).
Edit: If you can't use '[' and ']' in every case, check the documentation for characters that are not allowable as well as keywords that are intrinsic to the system; those would be out of bounds. Off the top of my head, the characters allowed (for SqlServer) for an identifier are: a-z, A-Z, 0-9, _, $, #.
in general don't start with a number, don't use spaces, don't use reserved words and don't use non alphanumeric characters
however if you really want to you can still do it but you need to surround it with brackets
this will fail
create table 1abc (id int)
this will not fail
create table [1abc] (id int)
but now you need to use [] all the time, I would avoid names as the ones I mentioned above
Check the list of reserved keywords as indicated in other answers.
Also avoid using the "quoting" using quotes or square brackets for the sake of having a space or other special character in the object name. The reason is that when quoted the object name becomes case sensitive in some database engines (not sure about MSSQL though)
Some teams use the prefix for database objects (tables, views, columns) like T_PERSON, V_PERSON, C_NAME etc. I personally do not like this convention, but it does help avoiding keyword issues.
You should avoid any reserved SQL keywords (ex. SELECT) and from a best practices should avoid spaces.
Yes, and no.
Yes, because it's annoying and confusing to have names that match keywords, and that you have to escape in funny ways (when you're not consistently escaping)
and No, because it's possible to have any sequence of characters as an identifier, if you escape it properly :)
Use [square brackets] or "double quotes" to escape multi-word identifiers or keywords, or even names that have backslashes or any other slightly odd character, if you must.
Strictly speaking, there's nothing you can't name your columns. However, it will make your life easier if you avoid names with spaces, SQL reserved words, and reserved words in the language you're programming in.
You can use pretty much anything as long as you surround it with square brackets:
SELECT [value], [select], [insert] FROM SomeTable
I however like to avoid doing this, partly because typing square brackets everywhere is anoying and partyly because I dont generally find that column names like 'value' particularly descriptive! :-)
Just stay away from SQL keywords and anything which contains something other than letters and you shouldn't need to use those pesky square brackets.
You can surround a word in square brackets [] and basically use anything you'd like.
I prefer not to use the brackets, and in order to do so you just have to avoid reserved words.
MS SQL Server 2008 has these reserved words
Beware of using square brackets on updates, I had a problem using the following query:
UPDATE logs SET locked=1 WHERE [id] IN (SELECT [id] FROM ids)
This caused all records to be updated, however, this appears to work fine:
UPDATE logs SET locked=1 WHERE id IN (SELECT [id] FROM ids)
Note that this problem appears specific to updates, as the following returns only the rows expected (not all rows):
SELECT * FROM logs WHERE [id] IN (SELECT [id] FROM ids)
This was using MSDE 2000 SP3 and connecting to the database using MS SQL (2000) Query Analyzer V 8.00.194
Very odd, possibly related to this Knowledgebase bug http://support.microsoft.com/kb/140215
In the end I just removed all the unnecessary square brackets.
Like:
insert into table (col) values (N'multilingual unicode strings')
I'm using SQL Server 2008 and I already use nVarChar as the column data type.
You need the N'' syntax only if the string contains characters which are not inside the default code page. "Best practice" is to have N'' whenever you insert into an nvarchar or ntext column.
Yes, you do if you have unicode characters in the strings.
From books online (http://msdn.microsoft.com/en-us/library/ms191313.aspx)...
"Unicode string constants that appear in code executed on the server, such as in stored procedures and triggers, must be preceded by the capital letter N. This is true even if the column being referenced is already defined as Unicode. Without the N prefix, the string is converted to the default code page of the database. This may not recognize certain characters. The requirement to use the N prefix applies to both string constants that originate on the server and those sent from the client."
It is preferable for compatibility sake.
Best practice is to use parameterisation in which case you don't need the N prefix.