개요
피싱 메일에서 보통 상위 공격자들은 메일 헤더의 spf 필드를 조작하는 방법도 사용하지만, 메이저한 도메인의 오타를 유발할 수 있는 혹은 비슷한 형태를 띄는 도메인을 직접 구매하여 사용하는 경우가 많음 (ex: origin == naver.com fake == naever.com)
해당 메이저한 도메인과 비슷한 형태를 띄는 도메인들은 사용자의 착각을 유발할 수 있으며 피싱 메일뿐만 아니라, 사용자가 직접 url 입력창에 url 주소를 직접 쳐서 페이지를 접근하는 사용자들의 오타를 유발하여 피싱 페이지로 접근되게도 유발할 수 있음
알고리즘 종류
처음에는 "단순하게 몇몇개의 문자열이 일치하면" 과 같이 단순하게 접근했지만, 정확성이 낮아질 것 같다는 생각이 들었고, 문자열의 유사성을 프로젝트 특성에 맞게 비교할 수 있는 알고리즘에 대해서 리서치를 진행하였음
문자열의 유사성을 확인할 수 있는 여러 알고리즘이 존재했지만 도메인과 같이 짧은 문자열을 대상으로 효율적으로 사용이 가능해야 한다는 점에서 아래와 같이 몇개의 대표적인 알고리즘이 추려졌고 복잡한 수식보다는 특징에 대하여 정리하여 설명함
1. Jaro-Winkler Distance
자로-윙클러 유사성 알고리즘은 짧은 문자열에 특화된 유사성 검사 알고리즘임
문자열의 시작 부분이 일치하면 높은 점수를 부여하여 유사성을 측정한다는 점에서 피싱에 사용되는 피해자를 기만하기 위해 사용되는 악성 도메인 탐지에 적합하다고 판단됨
ex:
origin == naver.com fake == naever.com --> 앞 두 글자가 매칭되므로 유사성에 높은 점수를 부여
2. Levenshtein Distance
레벤슈타인 거리 알고리즘은 두개의 문자열이 얼마나 다른지 나타내는 척도를 지닌 유사성 검사 알고리즘임
레벤슈타인 거리 알고리즘은 삽입, 삭제, 대치 3과정을 통해 유사성을 검사할 수 있으며, 해당 3과정의 횟수를 세어서 횟수가 클수록 문자열이 다름을 나타내고, 횟수가 작을수록 비슷한 문자열이라고 나타낼 수 있음
ex:
1. 삽입: cat vs cats (1회 카운트)
2. 삭제: apple vs apples (1회 카운트)
3. 대치: book vs cook (1회 카운트)
예제 (go)
코드
package main
import (
"fmt"
"github.com/adrg/strutil/metrics"
)
func main() {
domain1 := "google.com"
domainType1 := "gogle.com"
domainType2 := "googel.com"
jw := &metrics.JaroWinkler{}
jaroWinklerDistance := jw.Compare(domain1, domainType2)
fmt.Printf("Jaro-Winkler distance between '%s' and '%s': %.2f\n", domain1, domainType2, jaroWinklerDistance)
levenshteinDistance := metrics.NewLevenshtein().Distance(domain1, domainType1)
fmt.Printf("Levenshtein distance between '%s' and '%s': %d\n", domain1, domainType1, levenshteinDistance)
}
결과
Jaro-Winkler distance between 'google.com' and 'googel.com': 0.98
Levenshtein distance between 'google.com' and 'gogle.com': 1
자로-윙클러의 경우 0 ~ 100% 수치로, 레벤슈타인의 경우 횟수로 나타나는 것을 확인할 수 있음