쪼렙 as! 풀스택

Elastic Beanstalk 에서 HTTPS 로 Redirect 시키려 한 삽질들... 본문

개발 일지/AWS

Elastic Beanstalk 에서 HTTPS 로 Redirect 시키려 한 삽질들...

코코앱 2017. 5. 16. 16:57

Elastic Beanstalk (EB) 를 세팅할 때 Environment Type 은, Single Instance 가 아니고, Auto Scaling, Load Balancing 을 이용하도록 선택한다. 이러는 이유는 특별한 게 없고, ACM을 이용해서 발급받은 HTTPS 인증서는 Load Balancer 에만 적용이 가능하기 때문이다.


로드밸런서 에 ACM을 이용해서 HTTPS 인증서를 적용한다고 해도, 실제 EC2와 LoadBalancer 가 통시는하는 포트는 80포트 (HTTP) 통신이다. 이게 무슨소리? ㅡ,.ㅡ;;; 아래 그림을 보면 쉽게 이해될 것이다.


 

[ 출처 : https://bitbucket.org/osrf/cloudsim/wiki/Deployment ]


로드밸런서를 통해 HTTPS 통신을 할 때는, 우리가 운영하는 EC2 서버와 직접 HTTPS 통신을 하는게 아니다.

사용자는 HTTPS 를 통해서 로드밸런서에 접근을 하게 되고, 이 로드밸런서는 실제  EC2 와는 실제로 그냥 80포트를 이용한 HTTP 통신을 하게 된다. 

그러면 보안은 문제 없나?

하는 질문을 할 수 있겠지만, 아마존은 내부에 VPC 를 설치하여 내부에서만 통신하는것이기 때문에, 그 사이에 다른 어떤 공격자가 중간의 패킷을 가로챌 수 없는 상태이니, 안심해도 된단다. 

(CloudFlare 서비스에서 Flexible 방식도 이런 방식으로 HTTPS 통신을 한다.)


여기서 발생되는 문제.


HTTP 접속을 HTTPS 로 리다이렉트 시킬 때 문제가 된다.


1. 시도해 본 삽질.

보통 아파치의 .htaccess 파일에 

HTTP 통신으로 들어온것은, HTTPS 로 리다이렉트 시켜줘라. 라는 내용을 심어 두었는데,

사용자가 아무리 HTTPS로 접속을 해도 EC2 내부에서는 HTTP 통신으로 인식하기 때문에 리디렉션 무한루프...


2. PHP 에서 접속 포트를 찾아서 리디렉션하기.

이것도 1번과 같은 이유로, 서버 내부에서는 계속 무조건 80 포트로 인식하고 있기 때문에 이것도 리디렉션 무한루프...


그럼 어떻게 해야 하는가...


아마존은 이런 문제를 해결하기 위해서  X-Forwarded-Proto 라는 헤더값을 제공해준단다. 이것을 통해 실제 사용자가 HTTPS 요청을 했는지, HTTP 요청을 했는지 구분을 할 수 있단다.


http://stackoverflow.com/questions/14693852/how-to-force-https-on-elastic-beanstalk

나같은 사람이 많았나보다. Zags 의 답변을 보면, 이사람이 본 Bad Solution 들이 있는데, 내가 바로 그 사람이였다. ㅠㅠ 



해결방법. 1. 

.ebextension 파일을 수정함으로써 해결 - 위의 답변과 같은 방법으로 해결한다.


해결방법 2.

위의 방법이 어렵거나, 모든 접근을 HTTPS 로 강제하지 않기를 원한다면, 자바스크립트를 수정하는 방법이 있겠다.

<script type="text/javascript">

     if (document.location.protocol == 'http:') {

         document.location.href = document.location.href.replace('http:', 'https:');

 }

</script>


이 코드는 클라이언트단에서 실행되기 때문에 문제없이 HTTPS 로 Redirect 된다. 

물론, 일단 통신에 한번 성공을 하고, 자바스크립트까지 문제없이 다운로드 & 실행되어야만 한다. (한번의 불필요한 통신을 거치게 되어있다.)



해결방법 3. PHP 전용.


1
2
3
4
5
if(isset($_SERVER["HTTP_X_FORWARDED_PROTO"]) && $_SERVER["HTTP_X_FORWARDED_PROTO"] == 'http'){
    $url = "https://".$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
    redirect($url);
    return;
}
cs


Comments