前言部分
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.