Skip to content
튜닝 연습PL/SQL과 사용자 정의 함수
PLSQL-031

보안 점검에서 주문 목록 API의 정렬 칸으로 다른 매장의 주문 수를 알아낼 수 있었다

기초PLSQL버거681 / 700

상황 ​

버거 본사의 매장 관리 앱은 「오늘 주문」 목록을 프로시저 P_DAY_ORD로 받는다. 화면에서 정렬 기준(주문 시각, 주문 번호)과 방향을 고르면 앱이 정렬 문자열을 만들어 그대로 넘긴다. 매장과 날짜는 이미 바인드 변수로 넘긴다. 외부 보안 점검팀은 정렬 칸에 CASE WHEN (SELECT COUNT(*) FROM ORD WHERE SHOP_NO = 'SH0007' …) > 1500 THEN … 같은 식을 넣어, 목록 순서가 바뀌는지로 다른 매장의 주문 수를 알아낼 수 있었다고 보고했다. DBA는 같은 목록 문장이 정렬 문자열만 다른 채 공유 풀에 40여 개 있다고 덧붙였다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · ORD_NO · ORD_DT VARCHAR2(8) · ORD_HMS VARCHAR2(6) · SHOP_NO 'SH0001'(매장 200곳)
  • 인덱스: ORD_PK (ORD_NO) · ORD_X01 (ORD_DT, ORD_HMS) · 이 문제에서는 ORD_X02 (SHOP_NO, ORD_DT, ORD_HMS)도 있다 · 통계 최신
  • 매장 하나의 하루 주문은 약 7건이다
  • 앱은 iOS, 안드로이드, 웹 세 가지이고 정렬 문자열을 서로 다르게 만든다('ORD_HMS DESC', 'ord_hms desc', 'ORD_HMS DESC' 등)

원본 SQL ​

sql
PROCEDURE P_DAY_ORD(p_shop_no IN VARCHAR2, p_dt IN VARCHAR2,
                    p_order   IN VARCHAR2,          -- 앱이 만든 정렬 문자열 (예: 'ORD_HMS DESC')
                    o_rc      OUT SYS_REFCURSOR) IS
BEGIN
  OPEN o_rc FOR
    'SELECT ORD_NO, ORD_DT, ORD_HMS FROM ORD WHERE SHOP_NO = :s AND ORD_DT = :d ORDER BY ' || p_order
    USING p_shop_no, p_dt;
END;

실행계획 학습용 예시 ​

xplan
[V$SQLAREA: 이 프로시저가 만든 목록 문장 · 점검이 있던 날]
  ORDER BY 뒤의 글자                       문장 수     실행 수
  ORD_HMS DESC · ord_hms desc · …             23     151,402
  ORD_NO · ORD_NO ASC · ord_no · …            19      41,288
  보안 점검이 넣은 식 (CASE WHEN …)          312         312
  합계                                       354     193,002

[보안 점검 때 실행된 문장 하나 (V$SQL.SQL_TEXT)]
  SELECT ORD_NO, ORD_DT, ORD_HMS FROM ORD WHERE SHOP_NO = :s AND ORD_DT = :d
   ORDER BY CASE WHEN (SELECT COUNT(*) FROM ORD WHERE SHOP_NO = 'SH0007' AND ORD_DT = '20131224') > 1500
                 THEN ORD_NO ELSE ORD_HMS END

[한 번의 실행: DISPLAY_CURSOR (ALLSTATS LAST) · 'ORD_HMS DESC']
-------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |      7 |00:00:00.01 |      10 |
|   1 |  TABLE ACCESS BY INDEX ROWID | ORD     |      1 |      7 |      7 |00:00:00.01 |      10 |
|*  2 |   INDEX RANGE SCAN DESCENDING| ORD_X02 |      1 |      7 |      7 |00:00:00.01 |       3 |
-------------------------------------------------------------------------------------------------
   2 - access("SHOP_NO"=:S AND "ORD_DT"=:D)

문제 ​

  1. 바인드 변수를 썼는데도 주입이 가능했던 이유와, 목록 문장이 40개 넘게 생긴 이유를 설명하시오.
  2. 정렬 기준을 화면이 고르게 두면서 주입이 불가능하도록 프로시저를 고치시오. 정렬 컬럼을 바인드 변수로 넘기는 안(ORDER BY :col)이 왜 답이 아닌지도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자