앞 편에서 일반 연결을 보안 연결로 보내는 안내를 두는 방법을 살폈다. 그러나 그 안내만으로는 안전이 완성되지 않는다. 사용자가 처음 주소를 입력할 때 브라우저는 대개 일반 연결부터 시도하고, 그 첫 요청이 안전하지 않은 경로로 오가는 순간에 공격이 끼어들 수 있다. 리다이렉트는 이미 서버에 닿은 뒤에야 작동하므로, 서버에 닿기 전의 그 짧은 빈틈을 메우지 못한다.


이번 편은 이 빈틈을 닫는 강제 장치를 다룬다. 왜 리다이렉트만으로는 부족한지, 브라우저가 보안 연결을 기억하게 만드는 강제의 원리는 무엇인지, 최초 접속의 빈틈을 어떻게 메우는지, 강제의 범위를 하위 이름까지 어떻게 넓히는지, 유효 기간을 어떻게 점진적으로 늘리는지, 강제를 방해하는 혼합 콘텐츠는 무엇인지, 그리고 강제를 되돌리기 어렵다는 점을 어떻게 다루어야 하는지를 차례로 짚는다.


리다이렉트만으로는 부족하다


사용자가 주소창에 이름만 입력하면 브라우저는 보안 연결을 명시하지 않은 것으로 보고 일반 연결부터 시도하는 경우가 많다. 서버는 그 요청을 받아 보안 연결로 다시 오라는 안내를 돌려주고, 브라우저는 그제야 보안 연결로 옮겨 간다. 문제는 이 첫 일반 요청이 이미 안전하지 않은 경로를 한 번 오갔다는 데 있다. 그 순간에 중간의 공격자가 끼어들 여지가 생긴다.


이 빈틈을 노리는 공격은 첫 요청을 가로채 사용자를 가짜 서버로 유도하거나, 보안 연결로 올라가는 안내 자체를 지워 버린다. 사용자는 안내가 사라진 줄 모른 채 계속 일반 연결로 통신하게 되고, 그 내용은 공격자에게 그대로 노출된다. 리다이렉트는 서버가 응답할 기회를 얻은 뒤에야 작동하므로, 응답이 도달하기 전에 벌어지는 이 조작을 막지 못한다.


근본적인 해결은 브라우저가 처음부터 일반 연결을 시도하지 않게 만드는 것이다. 그 이름에 대해서는 항상 보안 연결로만 접속하라고 브라우저에게 미리 알려 두면, 첫 요청부터 보안 연결로 이루어져 안전하지 않은 경로가 아예 사라진다. 강제 장치는 바로 이 지시를 브라우저에 심는 방법이다. 안내가 사후 교정이라면 강제는 사전 차단이다.


이 차이를 이해하면 왜 두 장치를 함께 써야 하는지가 분명해진다. 강제 지시는 브라우저가 그 지시를 받은 뒤부터 효력을 갖고, 그 지시는 대개 보안 연결의 응답을 통해 전달된다. 그래서 사용자가 그 이름에 처음 접속할 때는 여전히 안내가 필요하고, 그 안내를 통해 강제 지시가 전달된 뒤부터 강제가 작동한다. 안내가 강제로 가는 다리를 놓는 셈이다.


강제의 원리


강제는 서버가 보안 연결의 응답에 특별한 지시를 실어 보내는 것으로 시작한다. 이 지시는 앞으로 정해진 기간 동안 이 이름에는 반드시 보안 연결로만 접속하라는 명령이다. 이 지시를 받은 브라우저는 그 사실을 저장해 두고, 이후 그 이름으로 갈 일이 있으면 사용자가 일반 연결을 입력하더라도 브라우저 내부에서 보안 연결로 바꾸어 요청한다.


이 저장된 지시 덕분에 두 번째 접속부터는 일반 연결이 아예 발생하지 않는다. 사용자가 주소를 어떻게 입력하든 브라우저가 서버에 요청을 보내기도 전에 보안 연결로 확정하므로, 안전하지 않은 첫 요청이라는 빈틈이 사라진다. 리다이렉트가 서버까지 갔다 오는 교정이었다면, 이 강제는 서버에 가기도 전에 브라우저 안에서 이루어지는 확정이다.


강제 지시는 보안 연결로 전달되어야 의미가 있다. 일반 연결로 이 지시를 보내면 그 응답 자체가 조작될 수 있어 신뢰할 수 없기 때문에, 브라우저는 일반 연결로 받은 강제 지시를 무시한다. 그래서 강제는 항상 보안 연결이 먼저 성립한 뒤 그 위에서 전달된다. 안전한 통로로 전달된 지시만이 브라우저에 기억된다.


이 강제는 그 이름에 한정된다. 브라우저는 어떤 이름에 대해 강제 지시를 받았는지를 기억하고, 그 이름에 대해서만 보안 연결을 강제한다. 다른 이름에는 영향을 주지 않으므로, 각 이름은 자신의 강제 상태를 독립적으로 갖는다. 강제를 하위 이름까지 넓히려면 별도의 설정이 필요하며, 이는 뒤에서 다룬다.


최초 접속의 빈틈과 선등록


강제 지시는 브라우저가 그 이름에 한 번은 보안 연결로 접속해 지시를 받아야 효력이 생긴다. 그러니 사용자가 그 이름에 생애 처음 접속하는 순간에는 아직 강제가 없어, 여전히 첫 요청의 빈틈이 남는다. 강제는 두 번째 접속부터 완전하지만 첫 접속만큼은 보호하지 못하는 한계가 있다. 이 첫 접속의 빈틈을 메우는 장치가 따로 마련되어 있다.


그 장치는 브라우저에 미리 심어 두는 목록이다. 특정 이름을 이 목록에 등록해 두면, 브라우저는 그 이름에 한 번도 접속한 적이 없어도 처음부터 보안 연결로만 접속한다. 강제 지시를 받아 기억하는 것이 아니라, 브라우저 자체에 그 이름의 강제가 내장되어 배포되는 것이다. 이로써 첫 접속의 빈틈까지 닫힌다.


이 목록에 등록하려면 몇 가지 조건을 충족해야 한다. 그 이름은 물론이고 하위 이름까지 모두 보안 연결을 갖추어야 하고, 강제 지시에 충분히 긴 유효 기간과 하위 이름 포함, 목록 등록 의사가 명시되어 있어야 한다. 조건을 갖춘 뒤 등록을 신청하면 검토를 거쳐 목록에 오르고, 이후 배포되는 브라우저에 반영된다. 등록은 신중하게 준비한 뒤에 진행할 일이다.


이 목록의 강력함은 곧 되돌리기 어려움이기도 하다. 한번 목록에 오르면 그 이름은 모든 사용자의 브라우저에서 보안 연결만 허용되므로, 어떤 사정으로 일반 연결이 필요해져도 되돌리는 데 오랜 시간이 걸린다. 목록에서 빠지는 것도 새 브라우저가 배포되어야 반영되기 때문이다. 그래서 목록 등록은 앞으로 그 이름을 영구히 보안 연결로만 운영하겠다는 확신이 설 때만 해야 한다.


적용 범위와 하위 이름


강제 지시에는 그 효력을 하위 이름까지 넓히는 선택이 있다. 이 선택을 켜면 대표 이름뿐 아니라 그 아래 모든 서브도메인에도 보안 연결이 강제된다. 대표 이름만 강제하고 하위 이름을 빠뜨리면, 공격자가 하위 이름을 만들어 일반 연결로 유도하는 우회로가 열린다. 하위 이름을 포함하는 것이 강제의 빈틈을 줄이는 방향이다.


다만 하위 이름을 포함하면 모든 하위 이름이 보안 연결을 갖추고 있어야 한다. 어떤 하위 이름이 아직 일반 연결로만 서비스되고 있다면, 강제를 하위 이름까지 넓히는 순간 그 하위 서비스에 접속할 수 없게 된다. 그래서 이 선택을 켜기 전에 모든 하위 이름이 보안 연결을 지원하는지를 먼저 확인해야 한다. 하나라도 준비되지 않은 하위 이름이 있으면 그것부터 갖추어야 한다.


내부용이나 개발용 하위 이름을 운영한다면 이 점을 특히 유의해야 한다. 겉으로 드러나지 않는 하위 이름이라도 강제의 범위에 들면 보안 연결을 요구받으므로, 그런 이름들의 인증서까지 미리 챙겨야 한다. 강제를 하위 이름까지 넓히는 결정은 대표 이름 하나가 아니라 그 아래 모든 이름을 함께 고려하는 결정이다.


범위를 넓힐 때도 점진적으로 접근하는 것이 안전하다. 먼저 대표 이름에만 강제를 적용해 안정성을 확인한 뒤, 모든 하위 이름의 준비가 끝났을 때 범위를 넓히는 순서가 바람직하다. 한꺼번에 넓게 적용했다가 준비되지 않은 하위 이름에서 문제가 터지면, 되돌리기 어려운 강제의 특성 때문에 수습이 오래 걸린다.


유효 기간과 점진적 도입


강제 지시에는 유효 기간이 담긴다. 브라우저는 이 기간 동안 강제를 유지하고, 기간이 지나기 전에 다시 지시를 받으면 기간을 갱신한다. 유효 기간이 길수록 강제가 오래 유지되어 안전하지만, 그만큼 설정에 문제가 있을 때 사용자가 오래 갇힌다. 그래서 도입 초기에는 유효 기간을 짧게 두는 것이 정석이다.


점진적 도입은 유효 기간을 조금씩 늘려 가는 방식으로 이루어진다. 처음에는 몇 분이나 몇 시간 정도로 짧게 두어, 설정에 문제가 있어도 그 기간만 지나면 강제가 풀리게 한다. 이 짧은 기간에 문제가 없음을 확인한 뒤 유효 기간을 며칠, 몇 달로 점차 늘려 간다. 이렇게 단계를 밟으면 되돌리기 어려운 강제를 안전하게 정착시킬 수 있다.


유효 기간을 처음부터 길게 두는 것은 위험하다. 아직 검증되지 않은 설정에 긴 유효 기간을 적용하면, 어딘가에서 보안 연결이 제대로 동작하지 않을 때 사용자가 그 긴 기간 내내 접속하지 못하게 된다. 강제의 유효 기간은 신뢰가 쌓인 만큼만 늘려야 하며, 확신 없이 긴 기간을 적용하는 것은 스스로에게 족쇄를 채우는 일이다.


강제를 완전히 걷어 내려면 유효 기간을 영으로 지정한 지시를 보내야 한다. 그러면 그 지시를 받은 브라우저는 저장된 강제를 지운다. 다만 이 해제 지시도 사용자가 다시 접속해 받아야 효력이 생기므로, 강제를 거두는 것 역시 즉각적이지 않다. 강제는 켜는 것도 끄는 것도 시간을 두고 퍼진다는 점을 늘 염두에 두어야 한다.


강제를 방해하는 혼합 콘텐츠


보안 연결로 열린 페이지 안에서 일부 자원을 일반 연결로 불러오면 혼합 콘텐츠 문제가 생긴다. 페이지 자체는 안전한 연결로 왔는데 그 안의 이미지나 스크립트가 안전하지 않은 경로로 오면, 그 부분이 조작이나 엿보기에 노출되어 페이지 전체의 안전이 무너진다. 브라우저는 이런 자원을 막거나 경고하며, 특히 실행되는 자원은 아예 차단한다.


혼합 콘텐츠는 대개 옛 주소가 페이지 안에 남아 있어 발생한다. 페이지를 보안 연결로 옮겼지만 그 안에 걸린 자원의 주소가 여전히 일반 연결을 가리키고 있으면, 그 자원이 혼합 콘텐츠가 된다. 그래서 보안 연결로 전환할 때는 페이지 자체뿐 아니라 그 안이 참조하는 모든 자원의 주소까지 함께 점검해야 한다.


이 문제를 줄이는 방법은 자원 주소를 연결 방식에 매이지 않게 두거나 보안 연결로 명시하는 것이다. 자원을 부를 때 연결 방식을 고정하지 않고 페이지와 같은 방식을 따르게 하면, 페이지가 보안 연결이면 자원도 보안 연결로 불린다. 브라우저가 안전하지 않은 자원 요청을 자동으로 보안 연결로 올려 시도하게 하는 지시를 두는 방법도 있다.


혼합 콘텐츠는 강제와 함께 다루어야 완전하다. 보안 연결을 강제해 페이지를 안전하게 열어도, 그 안의 자원이 안전하지 않으면 안전은 반쪽에 그친다. 강제가 페이지의 통로를 지킨다면, 혼합 콘텐츠 정리는 그 안의 내용물까지 지키는 일이다.


강제를 도입하는 순서


강제는 되돌리기 어려운 장치이므로 도입에 정해진 순서를 따르는 것이 안전하다. 첫 단계는 모든 페이지와 그 안의 자원이 보안 연결로 온전히 서비스되는지를 확인하는 것이다. 강제를 켜기 전에 이미 보안 연결만으로 서비스가 완전히 동작해야 하며, 혼합 콘텐츠가 하나도 남아 있지 않아야 한다. 준비가 끝나지 않은 상태에서 강제를 켜면 사용자가 접속하지 못하는 상황을 스스로 만든다.


두 번째 단계는 강제 지시를 짧은 유효 기간으로 적용해 보는 것이다. 이 단계에서는 하위 이름 포함이나 목록 등록을 아직 켜지 않고, 대표 이름에만 짧은 기간의 강제를 걸어 문제가 없는지를 지켜본다. 짧은 기간이므로 어딘가에서 문제가 드러나도 그 기간만 지나면 강제가 풀려 회복할 여지가 있다. 이 시험 단계가 이후의 확신을 뒷받침한다.


세 번째 단계는 검증이 끝난 뒤 유효 기간을 점차 늘리고 하위 이름까지 범위를 넓히는 것이다. 모든 하위 이름의 준비를 확인한 뒤 범위를 넓히고, 안정성이 쌓이면 유효 기간을 길게 잡는다. 그리고 그 이름을 영구히 보안 연결로만 운영하겠다는 확신이 섰을 때에야 마지막으로 목록 등록을 검토한다. 각 단계는 앞 단계의 안정을 확인한 뒤에만 다음으로 넘어간다.


이 순서의 바탕에는 강제가 사전 차단인 만큼 실수의 대가도 사전적이라는 인식이 있다. 리다이렉트는 잘못되어도 안내를 고치면 그만이지만, 강제는 잘못되면 사용자의 브라우저에 남아 그 기간 내내 접속을 막는다. 그래서 강제는 신중하게, 점진적으로, 되돌릴 여지를 남기며 도입해야 한다. 이렇게 리다이렉트의 한계, 강제의 원리, 최초 접속의 빈틈, 적용 범위, 유효 기간의 점진적 도입, 혼합 콘텐츠, 그리고 도입의 순서까지 이해하면 보안 연결이 빈틈없이 강제된다. 다음 편은 이 이름과 연결 위에 전송망을 얹는 방법을 다룬다.