![](https://i-blog.csdnimg.cn/blog_migrate/c95c1f1945e59dc0b00c0b3c94dfff5c.png)
在过去的几周中,我一直在撰写有关如何保护数据库免受SQL注入攻击的文章 。 今天,我们将通过研究隐式unicode转换如何使您的数据易受攻击来保持趋势。
![](https://i-blog.csdnimg.cn/blog_migrate/53e28c6e25f95fab1e781bffa33b6029.jpeg)
您也可以在我的 YouTube 频道 上观看此内容 。
什么是象形文字?
同源字符是看起来像另一个字符的字符。 l(小写字母“ L”)和1(数字)被视为单字形。 O(字母)和0(数字)也是如此。
同形异义字可以存在于字符集中(例如上述拉丁字符集示例),也可以存在于字符集之间 。 例如,您可能有Unicode撇号ʼ,它是拉丁单引号字符'
的同形符号。
SQL Server如何处理Unicode象形文字?
你应该问有趣。 如果将Unicode字符传递给非Unicode数据类型(如char),则SQL会将Unicode字符隐式转换为与其最相似的非Unicode象形文字。
为了了解这一点,我们可以使用上例中的Unicode撇号:
SELECT
CAST(N'ʼ' AS nchar) AS UnicodeChar,
CAST(N'ʼ' AS char) AS NonUnicodeChar
您可以在第二列中看到SQL自动将撇号转换为单引号:
![](https://i-blog.csdnimg.cn/blog_migrate/50c0334d6c9383354c231f38ddc281c8.png)
尽管这种隐式字符转换可以方便地显示非Unicode字符集中的Unicode字符,但对于SQL Server 安全性却可能造成灾难。
Unicode象形文字SQL注入
如果在构建动态SQL查询时已经在使用sp_executesql或QUOTENAME() ,则可以避免这种SQL注入。
我知道当拥有上述可靠,安全和经过测试的功能时,您不是那种会编写自己的安全功能的人。 但是,仅此一次,让我们假装您认为您可以编写自己的引号分隔代码来胜过黑客。
使用与上周相同的数据集 ,让我们创建一个新的存储过程,该过程将从用户的配置文件中返回一些数据:
DROP PROCEDURE IF EXISTS dbo.GetProfile
GO
CREATE PROCEDURE dbo.GetProfile
@Username nvarchar(100)
AS
BEGIN
-- Add quotes to escape injection...or not?
SET @Username = REPLACE( @Username , '''','''''')
DECLARE @Query varchar(max)
SET @Query = 'SELECT
FullName,
JoinDate
FROM
dbo.RegisteredUser
WHERE
UserName = ''' + @Username + '''
'
EXEC( @Query )
END
GO
代替使用sp_executesql或QUOTENAME(),让我们尝试编写自己的巧妙的REPLACE()函数,该函数将用两套单引号替换单引号。 从理论上讲,这应该防止SQL注入。
如果我们测试SQL注入的“正常”尝试,您会注意到此逻辑很好用。 拍拍自己的背部!
![](https://i-blog.csdnimg.cn/blog_migrate/a316b6f2d7a3cfe00cf8498f6db80390.png)
但是,如果我们将Unicode撇号传递给……:
![](https://i-blog.csdnimg.cn/blog_migrate/e581ffd42e5061c75fa496cdfff69f50.png)
发生这种情况的原因是因为我们将@Query参数声明为varchar而不是unicode nvarchar。 当我们构建动态SQL语句时,SQL 会将 nvarchar @Username参数隐式转换为非Unicode varchar:
![](https://i-blog.csdnimg.cn/blog_migrate/0f40e2e8db977b4c7b68cb9914363959.png)
因此,如果我替换撇号,那会使我安全吗?
没有。
我知道,黑名单/替换Unicode撇号似乎可以解决我们所有的问题。
而且它将……仅在这种情况下。 但是,除了单引号之外,还有更多的同名同音字。
出于好奇,我编写了一个脚本来搜索Unicode字符空间,以查看还存在其他同形文字:
DECLARE @FirstNumber INT=0;
-- number of possible characters in the unicode space
DECLARE @LastNumber INT=1114112;
WITH Numbers AS (
SELECT @FirstNumber AS n
UNION ALL
SELECT n+1 FROM Numbers WHERE n+1<= @LastNumber
), UnicodeConversion AS (
SELECT
n AS CharacterNumber,
CASE CAST(NCHAR(n) as CHAR(1))
WHEN '''' THEN NCHAR(n)
WHEN ';' THEN NCHAR(n)
END AS UnicodeCharacter,
CAST(NCHAR(n) as CHAR(1)) AS ASCIICharacter
FROM Numbers
)
SELECT
*
FROM
UnicodeConversion
WHERE
UnicodeCharacter IS NOT NULL
OPTION (MAXRECURSION 0)
![](https://i-blog.csdnimg.cn/blog_migrate/f05710250484e88047c1dc1894785d35.png)
尽管上面的屏幕截图中的字符看起来很相似,但它们实际上是同形文字。
我决定只搜索单引号和分号,因为它们经常在SQL注入攻击中使用,但这绝不是要列入黑名单的所有字符的详尽列表。
要把所有危险的象形文字都可靠地列入黑名单不仅非常困难,而且还要一直在Unicode中添加新字符,因此维护黑名单将是一场噩梦。 特别是如果将来维护此代码的人员对这些类型的注入攻击不熟悉。
并且不要厚颜无耻地认为您也可以过滤掉危险的SQL关键字-即使您替换(@ Username,'SELECT',”),也要记住有人可以通过并传递“ŚεℒℇℂƮ”之类的值。
结论
不要编写自己的安全功能-它们会失败。
防止SQL注入的最佳方法是不使用动态SQL。 如果必须使用动态SQL,请使用sp_executesql和QUOTENAME()。
您可能还喜欢 在Twitter上关注我 。
![](https://upscri.be/media/form.jpg)