세션 처리 세션 관리 기본

세션 보안

세션 모듈은 세션에 저장된 정보가 세션을 생성한 사용자만 볼 수 있다고 보장할 수 없습니다. 세션과 관련된 값에 따라 세션의 기밀성을 보호하기 위해 추가 조치가 필요합니다.

세션에서 전달되는 데이터의 중요성을 평가해야 하며 추가 보호가 배치될 수 있습니다. 이것은 일반적으로 사용자의 편의성 감소와 같은 대가를 치르게 합니다. 예를 들어, 간단한 사회 공학 전술로부터 사용자를 보호하려면 session.use_only_cookies를 활성화해야 합니다. 이 경우 클라이언트 측에서 무조건 쿠키를 활성화해야 합니다. 그렇지 않으면 세션이 작동하지 않습니다.

기존 세션 ID를 제3자에게 유출하는 방법에는 여러 가지가 있습니다. 예를 들어 JavaScript 삽입, URL의 세션 ID, 패킷 스니핑, 장치에 대한 물리적 액세스 등 유출된 세션 ID를 통해 제3자는 특정 ID와 연결된 모든 리소스에 액세스할 수 있습니다. 첫째, 세션 ID를 전달하는 URL입니다. 외부 사이트나 리소스에 대한 링크가 있는 경우 세션 ID가 포함된 URL이 외부 사이트의 리퍼러 로그에 저장될 수 있습니다. 둘째, 보다 적극적인 공격자가 네트워크 트래픽을 수신할 수 있습니다. 암호화되지 않은 경우 세션 ID는 네트워크를 통해 일반 텍스트로 흐릅니다. 솔루션은 서버에서 SSL/TLS를 구현하고 사용자에게 필수로 만드는 것입니다. 보안 향상을 위해 HSTS를 사용해야 합니다.

참고: HTTPS도 기밀 데이터를 항상 보호할 수는 없습니다. 예를 들어 CRIME 및 Beast 취약점으로 인해 공격자가 데이터를 읽을 수 있습니다. 또한 많은 네트워크에서 감사 목적으로 HTTPS MITM 프록시를 사용합니다. 공격자는 이러한 프록시를 설정할 수도 있습니다.


비적응 세션 관리

PHP의 세션 관리자는 현재 기본적으로 적응형입니다. 적응형 세션 관리자는 추가 위험을 감수합니다.

session.use_strict_mode가 활성화되고 세션 저장 핸들러가 이를 지원하면 초기화되지 않은 세션 ID가 거부되고 새 세션 ID가 생성됩니다. 이것은 사용자가 알려진 세션 ID를 사용하도록 하는 공격을 방지합니다. 공격자는 링크를 붙여넣거나 세션 ID가 포함된 이메일을 보낼 수 있습니다. 예를 들어 http://example.com/page.php?PHPSESSID=123456789 session.use_trans_sid가 활성화된 경우 피해자는 공격자가 제공한 세션 ID를 사용하여 세션을 시작합니다. session.use_strict_mode는 이 위험을 완화합니다.

경고 사용자 정의 저장 핸들러는 세션 ID 유효성 검사를 구현하여 엄격한 세션 모드를 지원할 수도 있습니다. 모든 사용자 정의 저장 핸들러는 세션 ID 유효성 검증을 구현해야 합니다.

세션 ID 쿠키는 도메인, 경로, httponly, secure 및 PHP 7.3부터 SameSite 속성으로 설정할 수 있습니다. 브라우저에서 정의한 우선 순위가 있습니다. 우선 순위를 사용하여 공격자는 영구적으로 사용할 수 있는 세션 ID를 설정할 수 있습니다. session.use_only_cookies를 사용해도 이 문제가 해결되지 않습니다. session.use_strict_mode는 이 위험을 완화합니다. session.use_strict_mode=On으로 설정하면 초기화되지 않은 세션 ID가 거부됩니다.

메모session.use_strict_mode가 적응형 세션 관리의 위험을 완화하더라도 공격자는 사용자가 공격자가 생성한 초기화된 세션 ID를 사용하도록 할 수 있습니다. 예를 들어 자바스크립트 인젝션. 이 공격은 이 설명서의 권장 사항으로 완화할 수 있습니다. 이 매뉴얼에 따라 개발자는 session.use_strict_mode를 활성화하고, 타임스탬프 기반 세션 관리를 사용하고, 권장 절차에 따라 session_regenerate_id()를 사용하여 세션 ID를 재생성해야 합니다. 개발자가 위의 모든 사항을 따르면 공격자가 생성한 세션 ID는 결국 삭제됩니다. 구식 세션에 대한 액세스가 발생하면 개발자는 사용자의 모든 활성 세션 데이터를 저장해야 합니다. 이 정보는 후속 조사와 관련이 있기 때문입니다. 사용자는 모든 세션에서 강제로 로그아웃해야 합니다. 즉, 재인증을 요구합니다. 이렇게 하면 공격자가 도난당한 세션을 악용하는 것을 방지할 수 있습니다.

경고 더 이상 사용되지 않는 세션에 대한 액세스가 반드시 공격을 암시하는 것은 아닙니다. 불안정한 네트워크 및/또는 활성 세션의 즉각적인 삭제로 인해 합법적인 사용자가 사용되지 않는 세션을 사용하게 됩니다.

PHP 7.1.0부터 session_create_id()가 추가되었습니다. 이 함수는 세션 ID에 사용자 ID를 접두사로 붙여 사용자의 모든 활성 세션에 효율적으로 액세스하도록 작동할 수 있습니다. 이 설정에서는 session.use_strict_mode를 활성화하는 것이 중요합니다. 그렇지 않으면 악의적인 사용자가 다른 사용자에 대해 악의적인 세션 ID를 설정할 수 있습니다.

참고: PHP 7.1.0 이전의 사용자는 CSPRNG를 사용해야 합니다. /dev/urandom 또는 random_bytes() 및 해시 함수를 사용하여 새 세션 ID를 생성합니다. session_create_id()는 충돌 감지 기능이 있으며 세션의 INI 설정에 따라 세션 ID를 생성합니다. session_create_id()를 사용하는 것이 좋습니다.


세션 ID 재생성

session.use_strict_mode는 좋은 완화 방법이지만 충분하지는 않습니다. 개발자는 세션 보안을 위해 session_regenerate_id()를 동등하게 사용해야 합니다.

세션 ID 재생성은 세션 ID 도난의 위험을 줄이므로 session_regenerate_id()를 주기적으로 호출해야 합니다. 예를 들어 보안에 민감한 콘텐츠의 경우 15분마다 세션 ID를 다시 생성합니다. 세션 ID가 도용된 경우에도 적법한 사용자와 공격자의 세션은 모두 만료됩니다. 즉, 사용자 또는 공격자가 액세스하면 구식 세션 액세스 오류가 생성됩니다.

인증 후와 같이 사용자 권한이 상승할 때 세션 ID를 다시 생성해야 합니다. session_regenerate_id()는 인증 정보를 $_SESSION으로 설정하기 전에 호출되어야 합니다. (session_regenerate_id()는 현재 세션에 타임스탬프 등을 저장하기 위해 현재 세션 데이터를 자동으로 저장합니다.) 새 세션에만 인증된 플래그가 포함되어 있는지 확인합니다.

개발자는 session.gc_maxlifetime에 의한 세션 ID 만료에 의존해서는 안 됩니다. 공격자는 피해자의 세션 ID에 주기적으로 액세스하여 만료를 방지하고 인증된 세션을 포함하여 계속 악용할 수 있습니다.

대신 개발자는 타임스탬프 기반 세션 데이터 관리를 구현해야 합니다.

경고 세션 관리자는 타임스탬프를 투명하게 관리할 수 있지만 이 기능은 구현되지 않습니다. 이전 세션 데이터는 GC까지 유지되어야 합니다. 동시에 개발자는 사용되지 않는 세션 데이터가 제거되었는지 확인해야 합니다. 그러나 개발자는 활성 세션 데이터를 즉시 제거해서는 안 됩니다. 즉. session_regenerate_id(true); 활성 세션에 대해 session_destroy()를 함께 호출해서는 안 됩니다. 이것은 모순되게 들릴지 모르지만 이것은 필수 요구 사항입니다.

session_regenerate_id()는 기본적으로 오래된 세션을 삭제하지 않습니다. 사용하지 않는 인증된 세션이 있을 수 있습니다. 개발자는 오래된 세션이 누군가에 의해 소비되는 것을 방지해야 합니다. 타임스탬프를 사용하여 자체적으로 사용되지 않는 세션 데이터에 대한 액세스를 금지해야 합니다.

경고 활성 세션을 갑자기 제거하면 바람직하지 않은 부작용이 발생합니다. 웹 애플리케이션에 동시 연결이 있거나 네트워크가 불안정하면 세션이 사라질 수 있습니다.

활성 세션이 갑자기 제거되면 잠재적인 악의적인 액세스를 감지할 수 없습니다.

개발자는 오래된 세션을 즉시 삭제하는 대신 $_SESSION에 단기 만료 시간(timestamp)을 설정하고 세션 데이터에 스스로 접근을 금지해야 합니다.

개발자는 session_regenerate_id() 직후 이전 세션 데이터에 대한 액세스를 금지해서는 안 됩니다. 이후 단계에서 금지되어야 합니다. 예를 들어 유선 네트워크와 같은 안정적인 네트워크의 경우 몇 초 후, 휴대폰이나 Wi-Fi와 같은 불안정한 네트워크의 경우 몇 분 후입니다.

사용자가 사용되지 않는 세션(만료된 세션)에 액세스하면 해당 세션에 대한 액세스가 거부되어야 합니다. 또한 공격을 나타낼 가능성이 있으므로 모든 사용자 세션에서 인증된 상태를 제거하는 것이 좋습니다.

session.use_only_cookiessession_regenerate_id()를 적절히 사용하면 공격자가 설정한 삭제 불가능한 쿠키로 개인 DoS가 발생할 수 있습니다. 이 경우 개발자는 사용자에게 쿠키를 제거하도록 초대하고 보안 문제의 영향을 받을 수 있음을 알릴 수 있습니다. 공격자는 취약한 웹 애플리케이션, 노출/악성 브라우저 플러그인, 물리적으로 손상된 장치 등을 통해 악성 쿠키를 설정할 수 있습니다.

경고 DoS 위험을 오해하지 마십시오. use_strict_mode=On는 일반 세션 ID 보안을 위해 필수입니다! 모든 사이트는 use_strict_mode를 활성화하는 것이 좋습니다.

DoS는 계정이 공격을 받고 있는 경우에만 발생할 수 있습니다. 애플리케이션의 JavaScript 주입 취약점이 가장 일반적인 원인입니다.


세션 데이터 삭제

사용되지 않는 세션 데이터는 액세스할 수 없고 삭제되어야 합니다. 현재 세션 모듈은 이것을 잘 처리하지 못합니다.

사용되지 않는 세션 데이터는 가능한 한 빨리 제거해야 합니다. 그러나 활성 세션을 즉시 제거해서는 안 됩니다. 이러한 요구 사항을 충족하려면 개발자가 자체적으로 타임스탬프 기반 세션 데이터 관리를 구현해야 합니다.

$_SESSION에서 만료 타임스탬프를 설정하고 관리합니다. 오래된 세션 데이터에 대한 액세스를 금지합니다. 사용되지 않는 세션 데이터에 대한 액세스가 감지되면 사용자 세션에서 모든 인증된 상태를 제거하고 강제로 다시 인증하는 것이 좋습니다. 사용되지 않는 세션 데이터에 대한 액세스는 공격을 나타낼 수 있습니다. 이를 달성하기 위해 개발자는 모든 사용자의 모든 활성 세션을 추적해야 합니다.

메모: 사용되지 않는 세션에 대한 액세스는 불안정한 네트워크 및/또는 웹 사이트에 대한 동시 액세스로 인해 발생할 수도 있습니다. 예를 들어 서버가 쿠키를 통해 새 세션 ID를 설정하려고 시도했지만 연결 손실로 인해 Set-Cookie 패킷이 클라이언트에 도달하지 못했을 수 있습니다. 한 연결은 session_regenerate_id()에 의해 새 세션 ID를 발행할 수 있지만 다른 동시 연결은 아직 새 세션 ID를 받지 않았을 수 있습니다. 따라서 개발자는 이후 단계에서 사용되지 않는 세션에 대한 액세스를 금지해야 합니다. 즉. 타임스탬프 기반 세션 관리는 필수입니다.

요약하면 session_regenerate_id() 또는 session_destroy()를 사용하여 세션 데이터를 삭제해서는 안 되지만 세션 데이터에 대한 액세스를 제어하려면 타임스탬프를 사용해야 합니다. session_gc()가 세션 데이터 저장소에서 사용되지 않는 데이터를 제거하도록 합니다.


세션 및 잠금

세션 데이터는 기본적으로 경쟁 조건을 피하기 위해 잠겨 있습니다. 요청 간에 세션 데이터를 일관되게 유지하려면 잠금이 필수입니다.

그러나 세션 잠금은 공격자가 DoS 공격을 수행하기 위해 남용할 수 있습니다. 세션 잠금에 의한 DoS 공격의 위험을 완화하려면 잠금을 최소화하십시오. 세션 데이터를 업데이트할 필요가 없는 경우 읽기 전용 세션을 사용합니다. session_start()와 함께 'read_and_close' 옵션을 사용하십시오. session_start(['read_and_close'=>1]); session_commit()을 사용하여 $_SESSION을 업데이트한 후 가능한 한 빨리 세션을 닫습니다.

현재 세션 모듈은 세션이 비활성 상태일 때 $_SESSION의 수정 사항을 감지하지 못합니다. 세션이 비활성 상태일 때 $_SESSION을 수정하지 않는 것은 개발자의 책임입니다.


활성 세션

개발자는 모든 사용자의 모든 활성 세션을 추적해야 합니다. 그리고 그들에게 얼마나 많은 활성 세션, 어느 IP(및 영역)에서, 얼마나 오랫동안 활성 세션이 있었는지 등을 알려 주십시오. PHP는 이러한 세션을 추적하지 않습니다. 개발자는 그렇게 해야 합니다.

이를 구현하는 다양한 방법이 있습니다. 한 가지 가능한 구현은 필요한 데이터를 추적하고 관련 정보를 저장하는 데이터베이스를 설정하는 것입니다. 세션 데이터는 GCed이므로 개발자는 활성 세션 데이터베이스 일관성을 유지하기 위해 GCed 데이터를 처리해야 합니다.

가장 간단한 구현 중 하나는 "사용자 ID 접두사 세션 ID"이며 필요한 정보를 $_SESSION에 저장합니다. 많은 데이터베이스는 문자열 접두사를 선택하는 데 좋은 성능을 가지고 있습니다. 개발자는 이를 위해 session_regenerate_id()session_create_id()를 사용할 수 있습니다(MAY).

경고 기밀 데이터를 접두사로 사용하지 마십시오. 사용자 ID가 기밀인 경우 hash_hmac() 사용을 고려하십시오.

경고 이 설정에서는 session.use_strict_mode를 활성화해야 합니다. 활성화되어 있는지 확인하십시오. 그렇지 않으면 활성 세션 데이터베이스가 손상될 수 있습니다.

사용되지 않는 세션에 대한 액세스를 감지하려면 타임스탬프 기반 세션 관리가 필수입니다. 사용되지 않는 세션에 대한 액세스가 감지되면 사용자의 모든 활성 세션에서 인증 플래그를 제거해야 합니다. 이것은 공격자가 도난당한 세션을 계속 악용하는 것을 방지합니다.


세션 및 자동 로그인

개발자는 자동 로그인에 긴 수명의 세션 ID를 사용하면 안 됩니다. 세션을 도난당할 위험이 높기 때문입니다. 자동 로그인 기능은 개발자가 구현해야 합니다.

setcookie()를 사용하여 안전한 일회용 해시 키를 자동 로그인 키로 사용하십시오. SHA-2보다 강력한 보안 해시를 사용합니다. 예를 들어 random_bytes() 또는 /dev/urandom의 임의 데이터가 있는 SHA-256 이상.

사용자가 인증되지 않은 경우 1회용 자동 로그인 키가 유효한지 확인하십시오. 유효한 경우 사용자를 인증하고 새로운 보안 일회성 해시 키를 설정합니다. 자동 로그인 키는 한 번만 사용해야 합니다. 즉, 자동 로그인 키를 재사용하지 말고 항상 새 키를 생성해야 합니다.

자동 로그인 키는 수명이 긴 인증 키이므로 최대한 보호해야 합니다. path/httponly/secure/SameSite 쿠키 속성을 사용하여 보안을 유지합니다. 즉. 필요한 경우가 아니면 자동 로그인 키를 전송하지 마십시오.

개발자는 자동 로그인을 비활성화하고 불필요한 자동 로그인 키 쿠키를 제거하는 기능을 구현해야 합니다.


CSRF(Cross-Site Request Forgeries) 공격

세션 및 인증은 CSRF 공격으로부터 보호하지 않습니다. 개발자는 스스로 CSRF 보호를 구현해야 합니다.

output_add_rewrite_var()는 CSRF 보호에 사용할 수 있습니다. 자세한 내용은 매뉴얼 페이지를 참조하십시오.

참고: 7.2.0 이전의 PHP는 trans sid와 동일한 출력 버퍼 및 INI 설정을 사용합니다. 따라서 PHP 7.2.0 이전 버전에서는 output_add_rewrite_var()를 사용하지 않는 것이 좋습니다.

대부분의 웹 애플리케이션 프레임워크는 CSRF 보호를 지원합니다. 자세한 내용은 웹 애플리케이션 프레임워크 매뉴얼을 참조하십시오.

PHP 7.3부터 세션 쿠키에 대한 SameSite 속성을 설정할 수 있습니다. 이것은 CSRF 취약성을 완화할 수 있는 추가 조치입니다.