웹 개발

CSP 보안설정

호박털이범 2018. 4. 18. 23:32

콘텐츠 보안 정책 (CSP)

CSP (Content-Security-Policy) :
이 정책은 Mozilla가 개발 한 표준으로, 실행 시점 인 브라우저에서 XSS (Cross Site Scripting) 공격을 막는 것을 목표로합니다.
CSP는 인라인 스크립트를 금지하는것을 요구합니다. 다만 꼭 사용해야한다면 nonce 를 사용하여 특정 스크립트를 허용해 사용 가능합니다.
CSP는 FACEBOOK, 트위터, 깃허브, 인스타그램 등 다양한 서비스에서 사용중입니다.
CSP 관련해서 여기를 참고해주세요.
CSP 호환 브라우저 (출처)

IE 는 엣지부터 지원하고, 모바일 브라우저는 거의 다 지원.

CSP 지시문 및 옵션

지시문 설명
default-src 디폴트 설정
connect-src 연결할 수 있는 URL을 제한 (ajax, websockets 등)
script-src 스크립트 관련 권한 집합을 제어
child-src iframe 태그에서 사용
style-src 스타일시트 관련 권한 집합을 제어
font-src 웹 글꼴을 제공할 수 있는 출처를 지정
img-src 이미지 관련 권한 집합을 제어
report-uri 콘텐츠 보안 정책 위반 시 브라우저가 보고서를 보낼 URL을 지정 meta 태그에서는 이 지시문을 사용할 수 없음
모든 지시문을 넣지 않았습니다. 나머지 옵션 및 설명은 여기를 참고해주세요.
src 옵션 설정
  • http://www.test.com,example.com, example.com:443 ... 접근 허용 도메인
  • 'none'은 모든것을 차단(잠금)
  • 'self' 는 현재 도메인만 허용
  • 'unsafe-inline'은 소스코드 내 인라인 자바스크립트 및 CSS를 허용
  • 'nonce-암호화된문자'은 인라인 자바스크립트 및 CSS를 허용 (아래에서 설명)
CSP 설정방법

1. meta 태그 설정
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self'; ">
2. php header 설정
<?php
$headerCSP = "Content-Security-Policy:".
                "default-src 'self';".  // 기본은 자기 도메인만 허용
                "connect-src 'self' ;". // ajax url은 자기 도메인만 허용    
                "script-src 'self'  example.com code.jquery.com https://ssl.google-analytics.com ;". // 자기자신,  접근허용 도메인 설정
                "style-src 'self' 'unsafe-inline';"; 
                "report-uri https://example.com/csp_report.php;".  // 보안 정책 오류 레포트 URL 지정(meta 태그에선 사용불가) 
header($headerCSP);
?>
                        
3 CSP 적용 예 (2. php header 에 설정된 정책을 바탕으로 예를 들었습니다.)
<script src='www.example.com/js/script.js'></script>  <!--  사용가능(접근허용도메인)  --> 
<script src='https://ssl.google-analytics.com/js/script.js'></script> <!--  사용가능 (접근허용도메인)  --> 
<script src='/js/script.js'></script> <!--  사용가능 (self)  -->
<script src='www.test.com/js/script.js'></script> <!--  사용 불가능 (허용되지 않은 도메인) -->                        
<script>alert('인라인스크립트 허용안함');</script> <!--  사용 불가능 (인라인 스크립트) -->                       

<link rel="stylesheet" href="/css/common.css"> <!--  사용가능 (self) -->
<span style='color:green;'>인라인스크립트 허용</span'> <!--  사용가능 (unsafe-inline) -->
<link rel="stylesheet" href="https://cdn.metroui.org.ua/v4/css/metro-all.min.css"> <!--  사용 불가능 (허용되지 않은 도메인) -->
                        
CSP 보안위반 REPORT 설정방법

보안 정책에 위반되었을 경우 아래 와 같이 console.log에 오류 정보 노출
해당 오류 정보는 report-uri 설정을 통해 리포트로 받을수 있음. ("report-uri https://example.com/csp_report.php;")


csp_report.php 파일 소스 코드

<?php
$postdata   = file_get_contents("php://input");
$data       = json_decode(trim($postdata), true);

$log = "================ CSP REPORT 로그 파일 (".date('Y-m-d H:i:s').") =============\n";
$fp         = fopen('/var/www/html/sim/log/csp_log.txt', 'a');
fwrite($fp, $log);
fwrite($fp, var_export($data, true)."\n");
fclose($fp);
?>
                        
csp_log.txt
================ CSP REPORT 로그 파일 (2018-04-18 20:49:25) =============
array (
  'csp-report' => 
  array (
    'document-uri' => 'http://192.168.200.157/sim/',
    'referrer' => '',
    'violated-directive' => 'img-src',
    'effective-directive' => 'img-src',
    'original-policy' => 'connect-src \'self\' ;default-src \'self\';frame-ancestors \'self\' ;frame-src \'none\';media-src \'self\' *.example.com;object-src \'none\'; report-uri http://192.168.200.157/sim/csp_report.php;script-src \'self\' \'unsafe-inline\' example.com code.jquery.com https://ssl.google-analytics.com ;style-src \'self\' \'unsafe-inline\';',
    'disposition' => 'enforce',
    'blocked-uri' => 'data',
    'status-code' => 200,
    'script-sample' => '',
  ),
)
                        
document-uri 위반이 발생한 페이지
original-policy CSP 전체 정책
violated-directive 위반한 특정 지시문
blocked-uri 정책을 위반한 리소스
CSP 자바스크립트 NONCE 설정

unsafe-inline 옵션을 사용하면 인라인 스크립트를 사용가능하지만, nonce 옵션을 사용하여 좀더 보안에 강화 할수 있음.
nonce-sha256으로 지정된 랜덤한 값을 설정하는것이 좋다. ex) $nonce_key = hash('sha256', microtime()); => nonce-<?=$nonce_key?>
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
<script nonce=EDNnf03nceIOfn39fn3e9h3sdfa>
    alert('해당 스크립트는 사용가능 한 인라인 스크립트 입니다.');
</script>

<script>
    alert('해당 스크립트는 사용 불가능한 인라인 스크립트 입니다.');
</script>