Networks

혼합 콘텐츠(Mixed Content): 원인과 해결 방법

Chrysans 2025. 4. 3. 14:25
728x90
반응형

혼합콘텐츠(mixedContent) 포스팅에 있는 http 화면 이미지

 

 

들어가며

웹 애플리케이션을 개발하다 보면 갑자기 브라우저에서 다음과 같은 경고를 마주칠 때가 있습니다:

이 페이지에 안전하지 않은 콘텐츠(혼합 콘텐츠)가 포함되어 있습니다.

 

혼합 콘텐츠(mixed content) 문제는 언뜻 보기에는 간단해 보이지만, 무시하면 사용자의 보안을 심각하게 위협할 수 있습니다. 현대 웹 개발에서 HTTPS는 더 이상 선택이 아닌 필수가 되었죠. 하지만 이미 구축된 프로젝트에서 HTTP와 HTTPS 리소스가 뒤섞여 있는 경우가 많습니다.

이 글에서는 혼합 콘텐츠가 무엇인지, 왜 문제가 되는지, 그리고 프론트엔드와 서버 측에서 어떻게 효과적으로 해결할 수 있는지 핵심만 살펴보겠습니다.

 


목차

  1. 혼합 콘텐츠란?
  2. 혼합 콘텐츠 발생 원인
  3. 프론트엔드에서의 해결 방법
  4. 서버 측에서의 해결 방법
  5. 혼합 콘텐츠 예방 방법
  6. 결론

혼합 콘텐츠란?

혼합 콘텐츠(Mixed Content)란 HTTPS로 로드된 웹 페이지에서 HTTP를 통해 추가 리소스(이미지, 비디오, 스크립트, 스타일시트 등)를 로드하는 경우를 말합니다.

웹 브라우저는 기본적으로 HTTPS 페이지에서 안전하지 않은 HTTP 리소스를 로드하려는 시도를 감지하고 차단하거나 경고합니다. 이러한 혼합 콘텐츠는 두 가지 유형으로 나뉩니다:

  1. 수동적(Passive) 혼합 콘텐츠: 이미지, 오디오, 비디오와 같이 사용자와 상호 작용하지 않는 콘텐츠
  2. 활성(Active) 혼합 콘텐츠: 스크립트, iframe, XHR 요청, CSS 파일과 같이 페이지의 동작을 변경할 수 있는 콘텐츠

활성 혼합 콘텐츠는 현대 브라우저 (예: Chrome, Firefox, Edge, Safari) 에서 기본적으로 차단되며, 수동적 혼합 콘텐츠는 경고와 함께 로드될 수 있습니다.


혼합 콘텐츠 발생 원인

혼합 콘텐츠가 발생하는 주요 원인은 다음과 같습니다:

 

하드코딩된 HTTP URL: 코드 내에 http://로 시작하는 URL을 직접 삽입한 경우

<img src="http://example.com/image.jpg">

 

상대 URL과 기본 프로토콜: 상대 URL을 사용하지만 기본 프로토콜이 HTTP인 경우

<script src="//example.com/script.js"></script>

레거시 코드와 서드파티 리소스: 오래된 코드나 외부 라이브러리에서 HTTP 리소스를 참조하는 경우

CMS나 데이터베이스에 저장된 URL: 콘텐츠 관리 시스템이나 데이터베이스에 HTTP URL이 저장된 경우


프론트엔드에서의 해결 방법

1. 프로토콜 상대 URL 사용하기

가장 간단한 방법 중 하나는 URL에서 프로토콜 부분을 제거하는 것입니다:

// 변경 전
const apiUrl = 'http://api.example.com/data';

// 변경 후
const apiUrl = '//api.example.com/data';

이렇게 하면 현재 페이지의 프로토콜(HTTP 또는 HTTPS)을 자동으로 따라갑니다. 하지만 이 방법은 리소스가 HTTPS를 지원하는 경우에만 작동합니다.

2.  Content Security Policy 

 Content Security Policy(CSP)를 사용하여 혼합 콘텐츠 문제를 해결할 수 있습니다. React, Vue, Angular 등의 모던 프레임워크에서는 다음과 같이 메타 태그를 추가하거나 Helmet 라이브러리를 사용할 수 있습니다:

React + React Helmet 예제 (React 18):

import React from 'react';
import { Helmet } from 'react-helmet-async';

function App() {
  return (
    <>
      <Helmet>
        <meta 
          httpEquiv="Content-Security-Policy" 
          content="upgrade-insecure-requests" 
        />
      </Helmet>
      
      <div className="App">
        {/* 앱 콘텐츠 */}
        <img src="http://example.com/image.jpg" /> {/* 브라우저가 자동으로 HTTPS로 업그레이드 */}
      </div>
    </>
  );
}

export default App;

 

이 방법은 브라우저에게 모든 HTTP 요청을 HTTPS로 자동 업그레이드하도록 지시합니다. 단, 해당 서버가 HTTPS를 지원해야 합니다.

서버 측에서의 해결 방법

1. HTTPS 리다이렉트 구현하기

Node.js Express 서버에서는 다음과 같이 모든 HTTP 요청을 HTTPS로 리다이렉트할 수 있습니다:

// Express 서버에서 HTTPS 리다이렉트 구현 (Node.js)
const express = require('express');
const app = express();

// HTTPS 리다이렉트 미들웨어
app.use((req, res, next) => {
  if (req.header('x-forwarded-proto') !== 'https' && process.env.NODE_ENV === 'production') {
    res.redirect(`https://${req.header('host')}${req.url}`);
  } else {
    next();
  }
});

// 나머지 라우트 및 미들웨어
app.use(express.static('public'));
// ...

app.listen(process.env.PORT || 3000);

이 코드는 프로덕션 환경에서 모든 HTTP 요청을 동일한 경로의 HTTPS URL로 리다이렉트합니다.

2. 서버 응답 헤더 설정

다음과 같은 보안 헤더를 설정할 수 있습니다

// Node.js Express 서버에서 보안 헤더 설정
const helmet = require('helmet');
const express = require('express');
const app = express();

// Helmet을 사용한 보안 헤더 설정
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        upgradeInsecureRequests: [], // 이 값이 비어 있어도 헤더가 설정됨
      },
    },
    // 추가 보안 헤더 설정
    strictTransportSecurity: {
      maxAge: 63072000, // 2년
      includeSubDomains: true,
      preload: true
    }
  })
);

app.use(express.static('public'));
// ...

app.listen(process.env.PORT || 3000);

이 코드는 Helmet 라이브러리를 사용하여 다음 두 가지 핵심 보안 헤더를 설정합니다:

  • Content-Security-Policy: upgrade-insecure-requests: 브라우저에게 모든 HTTP 요청을 HTTPS로 업그레이드하도록 지시
  • Strict-Transport-Security: 브라우저에게 해당 사이트에는 항상 HTTPS만 사용하도록 지시 (HSTS)

혼합 콘텐츠 예방 방법

1. HTTPS URL만 사용하기

가장 확실한 예방 방법은 모든 리소스에 HTTPS URL을 사용하는 것입니다:

// TypeScript에서 URL 생성 시 HTTPS 강제하기
function createSecureUrl(path: string): string {
  // 이미 프로토콜이 있는지 확인
  if (path.startsWith('http://') || path.startsWith('https://')) {
    // HTTP URL이면 HTTPS로 변환
    return path.replace(/^http:\/\//, 'https://');
  }
  
  // 상대 경로나 프로토콜이 없는 경우 HTTPS 추가
  if (path.startsWith('//')) {
    return `https:${path}`;
  }
  
  // 다른 경우 (예: '/images/logo.png')는 그대로 사용
  return path;
}

// 사용 예:
const imageUrl = createSecureUrl('http://example.com/image.jpg');
console.log(imageUrl); // 'https://example.com/image.jpg'

2. 빌드 시 자동 변환 설정

Webpack과 같은 빌드 도구를 사용하여 배포 시 모든 URL을 검사하고 HTTPS로 변환할 수 있습니다:

// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  // 기본 설정
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  
  // 플러그인 설정
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // 빌드 시 HTML 내 URL 변환을 위한 함수
      templateParameters: {
        toHttps: (url) => {
          if (typeof url === 'string') {
            return url.replace(/^http:\/\//, 'https://');
          }
          return url;
        }
      }
    }),
  ],
  
  // 기타 설정...
};

그리고 HTML 템플릿에서는 다음과 같이 사용할 수 있습니다:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>안전한 웹사이트</title>
  <!-- toHttps 함수를 사용하여 URL 변환 -->
  <link rel="stylesheet" href="<%= toHttps('http://example.com/styles.css') %>">
</head>
<body>
  <div id="root"></div>
  <!-- 스크립트도 안전하게 변환 -->
  <script src="<%= toHttps('http://example.com/external-script.js') %>"></script>
</body>
</html>

전문 용어 설명

혼합 콘텐츠 (Mixed Content) HTTPS 페이지에서 HTTP 리소스를 로드하는 경우
CSP (Content Security Policy) 웹사이트의 보안을 강화하기 위한 추가 보안 계층
HSTS (HTTP Strict Transport Security) 웹사이트가 HTTPS로만 접근되어야 함을 브라우저에 알리는 보안 기능
활성 혼합 콘텐츠 (Active Mixed Content) 페이지의 동작을 변경할 수 있는 스크립트 등
수동적 혼합 콘텐츠 (Passive Mixed Content) 이미지와 같이 페이지 동작에 영향을 미치지 않는 콘텐츠

트러블슈팅

문제: 서드파티 라이브러리에서 HTTP 리소스를 불러오는 경우

해결책: 서드파티 라이브러리를 직접 수정할 수 없는 경우, CSP upgrade-insecure-requests 지시문을 사용하거나, 가능하다면 최신 버전의 라이브러리로 업그레이드하세요.


 

베스트 프랙티스

안티 패턴

모든 리소스에 HTTPS 사용 하드코딩된 HTTP URL 사용
상대 URL 또는 프로토콜 상대 URL 사용 혼합 콘텐츠 경고 무시
Content-Security-Policy 헤더 설정 HTTP와 HTTPS 버전 모두 유지
빌드 프로세스에서 URL 검증 보안보다 호환성 우선시

결론

혼합 콘텐츠 문제는 현대 웹 개발에서 반드시 해결해야 할 보안 이슈입니다. 이 글에서 다룬 프론트엔드와 서버 측 해결책을 구현하면 대부분의 혼합 콘텐츠 문제를 효과적으로 해결할 수 있습니다.

가장 좋은 접근 방식은:

  1. 모든 리소스에 HTTPS URL 사용하기
  2. CSP와 같은 현대적인 보안 헤더 적용하기
  3. 빌드 시 URL을 자동으로 검증하고 변환하기
  4. 주기적으로 콘텐츠를 검사하여 HTTP URL이 포함되지 않았는지 확인하기

이러한 방법을 적용하면 사용자의 데이터를 보호하고 보안 경고 없이 웹사이트를 제공할 수 있습니다.

혼합 콘텐츠 문제를 해결하는 것은 단순히 브라우저 경고를 없애는 것이 아니라, 사용자의 데이터 보안을 강화하고 현대 웹의 보안 표준을 준수하는 중요한 단계입니다. 지금 바로 여러분의 웹사이트에서 혼합 콘텐츠 이슈가 있는지 확인하고 해결해보세요!

 

 

 

https://covelope.tistory.com/entry/Http-Https-%EB%9E%80-http-https-%EC%B0%A8%EC%9D%B4

 

Http / Https 란? (http 와 https 차이)

1. httpHTTP란 Hypertext Transfer Protocol의 약자이다.HTTP 는 HTML 문서와 같은 리소스를 가져올 수 있도록 해주는 프로토콜인데HTTP 는 웹에서 이루어지는 모든 데이터 교환의 기초이며 클라이언트-서버 프

covelope.tistory.com

 

728x90
반응형

'Networks' 카테고리의 다른 글

OSI 7 계층(osi 7 layer)  (0) 2023.05.11
Http / Https 란? (http 와 https 차이)  (0) 2023.04.24