Pet*_*ier 2 sql-server sql-injection powershell
我很懒惰,宁愿参数化一次(以获得名称的 object_id)并Invoke-SqlCmd
用于其余工作。
给定一系列用于处理任意服务器对象的函数,下面的测试是否足以确认 CmdletBinding 输入参数Int32
防止通过用户提供的方式注入$object_id
?
function foo {
[CmdletBinding()]Param(
[Int32]$object_id
)
$query = "select type_desc from sys.objects where object_id = $object_id;"
Invoke-Sqlcmd -ServerInstance "localhost" -Database "tempdb" -Query $query
}
foo 3
foo "0 union all select name from sys.syslogins where sid = 0x01"
foo $null
foo ([math]::Pow(2,31)+1)
foo @(1,2)
Run Code Online (Sandbox Code Playgroud)
你可以,但这样做有问题。
首先,它仍然不是一个好主意,因为该模式不可扩展。它仅适用于某些数据类型。一旦你有了一个字符串参数,你就回到了注入的地方。这也意味着您必须关心某些数据类型(字节数组、GUID、日期时间)如何转换为字符串。
您还必须注意,空值参数将被静默修改,0
因为[int32]
它是不可为空的类型。您必须指定[nullable[int32]]
以防止这种行为。即便如此,由于转换为字符串的空值将导致空字符串,因此您会得到一个更具描述性的参数错误。
你更愿意调试这个:
Invoke-Sqlcmd:';' 附近的语法不正确。
或这个:
使用“1”参数调用“填充”的异常:“参数化查询 '(@object_id int)select type_desc from sys.objects where object_i' 需要参数 '@object_id',该参数未提供。”
最后,如果您要大量使用查询,系统可能更难重用查询计划。
一旦你习惯了它真的不难指定:
function foo {
[CmdletBinding()]
Param(
$object_id
)
$Query = 'select type_desc from sys.objects where object_id = @object_id;'
$ConnectionString = 'Data Source={0};Initial Catalog={1};Integrated Security=SSPI' -f 'localhost', 'tempdb'
$SqlConnection = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $ConnectionString
$SqlCommand = New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $Query, $SqlConnection
$SqlCommand.Parameters.Add('@object_id', [System.Data.SqlDbType]::Int).Value = $object_id
$SqlDataAdapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList $SqlCommand
$DataTable = New-Object -TypeName System.Data.DataTable -ArgumentList 'objects'
[void]$SqlDataAdapter.Fill($DataTable)
$SqlConnection.Dispose()
, $DataTable
}
Run Code Online (Sandbox Code Playgroud)
[如果你只输出 DataRows 没问题,你可以去掉最后一行的逗号。逗号强制函数输出整个数据表。如果没有逗号,您将获得 DataRows 流。]
它看起来很复杂,但你可以一遍又一遍地重复使用这个模式。我基本上是从我自己的脚本中复制了上面的代码。
如果您关心的是代码部署,那么我认为这种方法仍然更好。上面的代码只需要 .Net Framework。事实上,该代码甚至可以在 PowerShell Core 6.0 上运行(当然,您必须调用(foo <value>).Rows
以获得等效的输出,因为 Core 尚未学习如何在命令行中显示数据表)。UsingInvoke-Sqlcmd
添加了对SqlServer
正在安装的PowerShell 模块的依赖。当然,它是第一方模块,但默认情况下它仍然不随 Windows 一起提供。您肯定会失去其他人在没有 的情况下完成封装的一些好处Invoke-Sqlcmd
,但仍然需要权衡。