안녕하세요! 프론트엔드 강사입니다.
오늘은 CSS를 다루면서 무심코 지나치기 쉬운, 하지만 아주 흥미롭고 중요한 보안 이슈에 대해 이야기해 보려고 합니다. 바로 :visited 가상 클래스 선택자와 프라이버시(개인정보 보호)의 관계입니다.
"어? 내가 방문했던 링크 색깔 바꿔주는 그 단순한 속성이 보안이랑 무슨 상관이지?"라고 생각하실 수 있는데요. 과거에는 이 순진해 보이는 속성이 사용자들의 인터넷 방문 기록을 탈탈 털어가는(?) 무시무시한 해킹 도구로 쓰이곤 했습니다. 어떻게 그런 일이 가능했는지, 그리고 브라우저가 이 문제를 어떻게 해결했는지 MDN 공식 문서를 통해 낱낱이 파헤쳐 드리겠습니다!
:visited 선택자 (Privacy and the :visited selector)원래 초창기의 CSS :visited 선택자는 프라이버시(개인정보 보호) 및 보안상 심각한 위험 요소였습니다. 아주 약간의 자바스크립트 코드만 있으면, 악의적인 웹사이트들은 사용자의 브라우징 히스토리를 몰래 뒤져서 이 사용자가 예전에 어떤 사이트들을 방문했었는지 알아낼 수 있었거든요.
이런 해킹은 window.getComputedStyle 같은 메서드나 다른 여러 기법들을 이용해서 이루어졌습니다. 이 과정은 순식간에 일어났고, 웹사이트들은 단순히 사용자가 과거에 어디를 돌아다녔는지 알아내는 것을 넘어서서, 그 방문 기록을 바탕으로 사용자의 성향이나 신원에 대한 엄청난 정보들을 유추해 낼 수 있었습니다.
👨🏫 강사님의 꿀팁 & 보충설명:
어떻게 해킹했는지 원리가 궁금하시죠? 해커들은 페이지에 투명하고 작은 링크 수천 개(예: 은행, 병원, 정치 사이트 등)를 쫙 깔아둡니다. 그리고 CSS로:visited { color: red; }를 주고,:link { color: blue; }를 주죠.
그 다음 자바스크립트(getComputedStyle)로 그 링크들의 색깔을 하나씩 검사합니다. 만약 "빨간색"으로 나온 링크가 있다면? "아하! 이 사용자는 이 사이트를 방문한 적이 있구나!" 하고 낚아채서 서버로 몰래 전송하는 겁니다. 소름 돋지 않나요?
이러한 끔찍한 프라이버시 문제를 완화하기 위해, 브라우저 제조사들은 이 방문한 링크(:visited)로부터 얻어낼 수 있는 정보의 양을 아주 엄격하게 제한하기로 결정했습니다.
사용자의 개인정보를 지켜주기 위해, 최신 브라우저들은 특정한 상황에서 웹 애플리케이션(자바스크립트 등)에게 선의의 '거짓말'을 합니다.
window.getComputedStyle 메서드나 element.querySelector 같은 함수들을 통해 링크의 스타일이나 상태를 물어보면, 브라우저는 사용자가 해당 페이지의 링크를 단 한 번도 방문한 적이 없다고 무조건 거짓말을 합니다. (실제로는 사용자가 방문해서 화면에는 :visited 스타일로 보여도, 자바스크립트한테는 무조건 기본 :link 스타일 정보만 던져줍니다.):visited + span 처럼 쓸 경우, 브라우저는 이 인접한 요소(span)를 그 링크가 아직 방문되지 않은 것처럼(즉, 기본 상태처럼) 스타일을 적용해 버립니다.👨🏫 강사님의 보충설명:
한마디로 브라우저 엔진 차원에서 자바스크립트의 눈을 완벽하게 가려버린 겁니다! 화면에 그릴 때는 방문한 링크처럼 그려주지만, 자바스크립트가 코드로 검사하려고 하면 철저하게 "난 모르는 일이야" 하고 잡아떼는 거죠. 이것이 바로 '하얀 거짓말'입니다.
여전히 방문한 링크에 스타일을 줄 수는 있지만, 아무 속성이나 막 쓸 수는 없습니다. 쓸 수 있는 스타일에 아주 강력한 제한이 생겼어요. 오직 아래에 나열된 속성들만 :visited 상태에서 변경할 수 있습니다:
color (글자색)background-color (배경색)border-color (그리고 관련된 하위 속성들)column-rule-coloroutline-colortext-decoration-colortext-emphasis-colorfill 속성과 stroke 속성의 색상 부분눈치채셨나요? 맞습니다. 오직 '색상(color)'과 관련된 속성들만 바꿀 수 있습니다! 마진, 패딩, 배경 이미지 등 레이아웃에 영향을 주거나 다른 리소스를 불러오는 속성은 절대 쓸 수 없어요.
더 나아가, 심지어 위에서 언급한 색상 속성들을 쓸 때조차도, 방문하지 않은 링크와 방문한 링크 간의 투명도(transparency) 차이는 절대 적용되지 않습니다. 이 제한 때문에 다양한 <color> 함수(예: rgba())에서 alpha(투명도) 파라미터를 조작하거나 transparent 키워드를 사용해서 이 두 상태(방문/미방문)를 구별하려는 시도는 모두 막히게 됩니다.
앞서 말씀드린 제한 사항들을 염두에 두고 스타일을 사용하는 올바른 예시를 살펴볼까요?
:link {
outline: 1px dotted blue;
background-color: white;
/* `background-color`의 기본값은 원래 `transparent(투명)`입니다.
반드시 여기서 투명하지 않은 다른 '초기 색상'을 명시적으로 지정해 주어야 합니다.
그렇지 않으면 아래 `:visited`에서 배경색을 바꾸려고 해도 투명도 제한에 걸려 적용되지 않습니다! */
}
:visited {
outline-color: orange; /* 방문한 링크는 주황색 아웃라인을 가집니다. */
background-color: green; /* 방문한 링크는 초록색 배경을 가집니다. */
color: yellow; /* 방문한 링크는 노란색 글씨를 가집니다. */
}
👨🏫 강사님의 꿀팁:
여기서 정말 많이들 실수하십니다. 평소에 링크 배경이 투명하길래 아무 생각 없이 놔뒀다가, 방문했을 때만 배경을 검게(:visited { background-color: black; }) 바꾸려고 하면 브라우저가 이걸 무시해 버립니다!
브라우저 입장에선 '투명'에서 '불투명'으로 바뀌는 것도 투명도가 변하는 것으로 간주해서 앞서 말한 '투명도 차이 금지' 룰에 걸리기 때문이에요. 그래서 반드시 초기 상태(:link)에background-color: transparent가 아닌 명시적인 색상(예:white)을 미리 선언해 두어야만 정상적으로 작동합니다. 잊지 마세요!
여러분들이 웹 사이트를 개발하실 때는, 다음과 같은 사항들을 반드시 고려하셔야 합니다:
background-image) 하면 작동하지 않습니다. 왜냐하면 오직 색상(colors)으로만 방문한 링크를 꾸밀 수 있기 때문입니다.:visited 선택자를 통해 색상을 주려고 할 때, 만약 그 색상이 투명(transparent)하거나 투명도가 포함된 색이라면 스타일이 무시되고 적용되지 않습니다.이 흥미진진한 CSS 역사에 대해 더 자세히 알고 싶으시다면 아래 문서를 꼭 읽어보세요!
:visited 선택자를 통한 사용자 히스토리 공격 방지하기 (Preventing attacks on a user's history through CSS :visited selectors) - Mozilla 보안 블로그 (2010년 글)이 페이지가 도움이 되었나요? (Was this page helpful to you?)
[예 (Yes)]
[아니요 (No)]
이 페이지는 MDN 기여자들에 의해 2025년 11월 7일에 마지막으로 수정되었습니다.
오늘의 이야기는 여기까지입니다! 우리가 무심코 지나치는 CSS 속성 하나에도 이렇게 사용자를 보호하기 위한 브라우저 개발자들의 엄청난 고민과 눈물(?)이 담겨있다는 사실, 정말 재미있지 않나요? 앞으로 :visited를 쓰실 때는 왜 색상밖에 못 바꾸는지 꼭 기억하시길 바랍니다! 수고하셨습니다.