전체 그래프
OWASP Top 10

Clickjacking

web-securityowaspclickjackingattack

상위: Web Security

요약

Clickjacking(클릭재킹, UI Redressing)은 투명한 iframe으로 피해자가 의도하지 않은 버튼이나 링크를 클릭하게 만드는 공격입니다. 사용자의 클릭을 "가로채서" 악성 행위를 수행합니다.

공격 원리

[보이는 화면]              [실제 구조]
┌─────────────────┐       ┌─────────────────┐
                         투명한 iframe    
  🎁 경품받기!           (실제 은행 사이트) 
    [클릭!]                 [송금 버튼]      실제 클릭됨
                                         
└─────────────────┘       └─────────────────┘

사용자 생각: "경품받기 버튼 클릭" 실제 동작: "은행 송금 버튼 클릭"

공격 코드

기본 Clickjacking

<!DOCTYPE html>
<html>
<head>
    <style>
        .decoy {
            position: absolute;
            width: 300px;
            height: 100px;
            z-index: 1;  /* 뒤에 배치 */
        }
        iframe {
            position: absolute;
            width: 300px;
            height: 100px;
            opacity: 0;  /* 투명하게 */
            z-index: 2;  /* 앞에 배치 */
        }
    </style>
</head>
<body>
    <div class="decoy">
        <button>🎁 무료 경품 받기!</button>
    </div>
    <iframe src="https://bank.com/transfer?to=attacker&amount=10000"></iframe>
</body>
</html>

드래그 앤 드롭 Clickjacking

<style>
    #source { width: 200px; height: 50px; }
    #target { position: absolute; opacity: 0; }
</style>

<div id="source" draggable="true">
    이 상자를 드래그하세요!
</div>
<iframe id="target" src="https://target.com/sensitive-action"></iframe>

<script>
    document.getElementById('source').ondragend = function() {
        // 드롭 시 iframe 내 요소에 데이터 전달
    };
</script>

커서 조작 Clickjacking

<style>
    body { cursor: none; }
    .fake-cursor {
        position: fixed;
        pointer-events: none;
        /* 실제 커서 위치에서 오프셋 */
        transform: translate(200px, 100px);
    }
</style>
<img class="fake-cursor" src="cursor.png">

공격 시나리오

시나리오 1: 좋아요/팔로우 탈취

피해자: "이 게임 시작" 클릭
실제: Facebook "좋아요" 또는 Twitter "팔로우" 클릭

시나리오 2: 카메라/마이크 권한 탈취

피해자: "퀴즈 시작" 클릭
실제: 브라우저의 "카메라 허용" 버튼 클릭

시나리오 3: 파일 다운로드

피해자: "다음" 클릭
실제: 악성 파일 다운로드 확인 버튼 클릭

시나리오 4: 결제 승인

피해자: "무료 체험 시작" 클릭
실제: 결제 승인 버튼 클릭

방어 방법

1. X-Frame-Options 헤더 (기본)

X-Frame-Options: DENY
설명
DENY모든 프레임 내 로드 차단
SAMEORIGIN같은 도메인에서만 허용
ALLOW-FROM uri특정 도메인만 허용 (deprecated)

서버 설정:

# Nginx
add_header X-Frame-Options "DENY" always;
# Apache
Header always set X-Frame-Options "DENY"

2. Content-Security-Policy (권장)

Content-Security-Policy: frame-ancestors 'none';
설명
'none'어디서도 프레임 불가
'self'같은 도메인만 허용
https://trusted.com특정 도메인만 허용

장점: X-Frame-Options보다 유연하고 현대적

3. JavaScript Frame Busting

// 기본 Frame Buster
if (top !== self) {
    top.location = self.location;
}

우회 가능한 약점:

<!-- 공격자가 차단 가능 -->
<iframe src="target.com" sandbox="allow-forms"></iframe>
<!-- sandbox가 JavaScript 실행 제한 -->

더 강력한 Frame Buster:

<style>
    /* 기본적으로 숨김 */
    html { display: none !important; }
</style>
<script>
    if (self === top) {
        document.documentElement.style.display = 'block';
    } else {
        top.location = self.location;
    }
</script>

4. SameSite 쿠키

Set-Cookie: session=abc123; SameSite=Strict; Secure

효과: iframe에서 로드 시 쿠키가 전송되지 않음 → 인증 실패

5. 중요 작업에 추가 확인

// 2단계 확인
function transfer() {
    const confirmed = confirm("정말 송금하시겠습니까?\n금액: 1,000,000원");
    if (confirmed) {
        // 송금 실행
    }
}

프레임워크별 설정

Django

# settings.py
X_FRAME_OPTIONS = 'DENY'

# 또는 특정 뷰만
from django.views.decorators.clickjacking import xframe_options_deny

@xframe_options_deny
def my_view(request):
    pass

Express.js

const helmet = require('helmet');
app.use(helmet.frameguard({ action: 'deny' }));

Spring

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.headers().frameOptions().deny();
    }
}

ASP.NET

// Web.config
<system.webServer>
    <httpProtocol>
        <customHeaders>
            <add name="X-Frame-Options" value="DENY" />
        </customHeaders>
    </httpProtocol>
</system.webServer>

테스트 방법

1. 수동 테스트

<!-- 테스트 페이지 -->
<iframe src="https://target.com" width="500" height="500"></iframe>

iframe에 사이트가 로드되면 취약

2. 브라우저 개발자 도구

Console 탭에서 에러 확인:
"Refused to display in a frame because X-Frame-Options is set to DENY"

3. curl로 헤더 확인

curl -I https://target.com | grep -i frame
# X-Frame-Options: DENY
# Content-Security-Policy: frame-ancestors 'none'

취약한 기능 우선순위

우선순위기능이유
최고결제, 송금금전적 피해
높음비밀번호 변경, 계정 삭제계정 탈취
중간설정 변경, 권한 부여보안 약화
낮음좋아요, 팔로우소셜 조작

관련 공격

  • CSRF - 클릭 없이 요청 위조
  • XSS - 스크립트 삽입 공격
  • Reverse Tabnabbing - 탭 조작 공격
  • CORS - Cross-Origin 정책

참고 자료