Sql-Server

在 SQL Server 中,有沒有一種方法可以在不使用動態 SQL 的情況下以未知數列為中心?

  • September 10, 2021

基本上,如果我們有這個:

create table #temp ([key] nchar, [value] nvarchar(2));
insert #temp ([key], [value]) values
            ('a',   '1'),
            ('b',   '2'),
            ...
            ('z',   '26')

目標是得到一個看起來像這樣的字元串:

{"a":"1","b":"2",..."z":"26"}

這裡的問題是列的數量和名稱都是未知的。現在,有一些方法可以使用PIVOT動態 SQL 來做到這一點,但一般來說,如果您只是將動態 SQL 與一堆隨機數據一起使用,那麼它可能很容易被注入。在 Stack Overflow 和這個網站上,我看到一些問題的答案使用了這種組合,但他們要麼沒有提到注射,要麼聲稱它對注射免疫,這讓我有點不安。

那麼……有沒有辦法在 SQL Server 中沒有動態查詢的情況下做到這一點?SQL 注入並不總是立竿見影的。有時注入的程式碼在表中處於休眠狀態,等待在動態 SQL 查詢中使用。在不使用動態 SQL 的情況下如何做到這一點- 或者如果做不到這一點,至少有一種真正不易受到注入攻擊的方法嗎?


針對評論的兩點說明:

#1:至於是否PIVOT使用,就我個人而言,我真正想做的就是切換行和列並從中創建一個 JSON 對象。我有一個看起來有點類似於上面的表,至少在一種情況下,它被用來儲存 C# 字典中的鍵和值。但是,在使用時JsonConvert.DeserializeObject,我需要它看起來像這樣:

{
   "Key1": "Value1",
   "Key2": "Value2",
   ...
}

不是這樣的:

[{
   "Key": "Key1",
   "Value": "Value1",
}, {
   "Key": "Key2",
   "Value": "Value2"
}, {
   ...
}]

出於某些原因,如果可能的話,我希望在 SQL 中進行這種轉換。

#2:SQL 注入的問題是:當你連接值時,如果其中一個值中有一個whatever'; SELECT * FROM SomeOtherTable; --類型的腳本,即使腳本在最初插入時沒有執行(因為很好地使用了 SQL 參數和類似的預防措施),如果它只是盲目地連接到動態 SQL 查詢中,它仍然可以在以後執行。

如果不能完全防止這種情況,我將不得不改變主意並在 C# 中處理這個問題。我真的不能假設數據中沒有像那樣瘋狂的東西,所以我不願意將它連接到正在執行的東西中,除非有一種真正安全的方法(不僅僅是逃避滴答聲)這樣做。

如果您只想要一個字元串,則不需要PIVOT,也不需要動態確定列名。假設 SQL Server 2017 或更高版本:

;WITH x(y) AS 
(
 SELECT QUOTENAME([key], '"') + ':' + QUOTENAME([value], '"')
 FROM #temp
)
SELECT output = N'{' + STRING_AGG(y, ',') + '}' FROM x;

問題中定義的列最多限制為 2 個字元,但如果您可能有更多,正如 Charlieface 指出的那樣,QUOTENAME()限制為 128 個字元。在這種情況下,您需要更安全一些的東西,再次假設 SQL Server 2017 或更高版本:

;WITH x(y) AS 
(
 SELECT '"' + STRING_ESCAPE([key],   'json') + '":"' 
            + STRING_ESCAPE([value], 'json') + '"'
 FROM #temp
)
SELECT output = N'{' + STRING_AGG(y, ',') + '}' FROM x;

如果您使用的是舊版本的 SQL Server,則可以使用效率較低且更麻煩的方法FOR XML PATH(我將轉義不安全的 JSON 字元REPLACE作為讀者練習):

;WITH x(y) AS 
(
 SELECT ',' + QUOTENAME([key], '"') + ':' + QUOTENAME([value], '"')
 FROM #temp
)
SELECT output = N'{' + STUFF(
   (SELECT y FROM x FOR XML PATH, TYPE).value
   (N'.[1]', N'nvarchar(max)'), 1, 1, '') + '}';

至於保護自己免受 SQL 注入,我寫了幾篇文章:

引用自:https://dba.stackexchange.com/questions/299362