前言部分
SQL语法允许开放SQL语句的每子句动态指定作为在括号中指定一个数据对象的内容。如果其中一个数据对象的全部或部分内容来自程序外部,则存在以下SQL注入之一的风险:
访问非允许的数据库表
如果动态指定的数据库表完全或部分来自程序外部,则用户可能会访问他们通常没有授权的数据库。如果在动态指定的数据库表中使用外部输入是不可避免的,则必须正确检查输入。
在以下程序部分中,方法CHECK_TABLE_NAME_STR仅允许访问飞行数据模型的表。来自其他或不存在的数据库表的输入被拒绝。也不允许访问超大数据库表,以避免对系统性能造成太大压力。
DATA dbtab TYPE string. cl_demo_input=>request( CHANGING field = dbtab ). TRY. dbtab = cl_abap_dyn_prg=>check_table_name_str( val = to_upper( dbtab ) packages = 'SAPBC_DATAMODEL' ). CATCH cx_abap_not_a_table cx_abap_not_in_package. cl_demo_output=>display( 'Wrong input' ). LEAVE PROGRAM. ENDTRY. DATA dref TYPE REF TO data. FIELD-SYMBOLS <fs> TYPE STANDARD TABLE. CREATE DATA dref TYPE STANDARD TABLE OF (dbtab) WITH EMPTY KEY. ASSIGN dref->* TO <fs>. DATA lines TYPE i. SELECT COUNT(*) FROM (dbtab) INTO (@lines). IF lines > 1000. cl_demo_output=>display( 'Table too large' ). LEAVE PROGRAM. ENDIF. SELECT * FROM (dbtab) INTO TABLE @<fs>. cl_demo_output=>display( <fs> ).
访问非允许的表列用户可能会访问为他们通常没有授权表列。用户还可以未经许可重命名列,或使用聚合函数执行未经授权的计算。如果在动态指定的表列中使用外部输入是不可避免的,则必须正确检查输入。
注意点:
在GROUP BY之后指定列时,相同的安全建议适用于在SELECT之后直接动态指定的列。
动态WHERE条件的操作
如果动态WHERE条件完全或部分来自程序外部,则用户可能会访问他们通常没有授权的数据。如果无法避免在动态WHERE条件中使用外部输入 ,则必须正确检查输入并且通常也会屏蔽输入。
注意点:
动态指定HAVING条件时,应用与动态WHERE条件相同的安全建议。
在以下程序部分中,通过使用类CL_ABAP_DYN_PRG的方法QUOTE来防止潜在的SQL注入,该方法在开头和结尾添加引号。如果未使用此方法,并且输入“x'OR name <>' ”,则会显示SCUSTOM表中的所有数据。
DATA name TYPE string. DATA customers TYPE TABLE OF scustom WITH EMPTY KEY. cl_demo_input=>request( CHANGING field = name ). DATA(cond) = `country = 'DE' AND name = ` && cl_abap_dyn_prg=>quote( name ). TRY. SELECT * FROM scustom WHERE (cond) INTO TABLE @customers. cl_demo_output=>display( customers ). CATCH cx_sy_dynamic_osql_syntax. cl_demo_output=>display( 'Wrong input' ). ENDTRY.
操纵动态变化表达式
如果动态更改表达式完全或部分来自程序外部,则用户可能会更改他们通常没有授权的数据。如果无法避免在动态更改表达式中使用外部输入,则必须正确检查输入并且通常也会屏蔽输入。
在以下程序部分中,通过使用类CL_ABAP_DYN_PRG的方法QUOTE来防止潜在的SQL注入,该方法在开头和结尾添加引号。如果未使用此方法,并且例如,如果在其中一个输入字段中输入“ ...'discount = '90 ”,则相关客户的折扣将设置为90。
DATA in TYPE REF TO if_demo_input. DATA customer TYPE scustom. DATA: id TYPE scustom-id, name TYPE string, street TYPE string, city TYPE string, postcode TYPE string. id = '00000001'. SELECT SINGLE * FROM scustom WHERE id = @id INTO @customer. name = customer-name. street = customer-street. city = customer-city. postcode = customer-postcode. in = cl_demo_input=>new( ). in->add_field( CHANGING field = name )->add_field( CHANGING field = street )->add_field( CHANGING field = city )->add_field( CHANGING field = postcode )->request( ). DATA(set_expr) = COND string( WHEN name IS NOT INITIAL THEN ` NAME = ` && cl_abap_dyn_prg=>quote( name ) ) && COND string( WHEN street IS NOT INITIAL THEN ` STREET = ` && cl_abap_dyn_prg=>quote( street ) ) && COND string( WHEN city IS NOT INITIAL THEN ` CITY = ` && cl_abap_dyn_prg=>quote( city ) ) && COND string( WHEN postcode IS NOT INITIAL THEN ` POSTCODE = ` && cl_abap_dyn_prg=>quote( postcode ) ). TRY. UPDATE scustom SET (set_expr) WHERE id = @id. CATCH cx_sy_dynamic_osql_syntax. cl_demo_output=>display( 'Wrong input' ). ENDTRY.