首先感谢你看这篇文章。如果我的格式错误,我是新来的,所以道歉。
我将SQL审计中的审计规范数据插入到SQL表中 - 问题是当我从PS运行语句时,当SQL输出遇到'例如
时它会失败CREATE LOGIN [TEST] WITH PASSWORD=N'******', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
'******'因报价而失败。
它失败的列是$row.statement
这是我的陈述:
# POWERSHELL SCRIPT TO PULL yesterday's AUDIT DATA FROM SELECTED SQL SERVERS
(Read from DBA..SQLAuditTargets)
#Proof of concept Auto Pull data
#Load SMO
#[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null;
#Load SQL Module
#Import-Module SqlPs | Out-Null
#Add-PSSnapin SqlServerCmdletSnapin100 | Out-Null
#Add-PSSnapin SqlServerProviderSnapin100 | Out-Null
# Set target Server/DB to write data to.
$TargetServerInstance=''
$TargetServerDB='DBA'
#Set CMS server source
$CMSSERVER='SQL1'
function Write-DataTable
{
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory=$true)] [string]$ServerInstance,
[Parameter(Position=1, Mandatory=$true)] [string]$Database,
[Parameter(Position=2, Mandatory=$true)] [string]$TableName,
[Parameter(Position=3, Mandatory=$true)] $Data,
[Parameter(Position=4, Mandatory=$false)] [string]$Username,
[Parameter(Position=5, Mandatory=$false)] [string]$Password,
[Parameter(Position=6, Mandatory=$false)] [Int32]$BatchSize=50000,
[Parameter(Position=7, Mandatory=$false)] [Int32]$QueryTimeout=0,
[Parameter(Position=8, Mandatory=$false)] [Int32]$ConnectionTimeout=15
)
$conn=new-object System.Data.SqlClient.SQLConnection
if ($Username)
{ $ConnectionString = "Server={0};Database={1};User ID={2};Password={3};Trusted_Connection=False;Connect Timeout={4}" -f $ServerInstance,$Database,$Username,$Password,$ConnectionTimeout }
else
{ $ConnectionString = "Server={0};Database={1};Integrated Security=True;Connect Timeout={2}" -f $ServerInstance,$Database,$ConnectionTimeout }
$conn.ConnectionString=$ConnectionString
try
{
$conn.Open()
$bulkCopy = new-object ("Data.SqlClient.SqlBulkCopy") $connectionString
$bulkCopy.DestinationTableName = $tableName
$bulkCopy.BatchSize = $BatchSize
$bulkCopy.BulkCopyTimeout = $QueryTimeOut
$bulkCopy.WriteToServer($Data)
$conn.Close()
}
catch
{
$ex = $_.Exception
Write-Error "$ex.Message"
continue
}
} #Write-DataTable
function Out-DataTable
{
[CmdletBinding()]
param([Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)] [PSObject[]]$InputObject)
Begin
{
$dt = new-object Data.datatable
$First = $true
}
Process
{
foreach ($object in $InputObject)
{
$DR = $DT.NewRow()
foreach($property in $object.PsObject.get_properties())
{
if ($first)
{
$Col = new-object Data.DataColumn
$Col.ColumnName = $property.Name.ToString()
$DT.Columns.Add($Col)
}
if ($property.IsArray)
{ $DR.Item($property.Name) =$property.value | ConvertTo-XML -AS String -NoTypeInformation -Depth 1 }
else { $DR.Item($property.Name) = $property.value }
}
$DT.Rows.Add($DR)
$First = $false
}
}
End
{
Write-Output @(,($dt))
}
} #Out-DataTable
###### Get Dynamic list of servers names to loop through.
#Change LIKE clause to signify Prod/dev etc.
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server="+$CMSServer+";Database=master;Integrated Security=True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "SELECT SQLName FROM DBA..SQLAuditTargets "
$SqlCmd.Connection = $SqlConnection
$SqlCmd.CommandTimeout = 15
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
#Initialize Array to hold new database objects
$iDatabases = @()
$servers = $DataSet.Tables[0] | % {([string]$_.SQLName).Trim()}
#Loop over each SQLName provided (SQLName is column in source table)
foreach ($instance in $servers)
{
try
{
"Connecting to $instance" | Write-Host -ForegroundColor green
############################
$DbQuery="DECLARE @auditfile varchar(200) SET @auditfile=(select log_file_path +LEFT(log_file_name, CHARINDEX('_',log_file_name))+'*.sqlaudit' from sys.server_file_audits where log_file_name LIKE '%DBA-SQL-Audit%')
SELECT
af.event_time, aa.class_desc, af.action_id, af.session_id, af.server_instance_name, af.database_name, af.server_principal_name, af.schema_name, af.object_name, af.statement, aa.containing_group_name, aa.covering_action_name
FROM sys.fn_get_audit_file(@auditfile, NULL, NULL) AS af INNER JOIN
sys.dm_audit_actions AS aa ON af.action_id = aa.action_id INNER JOIN
sys.dm_audit_class_type_map AS ctm ON aa.class_desc = ctm.securable_class_desc
WHERE (af.statement <> '')
AND (aa.class_desc <> 'SERVER')
AND (af.database_name <> 'master')
AND (ctm.class_type_desc = 'DATABASE')
AND (af.action_id IN ('AL', 'DR', 'CR', 'SVPD', 'SVSD', 'SVSR'))
AND (af.object_name NOT IN ('dbo', '', 'telemetry_xevents'))
AND af.Event_Time > '2018-02-27 00:00:28.0000000' and af.Event_Time < '2018-02-28 08:42:28.0000000'
GROUP BY
af.event_time, aa.class_desc, af.action_id, af.session_id, af.server_instance_name, af.database_name, af.server_principal_name, af.schema_name, af.object_name, af.statement, aa.containing_group_name, aa.covering_action_name
UNION
SELECT
af.event_time, aa.class_desc, af.action_id, af.session_id, af.server_instance_name, af.database_name, af.server_principal_name, af.schema_name , af.object_name, af.statement, aa.containing_group_name, aa.covering_action_name
FROM sys.fn_get_audit_file(@auditfile, NULL, NULL) AS af INNER JOIN
sys.dm_audit_actions AS aa ON af.action_id = aa.action_id INNER JOIN
sys.dm_audit_class_type_map AS ctm ON aa.class_desc = ctm.securable_class_desc
WHERE (af.statement <> '')
AND (aa.class_desc <> 'DATABASE')
AND (ctm.class_type_desc = 'SERVER AUDIT')
AND (af.database_name = 'master')
AND (af.object_name NOT IN ('dbo', '', 'telemetry_xevents'))
AND af.Event_Time > '2018-02-26 06:10:01.3531160'
GROUP BY
af.event_time, aa.class_desc, af.action_id, af.session_id, af.server_instance_name, af.database_name, af.server_principal_name, af.schema_name, af.object_name, af.statement, aa.containing_group_name, aa.covering_action_name"
write-output $row
#Connect
#$AuditOutput=invoke-sqlcmd -ServerInstance $instance -Query $DbQuery -Querytimeout 30
$AuditOutput=invoke-sqlcmd -ServerInstance $instance -Database 'Master' -Query $DbQuery -Querytimeout 30
############################
######################################################################
# Write Output to Target SQL Server table (SQL1, DBA database)
######################################################################
#Connect to the SQL server and the Database
$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source="+$TargetServerInstance+"; Initial Catalog="+$TargetServerDB+"; Integrated Security=SSPI")
# Open DB Connection
$conn.Open()
##################################
#DATABASES DETAILS INSERT
##################################
#Loop through each row
foreach ($row in $AuditOutput)
{
#Create SQL Insert Statement with Audit values
$db_insert_stmt = "INSERT INTO [dbo].[SQLAuditStaging]
([event_time]
,[class_desc]
,[action_id]
,[session_id]
,[server_instance_name]
,[database_name]
,[server_principal_name]
,[schema_name]
,[object_name]
,[statement]
,[containing_group_name]
,[covering_action_name])
VALUES ('$($row.event_time)','$($row.class_desc)','$($row.action_id)','$($row.session_id)','$($row.server_instance_name)','$($row.database_name)','$($row.server_principal_name)','$($row.schema_name)','$($row.object_name)','$($row.statement)','$($row.containing_group_name)', '$($row.covering_action_name)')"
# ## Create your command (Services)
$cmddb = $conn.CreateCommand()
$cmddb.CommandText = $db_insert_stmt
# ## Invoke the Insert statement
$cmddb.ExecuteNonQuery()
}
#Log Details of collection success
# Log collection success to table here
## Close DB Connection
$conn.Close()
}
catch
{
"Error on collections with $instance." | Write-Host -ForegroundColor Red
#Error log info collection here
}
}
提前致谢....
答案 0 :(得分:0)
您需要使用参数化查询。一个是因为你的代码由于引用转义而抛出错误,两个因为字符串连接易受SQL注入攻击。他们并不那么困难。
尝试这样的事情:
##################################
#DATABASES DETAILS INSERT
##################################
#Create SQL Insert Statement
$db_insert_stmt = "INSERT INTO [dbo].[SQLAuditStaging] ([event_time], [class_desc], [action_id], [session_id], [server_instance_name], [database_name], [server_principal_name], [schema_name], [object_name], [statement], [containing_group_name], [covering_action_name])
VALUES (@event_time,
@class_desc,
@action_id,
@session_id,
@server_instance_name,
@database_name,
@server_principal_name,
@schema_name,
@object_name,
@statement,
@containing_group_name,
@covering_action_name)"
#Loop through each row
foreach ($row in $AuditOutput) {
$cmddb = $conn.CreateCommand()
$cmddb.CommandText = $db_insert_stmt
# Assign parameter values
$cmddb.Parameters.Add('@event_time' ,[System.Data.SqlDbType]::DateTime2).Value = $row.event_time
$cmddb.Parameters.Add('@class_desc' ,[System.Data.SqlDbType]::NVarChar).Value = $row.class_desc
$cmddb.Parameters.Add('@action_id' ,[System.Data.SqlDbType]::Int).Value = $row.action_id
$cmddb.Parameters.Add('@session_id' ,[System.Data.SqlDbType]::Int).Value = $row.session_id
$cmddb.Parameters.Add('@server_instance_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.server_instance_name
$cmddb.Parameters.Add('@database_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.database_name
$cmddb.Parameters.Add('@server_principal_name',[System.Data.SqlDbType]::NVarChar).Value = $row.server_principal_name
$cmddb.Parameters.Add('@schema_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.schema_name
$cmddb.Parameters.Add('@object_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.object_name
$cmddb.Parameters.Add('@statement' ,[System.Data.SqlDbType]::NVarChar).Value = $row.statement
$cmddb.Parameters.Add('@containing_group_name',[System.Data.SqlDbType]::NVarChar).Value = $row.containing_group_name
$cmddb.Parameters.Add('@covering_action_name' ,[System.Data.SqlDbType]::NVarChar).Value = $row.covering_action_name
$cmddb.ExecuteNonQuery()
}
请注意,参数中使用的数据类型应与[dbo].[SQLAuditStaging]
的数据类型匹配。 $row
中列的值也应该是相关的数据类型。我上面猜了一下。使用this chart确定数据类型的匹配方式。