ABOUT ME

-

오늘 방문자
-
어제 방문자
-
전체
-
  • [MSSQL] 프로시저 동적쿼리 작성시 SQL Injection 우회 방법
    DB 2019. 6. 3. 23:37


    팀Slack방에 에러가 올라와서 Kibana에서 trace를 찾아보니 


    상품명 조회 파라미터에 ' (싱글쿼터)가 들어 온 것이었다.


    " 엥? lucy필터를 사용하고 있을탠대??? "

    (lucy필터는 네이버에서 만든 XSS 방지 라이브러리이다)

    싱글쿼터가 들어오면 오면 치환이 되어야 됬는대 안된 것이다.


    싱글쿼터가 들어오게되면 SQL Injection에 취약해 질수 있기 때문에 매우 위험한 상황이었다.


    (최근에 "여기어*" 라는 숙박 서비스가 SQL Injection으로 약 100만명의 정보가 유출된 사건도 있었다.)


    그래서 우리팀의 담당하고 있는 매뉴들에 대해서 a' or 1=1 or 'a'='a와 같은 문자열을 입력하니.


    모든 데이터가 촥! ...OTL


    이제서야 알게되었지만 lucy필터는 form-data에 대해서만 적용되고


    Request Raw Body로 넘어가는 JSON에 대해서는 처리해 주지 않는다 


    어설프게 그냥 용도 정도만 " lucy필터가 XSS처리해주는 구나~" 하고 대충 넘어 갔던게 후회됬다..


    (참고 lucy-xss-servlet-filter의 한계)


    팀내의 주요 API는 모두 RequestBody를 통해서 데이터를 받고 있었다. 그러니 당연히 필터링이 전혀안될 수 밖에.


    이 이슈에대해서 팀원들과 회의를 한 결과 공수가 적게 드는 쪽으로 방향을 맞추기로 했다.


    팀내 특성상(?) 쿼리를 프로시저에 작성하고있는대.


    프로시저중에서도 문자열을 합쳐서 만든 동적쿼리들에 대해서만 문제가 발생했다.


    아래는 검색화면에서 사용하는 SP와 유사하게 만든 것이다.


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    USE [cm]
    GO
    /****** Object:  StoredProcedure [dbo].[SQLINJECTION]    Script Date: 2019-06-04 오후 9:27:27 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
     
    ALTER PROCEDURE [dbo].[SQLINJECTION] 
        @pi_searchKeyword    NVARCHAR(MAX)
        ,@pi_start_date        NVARCHAR(10)
        ,@pi_end_date        NVARCHAR(10)
    AS
    BEGIN
     
        SET NOCOUNT ON;
        
        DECLARE 
            @v_select_sql NVARCHAR(MAX) = ''
            ,@v_from_sql NVARCHAR(MAX) = ''
            ,@v_where_sql NVARCHAR(MAX) = ''
            ,@v_sql  NVARCHAR(MAX) =''
     
        SET @v_select_sql += 'select top 100 * '
        
        SET @v_from_sql += ' from tb_user with(nolock) '
     
        IF @pi_start_date <> '' and @pi_end_date <> ''
        BEGIN
            SET @v_where_sql += ' where user_reg_date between ''' + @pi_start_date + ''' and '''+ @pi_end_date + ''''
        END 
     
        IF @pi_searchKeyword <> ''
        BEGIN
            SET @v_where_sql += 'and user_name='+ '''' + @pi_searchKeyword + ''''
        END
     
        SET @v_sql = @v_select_sql + @v_from_sql + @v_where_sql    
     
        PRINT @v_sql
     
        EXECUTE SP_EXECUTESQL @v_sql
     
    END
     
    cs



    SQL Injection은 바로 35행에서 발생한다.


    문자열인 싱글쿼터로 값을 감싸고 있기때문이다


    myBatis의 #{}을 쓴다해도, 프로시저 내부에서 싱글쿼터를 붙이기때문에 무용지물이다.






    그래서 어떻게 처리했는대?


    QUOTENAME()함수를 사용해서 처리했다.


    https://docs.microsoft.com/ko-kr/sql/t-sql/functions/quotename-transact-sql?view=sql-server-2017


    특정 구분자를 양옆으로 감싸주는 것이다.(문자열에 대해서만 처리하였음)


    22행, 35행, 36행이 수정한 내용이다


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    USE [cm]
    GO
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
     
    ALTER PROCEDURE [dbo].[SQLINJECTION] 
        @pi_searchKeyword    NVARCHAR(MAX)
        ,@pi_start_date        NVARCHAR(10)
        ,@pi_end_date        NVARCHAR(10)
    AS
    BEGIN
     
        SET NOCOUNT ON;
        
        DECLARE 
            @v_select_sql NVARCHAR(MAX) = ''
            ,@v_from_sql NVARCHAR(MAX) = ''
            ,@v_where_sql NVARCHAR(MAX) = ''
            ,@v_sql  NVARCHAR(MAX) =''
            ,@v_quote NVARCHAR(MAX)
     
        SET @v_select_sql += 'select top 100 * '
        
        SET @v_from_sql += ' from tb_user with(nolock) '
     
        IF @pi_start_date <> '' and @pi_end_date <> ''
        BEGIN
            SET @v_where_sql += ' where user_reg_date between ''' + @pi_start_date + ''' and '''+ @pi_end_date + ''''
        END 
     
        IF @pi_searchKeyword <> ''
        BEGIN
            SET @v_quote = QUOTENAME(@pi_searchKeyword,'''')
            SET @v_where_sql += 'and user_name=' + @v_quote
        END
     
        SET @v_sql = @v_select_sql + @v_from_sql + @v_where_sql    
     
        PRINT @v_sql
     
        EXECUTE SP_EXECUTESQL @v_sql
     
    END
     
    cs



    수정전 

    ' or 1=1 or 'a'='a 입력시 실행되는 SP내용


    1
    select top 100 *  from tb_user with(nolock)  where user_reg_date between '2018-01-01' and '2019-12-31'and user_name='' or 1=1 or 'a'='a'
    cs



    수정후

    ' or 1=1 or 'a'='a 입력시 실행되는 SP내용

    1
    select top 100 *  from tb_user with(nolock)  where user_reg_date between '2018-01-01' and '2019-12-31'and user_name=''' or 1=1 or ''a''=''a'
    cs


    or 1=1 or 'a'='a 입력시 실행되는 SP내용

    1
    select top 100 *  from tb_user with(nolock)  where user_reg_date between '2018-01-01' and '2019-12-31'and user_name='or 1=1 or ''a''=''a'
    cs


    TO BE 쿼리의 뒤쪽 문자열을 보면 싱글쿼터로 인젝션을 무효화 시키는 것을 볼 수 있다.


    처리하긴 하였지만 아쉬운점은 사실이다.  처음에 이 이슈가 발생했을 때 필터를 적용해보고 싶었지만..


    그럴수 없는 상황이었기 때문에 이렇게 우회하는 방법으로 처리하였다.






    더 좋은 방법이 있으면 댓글 부탁드립니다 ^^







    댓글

Designed by Tistory.