관리용 서브도메인은 CSRF를 막지 않는다: 출시 전 네 가지 점검
새 호스트 이름은 새 오리진이며 대개 같은 사이트입니다. 그래서 same-site 쿠키와 단순 폼이 이를 넘어갑니다. 토큰과 게이트, 권한 부여를 점검하세요.
관리 인터페이스를 admin.example.com으로 옮기는 것은 실제 개선입니다. 관리 쿠키를 공개 페이지와 멀리 두고, 두 애플리케이션에 별도 캐시를 주며, 더 엄격한 네트워크 정책을 하나의 호스트 이름에 적용할 수 있습니다. 그러나 그것은 CSRF 통제가 아니며, 공개 사이트의 코드를 로그인한 관리자 앞에 열어 두어도 안전하게 만들지 않습니다. 분리는 브라우저가 경계로 보는 것을 바꿀 뿐 서버가 검사하는 것을 바꾸지 않으므로, 작업이 끝났다고 말하기 전에 이 네 가지 점검을 하십시오.
오리진과 사이트를 서로 다른 두 단어로 읽기
오리진은 스킴과 호스트, 포트이므로 https://www.example.com과 https://admin.example.com은 서로 다른 오리진입니다. SameSite 의미의 사이트는 스킴에 등록 가능 도메인을 더한 것이므로, 이 두 호스트 이름은 보통 같은 사이트입니다. 이 차이가 함정의 전부입니다. 브라우저는 그 요청을 애초에 사이트 간으로 보지 않기 때문에, SameSite 속성은 형제 서브도메인 사이에서 도움이 되지 않습니다. 관리 인터페이스를 admin-example.net처럼 정말 다른 등록 가능 도메인으로 돌리면 요청은 실제로 사이트 간이 되고, 그때 SameSite=Lax나 Strict가 신뢰할 수 있는 일을 시작합니다.
그것에 대해 추론하는 대신 브라우저에서 시험하십시오.
공개 사이트를 불러오고 개발자 도구를 열어, 관리 호스트로 향하는 요청에 브라우저가 어떤 쿠키를 붙이는지 보십시오. 쿠키에 Domain 속성이 없으면 호스트 전용이며 자기 호스트 이름에 머뭅니다. Domain=example.com을 가지면 모든 형제에게 가고, 새 서브도메인은 그 쿠키에 대해 아무것도 얻지 못한 것입니다.
응답을 읽는 것과 요청을 보내는 것을 분리하기
동일 오리진 정책이 제한하는 것은 오리진 사이의 응답 읽기입니다. 요청이 전송되는 것을 막지 않고, 브라우저가 이미 가진 자격 증명을 폼이 실어 보내는 것도 막지 않습니다. 폼 콘텐츠 유형이나 단순한 GET을 가진 단순 요청은 사전 검사 없이 전달되고, 그 뒤에 CORS가 호출 스크립트가 응답을 읽을 수 있는지 정합니다. CSRF는 전적으로 보내는 쪽에 있습니다.
그래서 서버는 상태를 바꾸는 작업에 명시적 검사가 필요합니다. 세션에 묶인 동기화 토큰, 또는 서버에서 비교하는 이중 제출 쿠키가 그 경우를 덮습니다. Origin이나 Referer 비교는 두 번째 게이트로 더할 가치가 있지만 유일한 게이트가 될 수는 없습니다. 모든 요청이 그 헤더를 지니지 않고 모든 프록시가 그것을 보존하지도 않기 때문입니다. OWASP의 CSRF 예방 지침이 SameSite를 여러 겹 중 하나로 보는 이유가 바로 이것입니다.
네트워크 게이트가 실제로 무엇을 증명하는지 정하기
관리 호스트 앞의 VPN 출구 허용 목록은 좋은 통제이자 약한 신원 주장입니다. 요청이 VPN이 주는 주소에서 왔다는 것은 증명하지만, 그 출구에 있는 모두가 공유할 수 있는 주소이며, 뒤에 어떤 사람이나 기기가 있는지는 말하지 않습니다. 입구 필터로 다루고 인증으로 다루지 마십시오.
요청 수준의 검사와 독립적으로 유지하십시오. 허용 목록이 공격자와 상태를 바꾸는 끝점 사이에 있는 유일한 것이라면, 잘못 설정된 경로 하나, split-tunnel 클라이언트 하나, 빌린 노트북 하나가 통제를 완전히 없앱니다.
예상 범위 밖에서 오는 거부도 기록하십시오. 그 폭주는 누군가 방금 만든 경계를 탐색하고 있다는 가장 유용한 신호이고, 침묵은 필터에 아무것도 닿지 않는다는 신호입니다.
쿠키와 로그인, 권한 부여를 명시적으로 유지하기
쿠키의 범위가 결과의 대부분을 정합니다. 상위 도메인 쿠키는 관리 호스트로 보내지므로, 공개 사이트의 스크립트 주입이 관리자의 세션으로 행동할 수 있습니다. 호스트 전용 쿠키는 그럴 수 없습니다. 호스트 전용 형태를 선호하고 Secure 속성을 더하며, 플랫폼이 지원하는 곳에서는 __Host- 접두사를 쓰십시오. 그 접두사는 Secure와 Path=/, Domain 속성 없음을 요구합니다. 쿠키 단지가 아니라 응답 헤더를 읽으십시오. Set-Cookie: __Host-admin_session=...; Secure; Path=/; HttpOnly; SameSite=Lax에는 Domain 속성이 없습니다. 브라우저는 Domain이 있는 __Host- 쿠키를 버리므로, 형제 호스트에서 여전히 보이는 쿠키는 접두사가 결코 적용되지 않았다는 증거입니다.
다음으로 권한 부여를 서버에서, 요청마다, 모든 관리 끝점에서 유지하십시오. 로그인 때만이 아닙니다. 허용된 네트워크에서 온 요청도 여전히 인증되고 요청한 작업에 대해 권한이 부여되어야 합니다. 호스트 이름은 행위자가 아닙니다. 공개 애플리케이션과 관리 애플리케이션이 로그인 흐름이나 세션 저장소를 공유한다면 그 관계를 적어 두십시오. 공개 사이트의 결함에서 관리 작업으로 가는 가장 짧은 길이기 때문입니다.
분리 후 경계를 다시 점검하기
정상 경로만이 아니라 부정적 시험을 실행하십시오. 토큰 없는 사이트 간 폼은 거부되어야 하고, 다른 세션의 토큰도 거부되어야 하며, 어떤 GET도 상태를 바꾸지 않아야 합니다. 개발자 도구에서 쿠키 속성을 확인하고, 일치하지 않는 요청에서 Origin 검사가 작동하는지 확인하며, 필터가 일하는 것을 보려고 VPN 범위 밖에서 관리 호스트를 시도하십시오.
이 중 어느 것도 관리 애플리케이션 자체 내부의 XSS 결함을 없애지 않습니다. 별도 호스트 이름은 실제 가치가 있는 브라우저 측 격리 경계이며, 안전하지 않은 코드 실행 방지나 서버 측 요청 보호를 대신하지 않습니다. 분리 후의 목록은 보이는 것보다 짧습니다. 별도 오리진, 호스트 전용 쿠키, 변경마다의 토큰, 신원으로 신뢰하지 않는 입구 필터, 그리고 요청마다 정해지는 권한 부여입니다.