Daily CS) RegEx

goldenGlow_21·2025년 4월 25일
0

Daily CS

목록 보기
45/50

RegEx

Regular Expression

오늘의 주제는 정규표현식이다. 처음에는 쉬어가는 주제로 골랐지만... 예상이 틀린 모양이다. 가볍게 목차만 잡아봤는데 생각보다 엄청나게 다룰 것들이 많다. 그래도 내용을 가볍게 훑어 보니 하나같이 영양가 있어 보이는 것들이 많다. 끝까지 최대한 열심히 해서 한번 채워 보자.


1. 정규표현식(RegEx)이란 무엇인가

1.1 정의와 탄생 배경

정규표현식(Regular Expression, 약칭 RegEx 또는 regexp)은 문자열 내 특정 패턴을 찾거나, 검사하거나, 치환하는 데 사용하는 일종의 패턴 언어이다. 정규표현식은 단순한 문자열 검색을 넘어, 복잡한 규칙 기반의 텍스트 필터링을 가능하게 한다.

이 개념은 1950년대 수학자 스티븐 콜 클레이니(Stephen Cole Kleene) 가 형식 언어 이론에서 ‘정규 언어(Regular Language)’를 정의하면서 비롯되었다. 이후 컴퓨터 과학 분야에서는 Unix 유닉스 운영체제의 grep, sed, awk와 같은 유틸리티에서 도입되었고, 이는 텍스트 기반의 데이터 처리 방식에 혁명적인 영향을 끼쳤다.

정규표현식은 초기에는 컴파일러 이론과 이론적 언어 분석에 국한되어 있었지만, 지금은 보안, 데이터 분석, 웹 개발, 시스템 관리 등 거의 모든 실무 영역에서 필수적인 기술로 자리 잡고 있다.

1.2 문자열 처리에서의 필요성과 이점

문자열을 다루는 대부분의 프로그램에서는 특정 패턴을 찾거나, 사용자의 입력을 검증하거나, 민감정보를 추출하거나, 로그 파일에서 의미 있는 정보를 분석해야 할 때가 있다. 이럴 때, 조건문이나 루프만으로 처리하는 것은 너무 복잡하거나 비효율적일 수 있다. 정규표현식은 이러한 문제를 간결하고 강력하게 해결해준다.

주요 이점:

  • 간결한 패턴 정의: 복잡한 문자열 조건을 몇 줄의 패턴으로 표현 가능

  • 재사용성과 이식성: 대부분의 언어에서 동일한 정규식을 적용 가능

  • 자동화에 적합: 파일 이름 필터링, 로그 파싱, HTML 데이터 추출 등 반복 작업 자동화에 용이

  • 입력 검증 보안 강화: 이메일 주소, 전화번호, 비밀번호 형식 등을 검증해 XSS, SQL Injection 같은 보안 취약점 방지

정규표현식은 잘만 사용하면 텍스트 기반 자동화 작업에서 수십 배의 생산성을 가져올 수 있다.

1.3 다양한 언어와 도구에서의 활용 예

정규표현식은 대부분의 프로그래밍 언어와 개발 도구, 보안 플랫폼에서 핵심 기능으로 지원된다. 특히 아래와 같은 환경에서는 거의 필수적으로 사용된다.

프로그래밍 언어에서의 사용:

  • Python: re 모듈 사용 (re.search, re.findall, re.sub 등)
  • JavaScript: /pattern/ 또는 new RegExp() 구문으로 사용
  • Java: java.util.regex 패키지에서 제공
  • C#: System.Text.RegularExpressions 네임스페이스에서 지원

실무 도구에서의 사용 예:

도구/환경용도 예시
grep/sed/awk리눅스에서 로그 검색 및 편집 자동화
Wireshark패킷 내 문자열 탐지
IDS/IPS정규식 기반 룰로 악성 트래픽 식별 (e.g., Snort)
YARA악성코드 탐지용 패턴 정의
SIEM로그 분석 및 경고 규칙 정의
Suricata정규식 기반 서명 탐지 엔진

이처럼 정규표현식은 단순한 개발 도구를 넘어 보안 인프라, 데이터 분석 플랫폼, 자동화 파이프라인 등 다양한 분야에 깊이 통합되어 있으며, 이를 정확히 이해하고 활용하는 능력은 실무에서 큰 차이를 만든다.


2. 정규표현식의 기본 문법

정규표현식은 특정한 문자열 패턴을 정의하기 위한 문법 체계이다. 기본적인 문법 구조를 이해하면, 텍스트 내에서 원하는 형식을 쉽게 탐색하고 조작할 수 있다.

2.1 리터럴 문자와 특수문자

리터럴(literal) 문자는 그 자체로 해석되는 문자이다. 예를 들어 hello라는 정규표현식은 문자열 안에서 정확히 "hello"라는 단어를 찾는다.

하지만 정규표현식에는 특수한 기능을 가진 문자들도 있다. 이들을 특수문자(meta-character) 라고 하며, 정규표현식의 동작을 제어한다.

주요 특수문자:

문자의미
.임의의 한 문자 (개행 제외)
^문자열의 시작
$문자열의 끝
*앞 문자의 0회 이상 반복
+앞 문자의 1회 이상 반복
?앞 문자의 0회 또는 1회
``
()그룹화
[]문자 집합
\이스케이프 문자 (특수문자를 문자 그대로 사용하고 싶을 때)

2.2 문자 클래스([]), 범위 지정

[] 안에 문자를 나열하면, 그 중 하나라도 일치하면 매칭된다. 이를 문자 클래스라 한다.

예시:

  • [abc]a, b, 또는 c 중 하나와 일치
  • [0-9] → 숫자 하나와 일치 (0부터 9까지)
  • [A-Za-z] → 모든 영문자 (대소문자 포함)

반대의 의미:

  • [^abc]a, b, c를 제외한 문자

실전 예시:

  • [aeiou] → 모음 하나 찾기
  • [^0-9] → 숫자가 아닌 문자 찾기

2.3 반복자(*, +, ?, {n,m})

반복자(quantifier) 는 특정 패턴이 얼마나 반복되어야 하는지를 지정하는 도구이다.

주요 반복자:

기호의미
*0회 이상 반복 (zero or more)
+1회 이상 반복 (one or more)
?0회 또는 1회 (optional)
{n}정확히 n회 반복
{n,}n회 이상 반복
{n,m}n회 이상, m회 이하 반복

예시:

  • a* → 빈 문자열, "a", "aa", "aaa"
  • a+"a", "aa", "aaa" 등 (단, 최소 1회)
  • a{2,4}"aa", "aaa", "aaaa" 만 일치

2.4 앵커(^, $), 점(.)의 의미

앵커(anchor) 는 문자열 내에서 위치를 지정한다. 문자 자체가 아니라 위치 조건이다.

앵커의미
^문자열의 시작
$문자열의 끝

예시:

  • ^abc"abc"로 시작하는 문자열
  • xyz$"xyz"로 끝나는 문자열
  • ^abc$ → 전체 문자열이 정확히 "abc"여야 매칭

. (점) 은 개행 문자를 제외한 임의의 문자 하나를 의미한다.

예시:

  • a.c"abc", "a9c", "a_c" 등과 매칭

주의: .은 빈 문자는 매칭하지 않으며, 기본 설정에서는 개행 문자(\n)도 매칭하지 않는다.

2.5 이스케이프 처리와 주석

정규표현식에서 특수문자(., *, +, ? 등)를 문자 그대로 사용하고 싶을 때는 이스케이프 문자 \를 사용한다.

예시:

  • \. → 실제 점 문자 . 매칭
  • \d → 숫자 (0-9)
  • \w → 알파벳, 숫자, 밑줄 (word character)
  • \s → 공백 문자 (스페이스, 탭 등)

주석:

정규표현식 자체에는 기본적으로 주석 기능이 없지만, 일부 언어 또는 플래그에서는 x 플래그를 통해 공백과 주석을 허용할 수 있다.

Python 예시 (re.VERBOSE 플래그)

pattern = re.compile(r"""
    ^              # 문자열의 시작
    [A-Z][a-z]+    # 대문자로 시작하고, 소문자 여러 개
    \s             # 공백 문자
    [A-Z][a-z]+    # 성
    $              # 문자열의 끝
""", re.VERBOSE)

3. 캡처와 그룹화, 대체

정규표현식에서 ()는 단순히 여러 문자를 묶는 것(grouping) 을 넘어서, 해당 패턴에 매칭된 문자열을 "기억(capture)" 하게 만든다. 이 기능은 백레퍼런스, 치환, 추출 등에 매우 유용하게 사용된다.

3.1 괄호 () 를 이용한 그룹화

정규표현식에서 ()패턴을 논리적으로 묶어 하나의 단위로 처리할 수 있게 해준다.

예시:

  • 정규표현식: (ab)+
    • 의미: "ab"라는 문자열이 하나 이상 반복되는 패턴
    • 매칭 예시: "abab", "ab", "ababab"

또한 괄호로 묶인 부분은 자동으로 "그룹 번호"가 부여되며, 후속 처리나 참조에 사용된다.

그룹 번호의미
\1 또는 $1첫 번째 괄호 그룹
\2, $2두 번째 그룹

그룹 번호는 괄호가 열리는 순서대로 매겨진다.

3.2 캡처 그룹 vs 비캡처 그룹 (?:)

기본 괄호 ()는 캡처를 수행하지만, 모든 그룹이 반드시 "기억되어야" 하는 것은 아니다. 단순히 논리적 묶음만 하고, 캡처를 원치 않는 경우에는 (?:...)를 사용한다.

예시:

  • 정규표현식: (?:http|https)://
    • 의미: "http://" 또는 "https://" 로 시작하는 문자열
    • 장점: http/https를 캡처 그룹으로 기억하지 않음

차이점:

표현식그룹으로 기억?사용 예
(abc)O (캡처 그룹)\1, $1 등으로 참조 가능
(?:abc)X (비캡처 그룹)참조하지 않고 그룹화만

비캡처 그룹은 성능상 이점이 있으며, 캡처가 불필요할 경우 반드시 사용하는 것이 좋다.

3.3 OR 연산자 |

|OR 연산자, 즉 대체(alternation) 를 의미한다. 왼쪽 또는 오른쪽 패턴 중 하나라도 일치하면 매칭된다.

예시:

  • 정규표현식: cat|dog

    • "cat" 또는 "dog"와 매칭
  • 정규표현식: (cat|dog)s?

    • "cat", "cats", "dog", "dogs" 모두 매칭됨

주의: |는 가장 가까운 괄호나 표현 범위까지만 적용되므로, 우선순위 제어를 위해 괄호를 꼭 사용하는 것이 좋다.

잘못된 예시:

  • http|https://
    • "http" 또는 "https://" 를 찾는다 (의도한 대로 작동하지 않음)

올바른 예시:

  • (http|https)://
    • "http://" 또는 "https://" 를 정확히 탐지

3.4 백레퍼런스와 참조 활용

정규표현식의 강력한 기능 중 하나가 백레퍼런스(backreference) 이다. 앞서 매칭된 캡처 그룹의 값과 같은 값을 나중에 다시 참조할 수 있게 한다.

표현 방식:

표현 언어백레퍼런스 표현
정규표현식 내부\1, \2
Python, Perl 등match.group(1)
치환(Replacement)$1, $2 등으로 표현되는 경우도 있음

예시 1: 동일한 단어 반복 확인

\b(\w+)\s+\1\b
  • 의미: 동일한 단어가 연속으로 반복될 때 매칭 (예: "hello hello")
  • 그룹 1: 첫 번째 단어
  • \1: 첫 번째 단어가 다시 나와야 매칭됨

예시 2: XML 태그 짝 확인

<(\w+)>.*?</\1>
  • 의미: 열리는 태그와 닫히는 태그가 동일한 경우
  • (\w+): 태그명 캡처
  • </\1>: 동일한 태그명으로 닫힘

HTML과 같은 중첩 구조를 완벽히 다루기는 어렵지만, 간단한 태그 구조에서는 매우 유용하다.


4. 고급 패턴 구성 기법

4.1 Lookahead, Lookbehind (전방/후방 탐색)

Lookaround어떤 조건을 만족하는 "앞"이나 "뒤"의 텍스트를 검사하되, 실제 매칭에는 포함하지 않는 기능이다. 매우 강력하면서도 실전에서 자주 쓰인다.

Lookahead (전방 탐색)

패턴 뒤에 오는 내용을 조건으로 지정.

  • (?=...) : 긍정 전방 탐색 (positive lookahead)
  • (?!...) : 부정 전방 탐색 (negative lookahead)

예시:

\w+(?=@gmail\.com)
  • 의미: @gmail.com 앞에 있는 단어만 매칭 (이메일 주소에서 사용자명 추출)
foo(?!bar)
  • 의미: 뒤에 bar가 오지 않는 foo만 매칭

Lookbehind (후방 탐색)

패턴 앞에 오는 내용을 조건으로 지정.

  • (?<=...) : 긍정 후방 탐색 (positive lookbehind)
  • (?<!...) : 부정 후방 탐색 (negative lookbehind)

예시:

(?<=\$)\d+
  • 의미: $ 기호 바로 뒤에 오는 숫자만 매칭 (가격 탐지 등)
(?<!https:)//\w+
  • 의미: https: 가 아닌 경우에만 // 뒤의 문자열 매칭

일부 언어(Python, JavaScript 등)에서는 후방 탐색에 고정 길이만 허용하는 제약이 있으니 사용 전 확인 필요.

4.2 조건부 표현식

조건부 표현식은 이전에 캡처된 그룹이 존재하는지에 따라 패턴을 분기한다. 주로 복잡한 조건문 같은 처리가 필요한 경우 유용하다.

문법:

(?(group_number) then_pattern | else_pattern)

예시:

(<)?\w+(?(1)>)
  • 의미: <word> 또는 word 형태를 모두 처리
    • <가 있으면, >도 있어야 함
    • 그룹 1이 매칭되었는지를 조건으로 판단

실무에서 자주 쓰이진 않지만, XML/HTML 대응 구조 체크 등 특정 구조에서 유용하게 사용 가능함.

4.3 Greedy vs Lazy 매칭

정규표현식에서 반복자(*, +, {n,m} 등)는 기본적으로 Greedy(탐욕적) 하게 동작한다. 즉, 가능한 많은 문자를 매칭하려 한다. 이에 반해, Lazy(게으른) 매칭은 가능한 한 적은 양을 매칭한다.

반복자의미예시
*0회 이상 (Greedy)a.*b"accccb" 전체 매칭
*?0회 이상 (Lazy)a.*?b"accccb""acccb"까지

예시 1: HTML 태그 내 텍스트 추출

<.*>
  • Greedy: <div><p>text</p></div> 전체를 한 번에 매칭
<.*?>
  • Lazy: <div>, <p> 등 태그 하나씩 각각 매칭

Lazy 모드를 활용하면 불필요한 과한 매칭 방지가 가능하므로, HTML, JSON 등 중첩 구조에서 매우 중요함.

4.4 멀티라인 모드, Dotall 모드

정규표현식은 텍스트의 라인 구분 방식이나 개행 문자 처리 방식에 따라 동작 방식이 달라질 수 있다. 이 때 사용하는 것이 멀티라인 모드(m)Dotall 모드(s) 이다.

멀티라인 모드 (m)

  • ^, $문서 전체가 아니라 각 줄의 시작/끝으로 작동하게 함
^Error
  • 일반 모드: 문서 맨 앞에서만 "Error"를 찾음
  • 멀티라인 모드: 각 줄의 시작에서 "Error"를 찾음

사용 예: 로그 파일처럼 줄 단위로 분석할 때 필수

Dotall 모드 (s)

  • . 문자가 개행(\n)을 제외하고 모든 문자와 매칭됨
  • Dotall 모드를 활성화하면 개행까지 포함한 전체 매칭이 가능
a.*b
  • 일반 모드: a부터 b까지, 같은 줄에서만 매칭
  • Dotall 모드: 여러 줄에 걸쳐서 a부터 b까지 매칭 가능

활성화 방법 (Python 예시):

import re

re.findall(r"^Error", log, flags=re.MULTILINE)
re.findall(r"a.*b", text, flags=re.DOTALL)

대부분의 정규표현식 엔진(PCRE, Python, Java 등)은 (?m) 또는 (?s) 등으로 인라인으로도 설정 가능함


5. 정규표현식의 실무 활용

5.1 보안 필터링 (XSS, SQLi 탐지 등)

정규표현식은 웹 보안에서 입력값 검증과 공격 탐지에 널리 사용된다. 가장 대표적인 것이 XSS(크로스사이트 스크립팅)SQL 인젝션 필터링이다.

예: XSS 탐지를 위한 정규식

<.*?(script|img|svg|iframe|on\w+)[^>]*>
  • 의미: <script>, <img> 등의 위험 태그, onload, onclick 등 이벤트 속성 탐지
(?:javascript|vbscript|data):[^\s]+
  • 의미: href 또는 src 속성 등에 포함된 JavaScript 실행 시도 탐지

정규표현식만으로 완전한 XSS 방지는 불가능하므로, WAF와 CSP와의 병행이 필수이다.

예: SQL Injection 탐지를 위한 정규식

(?:\%27)|(?:')|(?:--)|(/\*)|(\*/)|(\b(select|union|insert|drop|update|delete)\b)
  • 의미: SQL 인젝션에서 자주 사용되는 특수문자와 키워드 탐지
\b(OR|AND)\b\s+\d+=\d+
  • 의미: 논리 연산자를 통한 조건 우회 시도

고급 탐지에서는 정규표현식 외에 파라미터 분석, 행동 기반 탐지와 함께 사용된다.

5.2 로그 분석에서의 패턴 매칭

정규표현식은 보안 로그에서 이상 행위나 오류를 빠르게 필터링하는 데 핵심 도구로 쓰인다.

예: Apache 액세스 로그에서 비정상 요청 탐지

"(GET|POST|HEAD)\s+(/[^\s]*)\s+HTTP/\d\.\d"\s+(4\d{2}|5\d{2})
  • 의미: 4xx 또는 5xx 에러 응답을 포함한 요청만 필터링

예: 인증 실패 로그 탐지 (SSH)

Failed password for (invalid user )?\w+ from (\d{1,3}\.){3}\d{1,3}
  • 의미: sshd 로그에서 로그인 실패 항목만 추출

5.3 이메일, URL, IP, 해시값 정규식

보안 필터나 스크립트 작성 시, 표준 구조를 가진 데이터들을 인식하고 파싱하는 용도로 자주 사용된다.

이메일

[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+

URL

https?:\/\/[\w.-]+(?:\.[\w.-]+)+(?:\/[\w\-.~:/?#[\]@!$&'()*+,;=]*)?

IPv4 주소

\b(?:\d{1,3}\.){3}\d{1,3}\b

해시값 (MD5, SHA1, SHA256)

\b[a-fA-F0-9]{32}\b         # MD5  
\b[a-fA-F0-9]{40}\b         # SHA1  
\b[a-fA-F0-9]{64}\b         # SHA256

5.4 악성코드 분석에서의 YARA + RegEx

YARA에서는 문자열 패턴 탐지에 정규표현식을 직접 사용할 수 있음. 특히, 악성 코드 내 코드 조각, 함수 이름, 특정 문자열 시그니처를 탐지할 때 유용하다.

예시: Windows API 호출 탐지

rule Suspicious_API_Call
{
    strings:
        $re = /Create(Remote)?Thread/
    condition:
        $re
}
  • 의미: 악성 행위에서 자주 등장하는 API (CreateThread, CreateRemoteThread) 탐지

예시: 이메일 주소 내 포함된 코드 탐지

rule Encoded_Email_Exfiltration
{
    strings:
        $email = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/ nocase
    condition:
        $email
}

YARA는 Perl Compatible RegEx(PCRE)를 지원하며, 복잡한 탐지를 위해서는 조합적 조건 구성이 중요하다.

5.5 SIEM/EDR/CTI 룰에 적용되는 정규식

대부분의 보안 플랫폼은 탐지 정책이나 룰셋 구성 시 정규표현식 기반의 필터링 규칙을 허용한다.

SIEM (ex. Splunk, ELK)

  • Splunk: rex 명령어 사용
index=web_logs | rex field=_raw "GET\s+(?<url_path>\/\S+)\s+HTTP"
  • Elasticsearch + Logstash의 Grok 필터
match => { "message" => "%{IP:client} - - \[%{HTTPDATE:timestamp}\] \"%{WORD:method} %{URIPATHPARAM:uri}" }

EDR 룰 기반 예

  • Process Command Line에 powershell.+(download|invoke) 포함 여부 필터링
  • cmd\.exe\s+/c\s+net\s+user 같은 고전적 권한 탈취 행위 탐지

CTI 기반 IOC 매칭

  • 외부에서 수집한 IOC 목록(도메인, URL, 해시 등)을 정규표현식으로 필터링하여 실시간 대응
  • MISP, OpenCTI 등의 플랫폼에서도 RegEx 매칭 기능 활용

6. 정규표현식 테스트 및 디버깅 도구

6.1 온라인 정규식 테스트 플랫폼 (regex101, regexr 등)

정규표현식을 개발하고 실험할 때 가장 먼저 활용되는 것이 온라인 테스트 툴이다. 주요 기능은 다음과 같다:

대표적인 플랫폼

플랫폼특징
regex101.com가장 널리 사용됨. 실시간 매칭 확인, 표현식 해석, 오류 메시지 제공, 여러 언어 모드(Python, JS, PHP 등) 지원
regexr.com시각화에 강함. 예제 내장, 문법 팁, 문서 연결 탁월
regexplanet.com다양한 언어별 정규식 테스트 가능 (Java, Python, C#, Ruby 등)
regexstorm.net.NET 정규식 전용. Lookbehind 등 지원 여부 확인에 유리

실습 팁

  • 테스트 문자열을 실제 로그, URL 목록, 이메일 집합 등 실제 상황과 유사한 예시로 구성해야 한다.
  • 플랫폼에서 제공하는 "Explain" 탭을 확인하면 각 표현식의 작동 원리를 시각적으로 파악할 수 있다.
  • 복잡한 조건은 테스트 후 "Unit Test"처럼 케이스별 검증하는 습관이 필요하다.

6.2 주요 언어에서의 디버깅 팁 (Python, JS, Java 등)

Python

  • re 모듈 사용. re.compile()로 사전 컴파일 후 .search().match()로 테스트 가능.
import re

pattern = re.compile(r"\b\d{3}-\d{4}\b")
match = pattern.search("전화번호는 010-1234입니다.")
print(match)  # None (패턴 불일치)
  • 디버깅 팁
    - pattern.findall(text)로 매칭되는 모든 항목 출력
    - re.DEBUG 플래그로 컴파일 로그 출력 가능
re.compile(r"\d+", re.DEBUG)

JavaScript

  • RegExp 객체 사용. test(), match(), exec() 등을 활용
let pattern = /\b\d{3}-\d{4}\b/;
console.log(pattern.test("전화번호는 010-1234입니다."));  // true
  • 디버깅 팁
    - 브라우저 콘솔에서 실시간 테스트
    - Lookbehind는 최신 브라우저에서만 지원됨
    - matchAll()로 모든 그룹 추출 가능

Java

  • PatternMatcher 클래스 사용
Pattern pattern = Pattern.compile("\\b\\d{3}-\\d{4}\\b");
Matcher matcher = pattern.matcher("전화번호는 010-1234입니다.");
System.out.println(matcher.find());  // true
  • 디버깅 팁
    - matcher.group()으로 캡처된 결과 확인
    - Java는 일부 Perl 스타일 기능 미지원 (예: 조건부 표현식)

언어별 주의할 점 요약

언어주의사항
Python(?<=...) 등 Lookbehind 사용 가능, re.DEBUG 유용
JavaScriptLookbehind는 일부 환경에서만 지원
Java일부 고급 정규식 미지원, 그룹 추출은 matcher.group(n)
C# (.NET)가장 강력한 정규식 지원, 조건부 패턴까지 가능

6.3 오류 메시지 해석 및 문제 해결

자주 발생하는 오류 유형

오류 메시지 예시원인해결 방법
nothing to repeat*, + 앞에 올 표현식 없음a*가 아니라 *a처럼 잘못 작성된 경우
unterminated character class닫는 ] 누락[A-Z[A-Z]
lookbehind assertion is not fixed widthLookbehind 길이가 가변적임(?<=a+) → 불가. (?<=abc)처럼 고정 길이여야 함
invalid escape sequence백슬래시 두 개 필요 (\\)Python에서 "\d"는 에러 → "\\d" 또는 r"\d" 사용
stack overflow or catastrophic backtracking과도한 그룹/반복으로 인해 백트래킹 폭주불필요한 반복 제거, Lazy 사용 고려 (+?, *?)

디버깅 전략

  • 표현식 분해: 하나의 긴 표현식을 나누어 각 부분만 따로 테스트
  • 단계별 테스트: 입력 케이스를 단계별로 바꾸어 가며 매칭 결과 확인
  • 오류 메시지 검색: regex101의 설명 탭이나 StackOverflow 검색 활용
  • 주석 기능 활용: 일부 엔진(Python 등)에서는 (?x)를 사용해 정규식에 주석 삽입 가능
pattern = re.compile(r'''
    ^               # 시작
    [a-zA-Z0-9._%+-]+   # 사용자 이름
    @               # @ 기호
    [a-zA-Z0-9.-]+      # 도메인
    \.[a-zA-Z]{2,}$     # 최상위 도메인
''', re.VERBOSE)

7. 실무 환경에서의 최적화 전략

7.1 성능 문제와 NFA vs DFA 이해

정규식의 성능 문제는 대체로 정규식 엔진이 사용하는 NFA(Non-deterministic Finite Automaton)DFA(Deterministic Finite Automaton) 모델에 따라 달라진다. 이 두 모델은 정규식의 매칭 과정에서 중요한 차이점을 가지고 있다.

NFA (비결정적 유한 오토마톤)

특징: NFA는 여러 상태를 동시에 추적할 수 있기 때문에, 다양한 경로를 동시에 탐색한다. 이 방식은 그 자체로 매우 유연하지만, 성능 문제를 일으킬 수 있다.

성능 문제: 복잡한 정규식을 사용할 경우, 상태 전환이 과도하게 발생하여, 특히 백트래킹(backtracking) 이 많이 일어날 수 있다. 백트래킹은 시간 복잡도가 증가하게 하여 성능에 악영향을 미친다.

예시: .*a.*b.*c와 같은 패턴은 NFA에서 백트래킹을 발생시키기 쉽다.

DFA (결정적 유한 오토마톤)

특징: DFA는 단일 경로만 추적하며, 상태 전환이 고정적이므로 매우 빠르다. DFA는 매칭할 때 한 번에 유일한 경로를 선택하고, 백트래킹이 필요 없기 때문에 성능 면에서 유리하다.

성능 장점: 정규식이 단순하고, 미리 컴파일된 상태에서 매우 빠른 속도를 보인다.

예시: a.*b.*c와 같은 패턴은 DFA에서 효율적으로 처리된다.

결론

정규식을 작성할 때 성능 문제를 고려해야 할 필요가 있으며, 특히 긴 문자열이나 복잡한 정규식에서 NFA가 성능 저하를 일으킬 수 있다는 점을 유념해야 한다. DFA 방식은 빠르지만, 일부 복잡한 정규식에서는 상태 전환 테이블 크기가 커져 메모리 사용량이 증가할 수 있다.

7.2 복잡한 정규식의 리팩토링

복잡한 정규식은 가독성뿐만 아니라 성능에도 영향을 미친다. 따라서 정규식을 효율적으로 리팩토링하는 것이 중요하다.

중복되는 패턴 제거

복잡한 정규식에서 동일한 문자열 패턴이 반복될 경우, 이를 별도의 그룹으로 묶어서 중복을 최소화할 수 있다. 예를 들어, (\d{3}-\d{3}-\d{4})|(\d{4}-\d{3}-\d{4})와 같은 패턴은 (\d{3,4}-\d{3,4}-\d{4})로 리팩토링할 수 있다.

중복을 없애면 정규식이 간결해지고, 처리 시간이 줄어든다.

기법 적용 예시

단일 문자와 문자 집합 최적화: [^a-zA-Z0-9]와 같은 문자 집합을 사용하는 대신, 단순한 \W(모든 비단어 문자)로 교체하는 것이 성능을 개선할 수 있다.

반복자 사용 최소화: * 또는 +와 같은 반복자는 성능 저하를 초래할 수 있다. 이러한 기호는 조건을 추가하거나, 더 구체적인 패턴으로 바꿔 성능을 개선할 수 있다.

클러스터링 기법

정규식을 클러스터링하여 패턴의 부분을 재사용할 수 있다. 예를 들어, (\d{2}){3} 대신 (\d{2}){3,3}처럼 고정된 반복 범위를 지정하면 더 효율적으로 동작할 수 있다.

7.3 코드 내 정규식의 가독성 향상 방법

정규식의 가독성을 높이는 것은 유지보수와 협업에 중요한 요소이다. 이를 위해서는 코드 내에서 정규식 자체를 명확하고 이해하기 쉽게 작성해야 한다.

주석 활용

코드 내에서 정규식을 사용할 때는 정규식의 목적이나 동작을 주석으로 설명하는 것이 중요하다.

예를 들어, ^(?:\d{3}-\d{2}-\d{4})$와 같은 복잡한 정규식에 대해서는 "SSN 번호 형식"과 같은 설명을 덧붙이면 추후 코드 작성자나 리뷰어가 쉽게 이해할 수 있다.

정규식 변수화

정규식을 변수로 선언하여 의미를 부여할 수 있다. 예를 들어, 전화번호를 매칭하는 정규식을 phonePattern = r"\d{3}-\d{3}-\d{4}"로 정의하고, 이를 코드 내에서 사용하면 읽기가 쉬워진다.

형식에 따라 변수화: 여러 형식을 처리할 때 동일한 패턴을 변수로 선언해두고 필요할 때 호출하는 방식도 유용하다.

정규식의 모듈화

정규식을 함수로 묶어 복잡한 처리 로직을 분리할 수 있다. 예를 들어, 이메일을 검사하는 정규식을 함수로 정의하여 재사용하면 코드가 깔끔해진다.

7.4 공격자 우회 패턴 분석과 방어용 정규식

공격자는 종종 보안 시스템을 우회하기 위해 정규식을 우회하는 특수한 문자열을 사용한다. 이를 방어하는 정규식을 설계하는 것이 중요하다.

URL 및 XSS 필터링

XSS 공격을 방어하기 위해 정규식을 사용할 때는 </script>, <script>와 같은 문자열을 차단해야 한다. 그러나 공격자는 <ScRiPt>와 같은 방식으로 대소문자를 섞을 수 있기 때문에 이를 고려한 정규식이 필요하다.

따라서 (?i)<script>와 같이 대소문자를 무시하는 옵션을 사용해야 한다.

SQL Injection 탐지

SQL Injection 공격에서 공격자는 ' 또는 --와 같은 특수 문자를 사용하여 쿼리를 변경하려 한다. 이를 방어하는 정규식에서는 입력값에서 이러한 특수 문자를 차단하는 패턴을 사용해야 한다.

예를 들어, [\s;--'"]와 같은 패턴을 사용하여 공백, 세미콜론, 작은따옴표 등을 차단하는 방식이다.

우회 패턴 예시

공격자는 공백을 URL 인코딩하거나 여러 개의 < 문자를 연속해서 사용하여 패턴을 우회하려고 할 수 있다. 이를 방어하려면 정규식에서 문자열 길이나 패턴의 반복을 제한하는 등의 방어 기법을 적용해야 한다.


8. 다양한 플랫폼 및 언어에서의 차이점

8.1 PCRE, JavaScript, Python, POSIX 비교

정규식 엔진은 사용하는 플랫폼과 언어에 따라 특성이 다르다. 특히, PCRE(Perl Compatible Regular Expressions), JavaScript, Python, POSIX는 정규식을 처리하는 방식에서 주요한 차이점들이 존재한다.

1) PCRE (Perl Compatible Regular Expressions)

PCRE는 Perl 언어에서 사용된 정규식 규칙을 따르며, 여러 프로그래밍 언어와 툴에서 널리 사용된다. 주로 다음과 같은 특징이 있다:

  • 유연성: PCRE는 Perl의 정규식 문법과 매우 유사하여, 복잡한 정규식을 작성하는 데 유리하다.
  • 확장성: Lookahead, Lookbehind, 조건부 표현식 등의 고급 기능을 제공한다.
  • 모드: 기본적으로 단일 라인 모드를 사용하며, 멀티라인, 대소문자 무시 등의 다양한 모드를 제공한다.

2) JavaScript

JavaScript에서의 정규식은 ECMAScript 규격에 기반을 두고 있으며, 주요 특징은 다음과 같다:

  • 제한된 기능: JavaScript의 정규식은 PCRE와 비교하여 일부 고급 기능(예: Lookbehind, 조건부 표현식)을 지원하지 않는다.
  • 플랫폼 호환성: 웹 브라우저에서 바로 실행될 수 있어 매우 유용하다. 하지만 특정 기능은 일부 구형 브라우저에서 지원되지 않을 수 있다.
  • 정규식 객체: RegExp 객체를 사용하여 정규식을 처리하며, exec()test() 메서드가 주로 사용된다.

3) Python

Python의 정규식은 re 모듈을 통해 제공되며, 고급 정규식 기능을 지원한다. 주요 특징은 다음과 같다:

  • 다양한 기능: Python은 Lookahead, Lookbehind, 조건부 표현식 등 복잡한 정규식 기능을 지원한다.
  • 유연한 처리: re.compile()을 사용해 정규식을 미리 컴파일할 수 있으며, 패턴 객체를 재사용할 수 있어 성능 향상에 유리하다.
  • 디버깅 도구: re.DEBUG와 같은 디버깅 옵션을 통해 정규식의 작동 과정을 추적할 수 있다.

4) POSIX

POSIX는 유닉스 계열 시스템에서 사용하는 정규식 표준이다. 특징은 다음과 같다:

  • 기본적인 문법: POSIX 정규식은 PCRE와 비교하여 상대적으로 간단하고 제한적이다. 고급 기능은 제한적이며, 주로 기본적인 패턴 매칭에 사용된다.
  • 형식: POSIX는 Basic Regular Expression (BRE)Extended Regular Expression (ERE) 두 가지 모드로 나누어진다.
  • 호환성: POSIX 표준은 유닉스 시스템의 다양한 툴과 호환되며, 여러 시스템에서 일관된 동작을 보장한다.

비교 표

특징PCREJavaScriptPythonPOSIX
기능고급 기능 지원 (Lookahead, Lookbehind 등)제한적인 기능 (조건부, Lookbehind 미지원)고급 기능 지원 (Lookahead, Lookbehind 등)기본적인 기능만 지원
문법Perl 스타일ECMAScript 스타일Python 스타일POSIX 스타일
사용처Perl, PHP, 다양한 툴웹 브라우저, Node.jsPythonUnix/Linux 시스템
호환성고급 기능을 지원하는 다양한 플랫폼일부 브라우저에서 제한적 지원Python 환경 내에서 안정적 지원Unix 시스템에서 널리 사용됨

8.2 언어별 기능 차이와 호환성 문제

정규식을 다루는 언어마다 제공하는 기능이 다르며, 이로 인해 발생하는 호환성 문제도 존재한다. 이러한 문제를 해결하기 위해서는 각 언어가 제공하는 정규식 엔진의 차이를 이해하고, 그것에 맞게 정규식을 작성해야 한다.

1) 언어별 기능 차이

  • Lookbehind: JavaScript는 Lookbehind를 지원하지 않지만, Python과 PCRE에서는 Lookbehind를 사용할 수 있다. 이는 일부 복잡한 패턴을 다룰 때 큰 차이를 만든다.

  • 예시:

    • Python: (?<=\d{3})abc (3자리 숫자 뒤에 오는 "abc"를 매칭)
    • JavaScript: 지원하지 않음
  • 조건부 표현식: POSIX와 JavaScript에서는 조건부 표현식을 사용할 수 없다. 이에 비해 Python과 PCRE는 조건부 표현식을 지원하여, 패턴 내에서 더 복잡한 논리적 분기를 구현할 수 있다.

2) 호환성 문제

  • 호환성 이슈: 동일한 정규식이 서로 다른 엔진에서 다르게 동작할 수 있다. 예를 들어, Python에서 동작하는 정규식이 POSIX 환경에서는 동작하지 않을 수 있다. 이 경우, 정규식을 다시 작성하거나 특정 엔진에 맞는 형태로 변환해야 한다.

  • 고급 기능의 제한: 일부 고급 기능(예: 조건부 표현식, Lookbehind 등)은 모든 언어에서 지원되지 않기 때문에, 크로스 플랫폼에서의 호환성 문제가 발생할 수 있다. 이를 해결하기 위해서는 각 언어에 맞는 대체 패턴을 찾아야 한다.

8.3 플랫폼별 정규식 엔진의 한계와 특성

각 플랫폼에서 제공하는 정규식 엔진은 고유한 특성을 가지며, 이에 따라 성능이나 기능에 차이가 있을 수 있다. 이는 정규식을 작성할 때 중요한 요소가 된다.

1) PCRE 엔진의 특성

  • 장점: 강력한 기능을 제공하며, 다양한 문법을 지원한다. 특히 Perl 스타일의 문법을 따르므로, 복잡한 패턴을 쉽게 작성할 수 있다.
  • 단점: 성능이 중요할 때는 과도한 백트래킹이나 복잡한 패턴으로 인해 성능 문제가 발생할 수 있다.

2) JavaScript 엔진의 특성

  • 장점: 웹에서 바로 사용할 수 있기 때문에 웹 개발에 적합하다. 간단한 정규식 처리에서 빠른 성능을 보인다.
  • 단점: 고급 기능(예: Lookbehind)을 지원하지 않기 때문에, 일부 복잡한 정규식을 다루는 데 어려움이 있을 수 있다.

3) Python 엔진의 특성

  • 장점: 강력한 정규식 기능을 제공하며, 복잡한 패턴 처리에서 매우 유용하다. re.DEBUG와 같은 디버깅 기능을 통해 코드 최적화가 용이하다.
  • 단점: 정규식이 복잡해질수록 성능 저하가 발생할 수 있으며, 적절한 최적화가 필요하다.

4) POSIX 엔진의 특성

  • 장점: Unix 계열 시스템에서 안정적이고, 널리 사용된다. 간단한 패턴 매칭에 최적화되어 있다.
  • 단점: 고급 정규식 기능이 부족하여, 복잡한 패턴을 처리하는 데 한계가 있다. 특히 조건부 표현식이나 Lookbehind는 지원되지 않는다.

9. 자주 쓰이는 정규표현식 패턴 모음

9.1 이메일, 도메인, URL, IP 정규식

정규식은 이메일 주소, URL, IP 주소와 같은 텍스트를 추출하거나 유효성을 검사하는 데 널리 사용된다.

1) 이메일 정규식

이메일 주소는 사용자 이름과 도메인 이름으로 구성된다. 이 패턴은 사용자 이름과 도메인 이름의 구문을 검증하기 위해 사용된다.

  • 정규식 예시:
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
  • 설명:
    • ^[a-zA-Z0-9._%+-]+: 이메일의 사용자 이름을 검증 (영문, 숫자, 특수기호 허용)
    • @[a-zA-Z0-9.-]+: @ 기호 이후 도메인 이름
    • \.[a-zA-Z]{2,}$: 도메인의 최상위 도메인(TLD) 부분

2) 도메인 정규식

도메인 이름을 검증할 때 유용한 정규식이다. 도메인 이름은 알파벳과 숫자, 하이픈으로 구성되며, 끝은 최상위 도메인(TLD)이어야 한다.

  • 정규식 예시:
^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$
  • 설명:
    • ^[a-zA-Z0-9-]+: 도메인 이름의 첫 부분 (알파벳, 숫자, 하이픈 허용)
    • \.[a-zA-Z]{2,}$: 마침표 이후의 최상위 도메인 (2자 이상)

3) URL 정규식

URL의 유효성을 검사하는 정규식이다. HTTP, HTTPS, FTP 등의 다양한 프로토콜을 포함하여 URL을 검사할 수 있다.

  • 정규식 예시:
^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$
  • 설명:
    • ^(https?|ftp): HTTP 또는 FTP 프로토콜
    • :\/\/: :// 구분 기호
    • [^\s/$.?#].[^\s]*$: URL 경로 및 쿼리 문자열 부분 (공백, /, ?, # 등의 특수 문자 제외)

4) IP 주소 정규식

IPv4 주소를 검증하기 위한 정규식이다. IPv4는 네 개의 0~255 범위의 숫자로 이루어진다.

  • 정규식 예시:
^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$

설명:

  • 25[0-5]: 250~255 범위의 숫자
  • 2[0-4][0-9]: 200~249 범위의 숫자
  • [01]?[0-9][0-9]?: 0~199 범위의 숫자

이 정규식은 IPv4 주소 형식을 정확하게 검증할 수 있다.

9.2 파일 경로, 버전 문자열, 해시 패턴

정규식은 파일 경로, 버전 문자열, 해시 값 등 다양한 시스템 정보를 검증하는 데 유용하게 사용된다.

1) 파일 경로 정규식

파일 경로는 운영 체제에 따라 다를 수 있다. 윈도우와 유닉스 계열 시스템에서 파일 경로 형식이 다르기 때문에 이를 고려한 정규식이 필요하다.

  • 정규식 예시 (유닉스/리눅스):
^(/[^/ ]*)+/?$
  • 설명:
    • /[^/ ]*: 각 디렉토리 이름이 공백이나 /가 아닌 문자열로 구성
    • /?$: 마지막에는 슬래시(/)가 올 수 있고, 끝날 수 있음

2) 버전 문자열 정규식

버전 문자열은 보통 "주버전.부버전.수정버전" 형식이다. 예를 들어, 1.0.0, 2.1.0 등이 있다.

  • 정규식 예시:
^\d+\.\d+\.\d+$
  • 설명:
    • \d+: 숫자 1개 이상
    • \.: 점 (버전 구분자)
    • 이 정규식은 1.0.0과 같은 간단한 버전 번호 형식을 검증할 수 있다.

3) 해시 패턴 정규식

MD5, SHA-1, SHA-256과 같은 해시 값은 고정된 길이의 16진수 문자열이다. 각 해시 알고리즘에 대한 패턴을 정의할 수 있다.

  • 정규식 예시 (MD5):
^[a-f0-9]{32}$
  • 설명:
    - [a-f0-9]{32}: 16진수 값이 32자리
    - 이는 MD5 해시 값의 표준 형식을 검증하는 정규식이다.

9.3 주민등록번호, 전화번호 등 개인정보 탐지

정규식을 활용하여 개인정보를 추출하거나 검증할 수 있다. 예를 들어, 주민등록번호나 전화번호는 국가별로 형식이 다르지만, 기본적인 구조는 고정적이다.

1) 주민등록번호 정규식

한국의 주민등록번호는 6자리 숫자-7자리 숫자 형식

  • 정규식 예시:
^\d{6}-\d{7}$
  • 설명:
    • \d{6}: 6자리 숫자 (생년월일)
    • -\d{7}: 하이픈 뒤 7자리 숫자

2) 전화번호 정규식

전화번호는 국가별로 형식이 다르지만, 일반적인 형태로 검증할 수 있다.

  • 정규식 예시:
^\d{2,3}-\d{3,4}-\d{4}$
  • 설명:
    - \d{2,3}: 2자리 또는 3자리 숫자 (지역 번호)
    - -\d{3,4}-\d{4}: 하이픈 구분을 포함한 7자리 또는 8자리 숫자

9.4 로그 분석용 커스텀 정규식 예제

로그 파일에서 특정 패턴을 추출하거나 필터링할 때 자주 사용되는 정규식 예제를 소개한다.

1) IP 주소 로그 필터링

서버 로그에서 IP 주소를 추출할 때 유용하다. 일반적으로 각 로그 항목에는 요청을 보낸 클라이언트의 IP 주소가 포함된다.

  • 정규식 예시:
^(\d{1,3}\.){3}\d{1,3}
  • 설명:
    • (\d{1,3}\.): 1~3자리 숫자와 마침표
    • {3}: 3번 반복 (IPv4 주소의 각 부분)
    • \d{1,3}: 마지막 1~3자리 숫자
    • 이 정규식은 로그에서 IP 주소를 추출하는 데 유용하다.

2) 로그에서 시간 추출

서버 로그에는 보통 요청이 발생한 시간이 포함된다. 로그에서 시간을 추출하기 위한 정규식이다.

  • 정규식 예시:
\[(\d{2}/[A-Za-z]+/\d{4}:\d{2}:\d{2}:\d{2} \+\d{4})\]
  • 설명:
    - \[(\d{2}/[A-Za-z]+/\d{4}:\d{2}:\d{2}:\d{2} \+\d{4})\]: 대괄호 안에 있는 날짜와 시간
    - 이 정규식은 로그에서 시간 정보를 추출하는 데 사용된다.

10. 보안 관점에서의 정규표현식

정규표현식은 보안 분야에서 중요한 역할을 하며, 로그 분석, 입력 검증, 악성 코드 탐지 등의 작업에 널리 사용된다. 그러나 정규표현식이 제대로 사용되지 않으면 보안상 취약점이 발생할 수 있으며, 특히 ReDoS 공격과 같은 위험을 초래할 수 있다.

10.1 ReDoS (Regular Expression Denial of Service) 공격

ReDoS 공격은 정규표현식 서비스 거부 공격으로, 정규표현식의 비효율적인 설계로 인해 시스템 성능을 저하시켜 서비스 거부 상태를 초래하는 공격이다. 이는 주로 정규표현식이 catastrophic backtracking 문제에 취약할 때 발생한다. 악의적인 사용자는 고의로 입력값을 조작하여 정규표현식 엔진이 지나치게 많은 시간을 소모하도록 할 수 있다.

ReDoS 공격이 발생하는 원리

정규표현식 엔진은 텍스트와 패턴을 매칭할 때 각 경우의 수를 탐색한다. 복잡한 정규표현식이나 비효율적인 반복문을 사용하면, 특히 반복 연산자(*, +, ?, {m,n} 등)와 분리된 패턴들이 서로 맞물릴 때 계산 시간이 급격히 증가할 수 있다. 이러한 현상은 백트래킹(backtracking) 이라고 하며, 비효율적인 정규표현식은 수천, 수백만 번의 백트래킹을 유발할 수 있다.

ReDoS 공격 예시

  • 정규표현식:
^(a|aa)+$
  • 입력값: "aaaaaaaaaaaaa..." (길이가 매우 긴 문자열)

  • 이 정규표현식은 a 또는 aa의 반복을 허용한다. 긴 입력값이 주어졌을 때, 정규표현식 엔진은 가능한 모든 방법으로 입력값을 분할하여 매칭을 시도한다. 이로 인해 catastrophic backtracking이 발생하며, 시스템 자원을 과도하게 소모하게 된다.

ReDoS 공격을 방지하는 방법

  1. 정규표현식 최적화: 반복문(*, +, ?)을 최소화하고, 가능하면 명확하게 범위를 지정하는 것이 좋다.
  2. 타임아웃 설정: 정규표현식 엔진에 처리 시간 제한을 설정하여, 백트래킹이 지나치게 많이 발생하면 강제로 실행을 중단하도록 할 수 있다.
  3. 정규표현식 검사 도구 사용: regex101 같은 도구에서 성능을 테스트하고, 백트래킹이 발생할 가능성이 높은 패턴을 점검할 수 있다.

10.2 입력값 검증 vs 허용 목록 접근 방식

입력값 검증은 보안에서 중요한 부분이며, 악의적인 사용자로부터 시스템을 보호하기 위해 필수적이다. 정규표현식을 활용한 입력값 검증은 두 가지 주요 접근 방식으로 나눠볼 수 있다.

1) 입력값 검증 (Input Validation)

입력값 검증은 모든 입력을 검증하여 허용되지 않는 데이터를 필터링하는 방식이다. 정규표현식을 사용하여 예상되는 형식만을 허용하고, 그 외의 데이터를 거부할 수 있다. 예를 들어, 이메일 주소, 전화번호, 주민등록번호 등의 입력 형식을 검증할 때 사용된다.

  • 장점:

    • 사용자가 제공하는 데이터를 정확하게 통제할 수 있다.
    • 유효하지 않은 입력을 조기에 차단하여 시스템의 무결성을 보호할 수 있다.
  • 단점:

    • 너무 제한적인 규칙을 설정하면 유효한 입력까지 거부할 수 있다.
    • 모든 경우를 처리하는 것이 어려울 수 있다. (예: 다국적 전화번호 형식)

2) 허용 목록 접근 방식 (Whitelisting)

허용 목록 접근 방식은 특정 조건을 만족하는 정확히 정의된 입력만 허용하는 방식이다. 이는 입력값 검증과는 반대로, 허용된 항목만을 명시적으로 정의하는 것이다.

  • 장점:

    • 보안적으로 강력하다. 예상치 못한 형식의 데이터는 차단된다.
    • 악성 코드나 예상하지 못한 입력값이 시스템에 들어올 가능성이 줄어든다.
  • 단점:

    • 예상치 못한 입력이나 다양한 형식을 모두 다룰 수 없어 사용자가 불편할 수 있다.

적절한 접근 방식 선택

보안적으로 가장 강력한 방법은 허용 목록 접근 방식을 사용하는 것이다. 하지만 현실적으로 입력값 검증을 통해 허용되는 형식을 명확히 정의하는 것이 더 직관적이고 구현이 쉬운 경우가 많다. 두 접근 방식은 보안 요구 사항에 맞춰 적절히 결합하여 사용하는 것이 좋다.

10.3 보안 룰 작성 시 정규표현식의 역할

보안 룰 작성 시 정규표현식은 시스템의 무결성, 데이터 유효성, 악성 코드 탐지 등을 위해 매우 중요하다. 보안 시스템에서는 정규표현식을 사용하여 악성 패턴을 감지하고, 공격을 차단하는 데 활용한다.

1) 웹 애플리케이션 방화벽 (WAF)

WAF는 웹 애플리케이션에서 발생할 수 있는 다양한 공격 (SQL Injection, XSS 등)을 방어하는 역할을 한다. 이때, 정규표현식을 사용하여 악성 요청 패턴을 탐지하고, 차단할 수 있다.

  • 예: (<script>), ' OR 1=1 --와 같은 SQL 인젝션 또는 XSS 공격 패턴을 정규표현식으로 검출

2) 로그 모니터링 및 침입 탐지 시스템 (IDS)

정규표현식은 로그 분석침입 탐지 시스템(IDS) 에서도 핵심적인 역할을 한다. 특정 패턴이나 키워드를 탐지하여 공격 징후를 추적하고, 알림을 발생시킨다.

  • 예: 시스템 로그에서 failed login attempt와 같은 패턴을 탐지하거나, 악성 스크립트가 포함된 요청을 차단

3) 악성 코드 탐지

정규표현식은 악성 코드 탐지에도 사용된다. 예를 들어, YARASuricata와 같은 도구에서는 정규표현식을 사용하여 특정 파일 서명이나 패턴을 탐지한다.

정규표현식은 특정 바이러스나 악성 파일의 서명을 추적하는 데 유용하다. 악성 코드의 특정 문자열이나 특정 동작을 정의할 수 있다.

10.4 YARA, Suricata, Sigma에서의 정규식 적용

정규표현식은 다양한 보안 도구에서 악성 코드 탐지 및 침입 탐지의 중요한 도구로 활용된다. 여기에서는 YARA, Suricata, Sigma와 같은 보안 툴에서 정규표현식을 어떻게 적용하는지 살펴본다.

1) YARA

YARA는 파일이나 프로세스에서 특정 악성 코드 서명이나 패턴을 검출하는 데 사용된다. YARA 룰에서 정규표현식을 사용하여 바이러스나 악성 코드의 특성을 정의할 수 있다.

  • YARA 룰 예시:
rule MalwareExample
{
    strings:
        $malware_pattern = /bad_pattern_of_malware/i
    condition:
        $malware_pattern
}

YARA는 정규표현식을 통해 특정 문자열을 검색하여 악성 코드 또는 의심스러운 파일을 탐지한다.

2) Suricata

Suricata는 고급 침입 탐지 시스템(IDS) 및 침입 방지 시스템(IPS)으로, 네트워크 트래픽을 분석하고 악성 활동을 탐지한다. Suricata는 정규표현식을 활용하여 네트워크 트래픽에서 악성 패턴을 탐지할 수 있다.

  • Suricata 규칙 예시:
alert http any any -> any any (msg:"SQL Injection attempt"; content:"' OR 1=1 --"; classtype:attempted-user; sid:1000001;)

Suricata는 content에 정규표현식을 사용할 수 있으며, 이를 통해 SQL 인젝션, XSS 등 여러 종류의 공격을 탐지할 수 있다.

3) Sigma

Sigma는 다양한 보안 정보 및 이벤트 관리(SIEM) 시스템에 대한 공통된 로그 기반 탐지 규칙을 작성하는 프레임워크이다. Sigma에서는 정규표현식을 사용하여 로그에서 특정 이벤트나 패턴을 식별할 수 있다.

  • Sigma 규칙 예시:
title: Potential SQL Injection
logsource:
  product: windows
detection:
  selection:
    EventID: 1234
    Query: /' OR 1=1 --

Sigma 규칙에서 정규표현식은 로그 필터링 및 패턴 매칭에 사용된다. 이를 통해 악성 코드 또는 공격을 추적하고 탐지할 수 있다.


11. 마무리

역대급으로 힘들었던 주제였다. 내용도 내용이지만, 실제 플랫폼/툴에서 덩규표현식이 사용되는 예시를 하나하나 찾기 힘들어 gpt를 이용해 예시를 받았는데, 또 그걸 이해한다고 시간을 쓰다 보니 초기에 상정한 시간에 비해 한참 더 걸렸다.

그래도 그만큼 배운 것들이 많긴 하지만... 앞으로도 daily라는 작성 기조를 이어나가려면 조금은 템포 조절을 해야 할 것 같다는 생각이 든다. 다음주 주제는 조금 가벼운 걸로 잡아봐야겠다.

profile
안드로이드는 리눅스의 꿈을 꾸는가

0개의 댓글