来自powershell的SQL插入失败并带引号

时间:2018-02-28 10:23:39

标签: sql powershell

首先感谢你看这篇文章。如果我的格式错误,我是新来的,所以道歉。

我将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     
    }

    }  

提前致谢....

1 个答案:

答案 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确定数据类型的匹配方式。