Eu sou preguiçoso e prefiro parametrizar uma vez (para obter um object_id para um nome) e usar Invoke-SqlCmd
para o resto do trabalho.
Dada uma série de funções destinadas a trabalhar com um objeto de servidor arbitrário, o teste abaixo é suficiente para confirmar que CmdletBinding um parâmetro de entrada Int32
impede a injeção por meio do $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)
Você pode, mas há problemas em fazer isso.
Principalmente, ainda não é uma ótima ideia porque o padrão não é extensível. Funciona apenas para determinados tipos de dados. Assim que você tiver um parâmetro de string, você estará de volta onde estava com a injeção. Isso também significa que você precisa se preocupar com como certos tipos de dados (matrizes de bytes, GUIDs, datas) são convertidos em strings.
Você também deve estar ciente de que os parâmetros de valor nulo serão modificados silenciosamente,
0
pois[int32]
é um tipo não anulável. Você teria que especificar[nullable[int32]]
para evitar esse comportamento. Mesmo assim, como os valores nulos que são convertidos em uma string resultarão em uma string vazia, você obtém um erro muito mais descritivo com um parâmetro.Você prefere depurar isso:
Ou isto:
Finalmente, se você for usar muito a consulta, pode ser mais difícil para o sistema reutilizar o plano de consulta.
Não é tão difícil especificar quando você está acostumado:
[Se você estiver bem com a saída de DataRows apenas, você pode remover a vírgula da última linha. A vírgula força a função a produzir o DataTable inteiro. Sem a vírgula, você obterá um fluxo de DataRows.]
Parece complicado, mas você pode reutilizar esse padrão várias vezes. Eu essencialmente copiei o código acima de meus próprios scripts.
Se sua preocupação é a implantação de código, eu diria que esse método ainda é melhor. O código acima requer apenas o .Net Framework. De fato, o código funciona até mesmo no PowerShell Core 6.0 (concedido, você precisa chamar
(foo <value>).Rows
para obter uma saída equivalente porque o Core não foi ensinado a exibir DataTables na linha de comando). O usoInvoke-Sqlcmd
adiciona uma dependência noSqlServer
módulo do PowerShell que está sendo instalado. Claro, é um módulo original, mas ainda não é fornecido com o Windows por padrão. Você definitivamente perde alguns benefícios do encapsulamento sendo feito por outros semInvoke-Sqlcmd
, mas ainda há uma troca.