Home WHERE 절에 1=1은 왜 사용할까? 이 방법은 괜찮을까?
Post
Cancel

WHERE 절에 1=1은 왜 사용할까? 이 방법은 괜찮을까?

글을 작성하게 된 계기


회사 코드를 리팩토링하던 중, 1=1 이 많은 곳에서 사용되는 것을 보고, 이를 사용할 때 주의할 점과 1=1에 대한 생각을 정리하기 위해 글을 작성하게 되었습니다.

리팩토링 중인 프로젝트는 MyBatis를 사용하고 있으며, 이를 위주로 글을 작성했습니다.





1. 1=1


WHERE 절에 1=1 을 사용하는 것은 SQL문을 작성할 때 자주 사용되는 방법 중 하나입니다. 이 방법은 조건문을 작성할 때, 조건이 없는 경우에도 SQL문이 정상적으로 실행 되도록 하며, AND로 다양한 조건 을 줘서 동적 쿼리 를 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="getCodes" parameterType="......" resultType="......">

    ......

    <if test="CODE == 'GROUPCODE'">
        SELECT
        currency_code as code, ......
        FROM iso4217_currency_list
        WHERE 1 = 1 # 여기
        AND use_flag = 'Y'
        ORDER BY currency_code
    </if>

    ......

</select>





동적 쿼리를 사용할 수 있기 때문에 다양한 상황에 대응 해 활용할 수 있지만, 이로 인해 업데이트 시, 데이터가 삭제된다거나 SQL Injection에 취약해질 수 있습니다.

사용자 입력을 직접 쿼리에 삽입하는 경우, 검증되지 않은 입력이 SQL 명령어의 일부가 되어 데이터베이스에 무단으로 접근하거나 수정, 삭제 등을 수행할 수 있는 문제점이 있습니다.







2. 주의할 점


1=1을 사용할 때, 몇 가지 주의할 점이 있는데, 이에 대해 살펴보겠습니다.

  1. 쓰기 작업
  2. SQL Injection



2-1. 쓰기 작업

아래 쿼리에서 @userID가 NULL이면, AND userID = @userID 조건은 효과가 없게 되어 데이터베이스의 모든 사용자 데이터가 삭제될 수 있습니다.

1
2
3
DELETE FROM users
WHERE 1 = 1
AND userID = @userID;





이는 UPDATE의 경우도 마찬가지인데요, 여기서 @productID가 NULL이라면, AND productID = @productID 조건이 무시되어 모든 상품의 가격이 100으로 설정됩니다.

1
2
3
4
UPDATE products
SET price = 100
WHERE 1 = 1
AND productID = @productID;





2-2. SQL Injection

예를 들어, 웹 애플리케이션에서 사용자로부터 입력받은 데이터를 아래와 같이 쿼리에 직접 사용하는 경우를 생각해 보겠습니다.

1
2
String userInput = "anything' OR 'x'='x";
String query = "SELECT * FROM users WHERE 1 = 1 AND username = '" + userInput + "'";





여기서 사용자 입력이 “anything’ OR ‘x’=’x” 라면, 쿼리는 다음과 같이 됩니다. 이는 모든 사용자를 선택하며, 이로 인해 데이터베이스에서 민감한 정보가 노출되거나, 수정, 삭제 등의 위험한 작업이 발생할 수 있습니다.

1
SELECT * FROM users WHERE 1 = 1 AND username = 'anything' OR 'x'='x';

이에 대한 해결책은 Statement와 PreparedStatement를 참조해주세요.







3. 해결책


이는 간단하게 null 체크 을 사용하거나 trim 이나 prefix 를 사용해 방어할 수 있습니다.

  1. null 체크
  2. trim/prefix



3-1. null 체크

쓰기 작업을 할 때, null 조건을 사용하면 다음과 같이 테이블을 대상으로 한 쿼리를 막을 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="selectProducts" resultType="Product">
    SELECT PRODUCT_ID, NAME, PRICE, CATEGORY
    FROM PRODUCTS
    <where>
        <if test="category != null">
            CATEGORY = #{category}
        </if>
        <if test="minPrice != null">
            AND PRICE >= #{minPrice}
        </if>
        <if test="maxPrice != null">
            AND PRICE <= #{maxPrice}
        </if>
    </where>
</select>





3-2. trim/prefix

또는 trim 이나 prefix 를 사용해 막을 수도 있는데요, trim은 첫 번째 조건 앞의 불필요한 AND나 OR를 자동으로 제거합니다. prefix 속성을 사용하여 조건이 존재할 경우 자동으로 WHERE를 추가합니다. 따라서 이를 통해 악의적인 공격을 방어할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="selectProductsDetailed" resultType="Product">
    SELECT PRODUCT_ID, NAME, PRICE, CATEGORY
    FROM PRODUCTS
    <trim prefix="WHERE" prefixOverrides="AND | OR">
        <if test="category != null">
            AND CATEGORY = #{category}
        </if>
        <if test="minPrice != null">
            AND PRICE >= #{minPrice}
        </if>
        <if test="maxPrice != null">
            AND PRICE <= #{maxPrice}
        </if>
    </trim>
</select>





MyBatis를 위주로 글을 작성했는데요, Jpa/QueryDsl, Jdbc의 PreparedState 를 사용하면 이런 걱정을 할 필요가 없습니다. 값을 바인딩하는 과정에서 이를 자동으로 방어해 주기 때문입니다. 따라서 미리 걱정하기보다 MyBatis를 사용하고 있다면 이런 방어책을 알고 한 번 정도 살펴보면 좋을 것 같습니다.







4. 정리


동적 쿼리를 편하게 작성할 수 있다는 점은 좋지만, 이로 인해 발생할 수 있는 문제점을 고려하여 적절한 방어책을 대비해 두는 게 좋습니다. 개인적으로는 1=1 보다는 다른 방법을 사용하는 것이 좋다고 생각하는데, 편리하더라도 위험 상황에 노출될 여지가 있다면 애초에 사용하지 않는 게 맞다고 생각 하기 때문입니다. 물론 이는 개인적인 의견이기 때문에 팀 규칙이나 기존에 작성한 코드를 잘 따릅시다.


This post is licensed under CC BY 4.0 by the author.