[실전 보안 14] 사용자가 맡긴 정보를 지키는 일

드디어 이 시리즈의 마지막 편이다. 지난 편에서 커뮤니티의 안전은 코드와 사람이 함께 만든다고 했고, 그 모든 방어가 지키려는 가장 소중한 대상이 사용자의 개인정보라고 예고했다. 사용자는 우리를 믿고 이메일 주소와 활동 기록을 맡긴다. 그 신뢰를 저버리지 않는 것이 방어의 궁극적 목적이다. 이번 편에서는 개인정보를 취급할 때 지켜야 할 원칙들을, 지금까지 다룬 이야기들을 하나로 엮으며 정리해 본다. 신생 사이트 운영자가 반드시 새겨야 할 마지막 당부인 셈이다.


1. 가장 안전한 정보는 없는 정보다

개인정보를 다루며 얻은 첫 번째 원칙은 역설적이게도 되도록 적게 모으라는 것이었다. 우리가 가진 정보가 많을수록 유출됐을 때 피해도 크고, 지켜야 할 부담도 커진다. 그래서 어떤 정보를 수집하기 전에 늘 물었다. 이 정보가 정말 서비스에 필요한가. 없어도 되는 정보라면 애초에 받지 않는 게 가장 안전했다. 갖고 있지 않은 정보는 유출될 수도, 악용될 수도 없기 때문이다.


이 원칙에 따라 우리 커뮤니티는 가입에 꼭 필요한 최소한만 받으려 했다. 서비스에 없어도 되는 민감한 개인정보는 처음부터 요구하지 않았다. 화려한 개인화 기능을 위해 이것저것 받고 싶은 유혹이 있었지만, 그 정보를 지킬 책임과 유출 위험을 생각하면 절제하는 편이 옳았다. 편의를 위해 정보를 늘리는 것과 안전을 위해 정보를 줄이는 것 사이에서, 나는 신생 사이트일수록 후자로 기울어야 한다고 판단했다.


수집한 정보도 목적을 다하면 지우는 게 원칙이었다. 영원히 쌓아 두는 정보는 언젠가 새는 정보다. 더 이상 필요 없는 기록은 적절한 시점에 정리해, 우리가 짊어진 위험의 총량을 줄이려 했다. 정보를 모으는 것만큼 비우는 것도 방어라는 관점은, 무언가를 계속 축적하는 데 익숙한 개발자에게는 의외로 낯선 발상이었지만 중요한 전환이었다.


2. 저장할 때와 오갈 때

모을 수밖에 없는 정보는 안전하게 지켜야 했다. 여기서 앞 편들의 이야기가 하나로 모인다. 비밀번호는 되돌릴 수 없게 해싱해 저장하고, 오가는 정보는 암호화된 통신으로만 주고받는다. 특히 민감한 정보일수록 저장할 때도 그대로 두지 않고 보호된 형태로 두는 게 좋았다. 저장소가 유출되는 최악의 순간에도 정보가 곧바로 읽히지 않도록 대비하는 것이다.


또 정보에 접근할 수 있는 통로를 최소로 좁혔다. 앞서 권한 편에서 다룬 원칙이 여기서도 적용된다. 사용자의 정보에는 그 정보를 볼 정당한 자격이 있는 요청만 닿을 수 있어야 한다. 운영자라도 아무나 모든 정보를 들여다볼 수 있으면 안 되고, 꼭 필요한 최소한의 접근만 허용해야 한다. 정보를 지키는 벽은 바깥의 공격자뿐 아니라 내부의 무분별한 접근에 대해서도 세워져야 했다.


시크릿 관리 편에서 다룬 열쇠들도 결국 이 정보를 지키기 위한 것이었다. 저장소에 접근하는 열쇠, 정보를 보호하는 데 쓰는 열쇠가 새면 아무리 잘 저장해도 소용없다. 그래서 정보 보호와 시크릿 관리는 떼어 놓을 수 없는 짝이었다. 방어의 여러 층이 서로 맞물려, 어느 하나만 무너져도 정보가 위태로워지는 구조라는 걸 다시 확인했다.


3. 새어 나가는 뜻밖의 통로

정보는 거창한 해킹이 아니라 사소한 부주의로 더 자주 샌다. 대표적인 게 로그나 오류 메시지였다. 문제를 추적하려고 남긴 기록에 사용자의 민감한 정보가 무심코 포함되면, 그 기록에 접근하는 누구나 정보를 보게 된다. 그래서 무엇을 기록으로 남길지 신중히 골라, 민감한 정보는 기록에 남기지 않거나 가려서 남기도록 했다. 편의를 위한 기록이 유출의 통로가 되지 않게 한 것이다.


화면에 정보를 얼마나 드러내느냐도 문제였다. 어떤 사용자의 정보가 다른 사용자에게 필요 이상으로 노출되지 않게 신경 써야 했다. 예를 들어 화면에 보이지 않는다고 안심하면 안 되고, 그 정보를 실어 나르는 뒤편의 응답에도 불필요한 개인정보가 딸려 가지 않는지 확인해야 했다. 봇 검증 편에서 말했듯 화면은 겉모습일 뿐이고, 실제 오가는 데이터를 기준으로 노출을 판단해야 했다.


주소나 링크에 정보를 담는 것도 조심했다. 주소에 담긴 값은 여러 곳에 기록으로 남기 쉬워, 민감한 정보를 주소에 실으면 뜻하지 않게 여러 경로로 퍼진다. 그래서 민감한 값은 주소가 아니라 보호된 방식으로 주고받도록 했다. 정보가 새는 통로는 정면의 공격보다 이런 옆문과 뒷문이 훨씬 많았고, 그 사소해 보이는 틈들을 하나씩 막는 게 실제 개인정보 보호의 대부분이었다.


4. 사고를 전제로 대비하기

아무리 잘 지켜도 완벽은 없다는 게 이 시리즈 내내 깔린 전제였다. 개인정보도 마찬가지다. 그래서 유출이 절대 안 일어난다고 자신하기보다, 만에 하나 일어났을 때를 대비하는 게 성숙한 태도였다. 정보를 최소로 모으고 보호된 형태로 저장한 것도 결국 그 최악의 순간에 피해를 줄이기 위한 대비였다. 최악을 가정하고 설계하면 평상시에도 자연히 더 안전해졌다.


대비에는 감지도 포함됐다. 이상한 접근이나 대량 조회 같은 징후를 알아채는 장치를 두면, 사고가 커지기 전에 대응할 수 있다. 앞 편들에서 다룬 요청 제한, 자동 점검, 신고 같은 장치들이 여기서도 정보를 지키는 눈 역할을 했다. 방어가 무너지는 순간을 빨리 아는 것만으로도 피해를 크게 줄일 수 있다는 원칙은, 정보 보호에서 특히 중요했다.


그리고 무슨 일이 생겼을 때 정직하게 대응하겠다는 마음가짐도 필요했다. 사고를 숨기려는 유혹은 늘 있지만, 사용자의 신뢰는 정직함 위에서만 유지된다. 문제가 생기면 사용자에게 알리고, 무엇을 어떻게 조치하는지 투명하게 밝히는 것이 결국 신뢰를 지키는 길이다. 정보를 맡긴 사용자에 대한 책임감이 모든 기술적 방어의 밑바탕에 있어야 한다는 걸, 나는 사이트를 운영하며 점점 더 깊이 느꼈다.


5. 시리즈를 마치며

열네 편에 걸쳐 신생 커뮤니티를 지키는 여러 방어를 다뤘다. 돌아보면 모든 이야기가 몇 가지 단순한 원리로 수렴했다. 사람과 기계를 구분하고, 반복을 값비싸게 만들고, 민감한 정보는 평문으로 두지 않고, 신뢰를 함부로 주지 않고 매번 확인한다. 화려한 기법보다 이 기본기를 빠뜨리지 않는 것이 훨씬 중요했다. 그리고 어떤 방어도 완벽하지 않으니 여러 겹으로 쌓아야 한다는 심층 방어의 정신이 그 위에 있었다.


또 하나 강조하고 싶은 건, 완벽을 목표로 지쳐 포기하기보다 지금 할 수 있는 만큼을 확실히 하는 게 낫다는 것이다. 1인 운영자에게 모든 방어를 완벽히 갖추는 건 불가능하다. 하지만 위험도에 따라 우선순위를 정하고, 할 수 있는 것부터 하나씩 쌓아 가면 사이트는 충분히 성가신 상대가 된다. 첫 편에서 말했듯 신생 사이트의 현실적인 목표는 무적이 아니라 성가심이다. 그 성가심이 대다수 자동 공격자를 돌려보낸다.


개인정보 보호를 마지막 편으로 둔 데는 이유가 있다. 지금까지 다룬 모든 방어가 결국 이 하나로 수렴하기 때문이다. 봇을 막는 것도, 인증을 지키는 것도, 입력을 정화하는 것도, 권한을 확인하는 것도, 궁극적으로는 사용자가 맡긴 정보와 신뢰를 지키기 위한 것이었다. 방어를 개별 기능으로만 보면 조각조각 흩어지지만, 무엇을 위한 방어인가를 물으면 모든 조각이 하나의 목적으로 꿰어졌다. 그 목적을 잊지 않는 것이 방어의 방향을 잃지 않는 길이었다.


그리고 개인정보 보호는 법과 제도의 영역이기도 하다는 걸 덧붙이고 싶다. 기술적 방어를 넘어, 사용자에게 무엇을 왜 수집하는지 정직하게 알리고, 사용자가 자기 정보를 확인하고 삭제할 수 있게 하는 것도 운영자의 책임이다. 이 부분은 기술만의 문제가 아니라 사용자를 대하는 태도의 문제라, 이 시리즈에서 깊이 다루지는 못했지만 결코 소홀히 해선 안 되는 영역임을 마지막으로 짚어 둔다.


이 시리즈가 나처럼 홀로 서비스를 만드는 이들에게 작은 지도가 되었으면 한다. 보안은 무섭고 어려운 전문가의 영역처럼 보이지만, 원리를 이해하고 기본을 지키면 누구나 시작할 수 있다. 배포 첫날 로그를 보고 덜컥 겁이 났던 나도 하나씩 방어를 쌓아 가며 여기까지 왔다. 사용자가 맡긴 신뢰를 지키겠다는 마음, 그것이 모든 기술의 출발점이었다. 긴 시리즈를 함께해 준 독자들께 감사드리며, 각자의 사이트가 안전하기를 바란다.