치환 문자열에 $가 들어 있었더니 엉뚱한 내용이 삽입된 이유
문제 발생
템플릿의 자리표시자를 사용자 입력으로 바꾸는 코드였습니다.
const template = "안녕하세요, {name}님";
const result = template.replace("{name}", userInput);닉네임이 $&인 사용자에서 결과가 안녕하세요, {name}님이 됐고, $'인 사용자에서는 뒷부분이 통째로 복제됐습니다.
원인 분석
치환 문자열에는 특수 패턴이 있습니다. MDN이 표로 정리해둔 그대로입니다.
| 패턴 | 삽입되는 것 |
|---|---|
$$ | $ 한 글자 |
$& | 매칭된 부분 문자열 |
$` | 매칭 앞부분 전체 |
$' | 매칭 뒷부분 전체 |
$n | n번째 캡처 그룹 |
$<Name> | 이름 있는 캡처 그룹 |
즉 두 번째 인자는 평범한 문자열이 아니라 작은 템플릿 언어입니다. 사용자 입력이 그 자리에 들어가면 입력이 코드처럼 해석됩니다. 눈에 잘 안 띄지만 성격은 인젝션과 같습니다.
문서에는 흔히 놓치는 규칙이 하나 더 있습니다 — 문자열 패턴은 한 번만 치환됩니다. 전역 검색·치환을 하려면 g 플래그가 붙은 정규식을 쓰거나 replaceAll()을 사용하십시오.
해결 방안
- 치환값이 데이터라면 함수를 넘깁니다. MDN이 명시합니다 — 위의 특수 치환 패턴은 replacer 함수가 반환한 문자열에는 적용되지 않습니다.
const result = template.replace("{name}", () => userInput); // 안전- 여러 번 바꿔야 하면
replaceAll을 씁니다. 문자열 패턴 한 번 치환 규칙을 잊고 "왜 첫 개만 바뀌지" 하는 상황을 없앱니다.
template.replaceAll("{name}", () => userInput);- 직접 이스케이프해야 한다면
$만 처리하면 됩니다.
const safe = userInput.replaceAll("$", "$$$$"); // $ → $$읽기 어려운 코드라서, 가능하면 1번 방식을 씁니다.
-
애초에 문자열 조합이 맞는지 봅니다. 화면에 그릴 값이라면 템플릿 치환보다
`안녕하세요, ${name}님`이나 React의 보간이 더 안전하고 명확합니다. -
정규식을 만들 때 사용자 입력을 그대로 넣지 않습니다. 같은 성격의 다른 구멍입니다 —
new RegExp(userInput)은 문법 오류나 성능 문제(ReDoS)로 이어질 수 있습니다. -
이런 값으로 테스트합니다.
$&,$',$1,$$는 실제 닉네임으로도 충분히 들어올 수 있는 문자열입니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.