보안
보안 클래스에는 사이트를 사이트 간 요청 위조(Cross-Site Request Forgery) 공격으로부터 보호하는 데 도움이 되는 메서드가 포함되어 있습니다.
라이브러리 로드하기
라이브러리를 로드하는 목적이 CSRF 보호 처리뿐이라면 로드할 필요가 없습니다. 필터로 실행되며 수동 상호 작용이 필요하지 않기 때문입니다.
그러나 직접 접근이 필요한 경우 Services 파일을 통해 로드할 수 있습니다.
<?php
$security = service('security');
사이트 간 요청 위조(CSRF)
경고
CSRF 보호는 POST/PUT/PATCH/DELETE 요청에 대해서만 사용할 수 있습니다. 다른 메서드의 요청은 보호되지 않습니다.
사전 요구 사항
CodeIgniter의 CSRF 보호를 사용할 때도 다음과 같이 코드를 작성해야 합니다. 그렇지 않으면 CSRF 보호가 우회될 수 있습니다.
자동 라우팅이 비활성화된 경우
다음 중 하나를 수행하세요.
$routes->add()를 사용하지 말고, 라우트에 HTTP 동사를 사용하세요.처리 전에 컨트롤러 메서드에서 요청 메서드를 확인하세요.
예:
if (! $this->request->is('post')) {
return $this->response->setStatusCode(405)->setBody('Method Not Allowed');
}
참고
$this->request->is() 메서드는 v4.3.0부터 사용할 수 있습니다. 이전 버전에서는 if (strtolower($this->request->getMethod()) !== 'post')를 사용해야 합니다.
자동 라우팅이 활성화된 경우
처리 전에 컨트롤러 메서드에서 요청 메서드를 확인하세요.
예:
if (! $this->request->is('post')) {
return $this->response->setStatusCode(405)->setBody('Method Not Allowed');
}
CSRF 설정
CSRF 보호 방법
경고
세션을 사용하는 경우 세션 기반 CSRF 보호를 사용하세요. 쿠키 기반 CSRF 보호는 Same-site 공격을 방지하지 못합니다. 자세한 내용은 GHSA-5hm8-vh6r-2cjq를 참고하세요.
기본적으로 쿠키 기반 CSRF 보호가 사용됩니다. 이는 OWASP Cross-Site Request Forgery Prevention Cheat Sheet의 Double Submit Cookie입니다.
세션 기반 CSRF 보호를 사용할 수도 있습니다. 이는 Synchronizer Token Pattern입니다.
app/Config/Security.php의 다음 설정 매개변수 값을 편집하여 세션 기반 CSRF 보호를 사용하도록 설정할 수 있습니다.
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Security extends BaseConfig
{
public $csrfProtection = 'session';
// ...
}
토큰 무작위화
BREACH와 같은 압축 사이드 채널 공격을 완화하고 공격자가 CSRF 토큰을 추측하지 못하도록 토큰 무작위화를 구성할 수 있습니다(기본적으로 꺼져 있음).
활성화하면 토큰에 무작위 마스크가 추가되어 토큰을 뒤섞는 데 사용됩니다.
app/Config/Security.php의 다음 설정 매개변수 값을 편집하여 활성화할 수 있습니다.
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Security extends BaseConfig
{
public $tokenRandomize = true;
// ...
}
토큰 재생성
토큰은 매 제출 시마다 재생성(기본값)되거나, 세션 또는 CSRF 쿠키의 수명 동안 동일하게 유지될 수 있습니다.
기본 토큰 재생성은 더 엄격한 보안을 제공하지만, 다른 토큰이 무효화되어 사용성 문제가 발생할 수 있습니다 (뒤로/앞으로 탐색, 여러 탭/창, 비동기 작업 등). app/Config/Security.php의 다음 설정 매개변수 값을 편집하여 이 동작을 변경할 수 있습니다.
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Security extends BaseConfig
{
public $regenerate = true;
// ...
}
경고
쿠키 기반 CSRF 보호를 사용하고 제출 후 redirect()를 사용하는 경우, 재생성된 CSRF 쿠키를 전송하기 위해 withCookie()를 호출해야 합니다. 자세한 내용은 리디렉션를 참고하세요.
참고
v4.2.3부터 Security::generateHash() 메서드로 CSRF 토큰을 수동으로 재생성할 수 있습니다.
실패 시 리다이렉션
v4.5.0부터 요청이 CSRF 유효성 검사에 실패하면 기본적으로 프로덕션 환경에서는 사용자가 이전 페이지로 리다이렉트되고, 다른 환경에서는 SecurityException이 발생합니다.
참고
프로덕션 환경에서 HTML 폼을 사용하는 경우, 더 나은 사용자 경험을 위해 이 리다이렉션을 활성화하는 것이 권장됩니다.
업그레이드 사용자는 설정 파일을 확인해야 합니다.
이전 페이지로 리다이렉트하려면 app/Config/Security.php에서 다음 설정 매개변수 값을 true로 설정하세요.
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Security extends BaseConfig
{
// ...
public bool $redirect = true;
// ...
}
리다이렉트 시 error 플래시 메시지가 설정되며, 뷰에서 다음 코드로 최종 사용자에게 표시할 수 있습니다:
<?= session()->getFlashdata('error') ?>
이는 단순히 오류가 발생하는 것보다 더 나은 경험을 제공합니다.
리다이렉트 값이 true이더라도 AJAX 호출은 리다이렉트되지 않고 SecurityException이 발생합니다.
CSRF 보호 활성화
app/Config/Filters.php를 수정하고 csrf 필터를 전역으로 활성화하여 CSRF 보호를 활성화할 수 있습니다.
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
public $globals = [
'before' => [
// 'honeypot',
'csrf',
],
];
// ...
}
특정 URI를 CSRF 보호 대상에서 제외할 수 있습니다(예: 외부에서 POST된 콘텐츠를 기대하는 API 엔드포인트). 필터에서 예외로 추가하여 이 URI들을 등록할 수 있습니다.
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
public $globals = [
'before' => [
'csrf' => ['except' => ['api/record/save']],
],
];
// ...
}
정규 표현식도 지원됩니다(대소문자 구분 없음).
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
public $globals = [
'before' => [
'csrf' => ['except' => ['api/record/[0-9]+']],
],
];
// ...
}
특정 메서드에 대해서만 CSRF 필터를 활성화하는 것도 가능합니다.
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Filters extends BaseConfig
{
public $methods = [
'GET' => ['csrf'],
'POST' => ['csrf'],
];
// ...
}
경고
$methods 필터를 사용하는 경우 자동 라우팅(레거시)를 비활성화해야 합니다. 왜냐하면 Auto Routing (Legacy)는 모든 HTTP 메서드로 컨트롤러에 접근할 수 있게 허용하기 때문입니다. 예상하지 못한 메서드로 컨트롤러에 접근하면 필터를 우회할 수 있습니다.
HTML 폼
폼 헬퍼를 사용하면 form_open()이 자동으로 폼에 숨겨진 CSRF 필드를 삽입합니다.
참고
CSRF 필드 자동 생성을 사용하려면 폼 페이지에 CSRF 필터를 켜야 합니다. 대부분의 경우 GET 메서드를 사용하여 요청됩니다.
그렇지 않은 경우 항상 사용 가능한 csrf_token() 및 csrf_hash() 함수를 사용할 수 있습니다:
<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" />
또한 csrf_field() 메서드를 사용하여 이 숨겨진 입력 필드를 생성할 수 있습니다:
// Generates: <input type="hidden" name="{csrf_token}" value="{csrf_hash}" />
<?= csrf_field() ?>
JSON 요청을 보낼 때 CSRF 토큰을 매개변수 중 하나로 전달할 수도 있습니다. CSRF 토큰을 전달하는 또 다른 방법은 csrf_header() 함수로 이름을 가져올 수 있는 특수 HTTP 헤더를 사용하는 것입니다.
또한 csrf_meta() 메서드를 사용하여 편리한 메타 태그를 생성할 수 있습니다:
// Generates: <meta name="{csrf_header}" content="{csrf_hash}" />
<?= csrf_meta() ?>
사용자가 전송하는 토큰의 순서
CSRF 토큰의 사용 가능 여부를 확인하는 순서는 다음과 같습니다.
$_POST배열HTTP 헤더
php://input(JSON 요청) - JSON을 디코딩한 후 다시 인코딩해야 하므로 이 방법이 가장 느립니다php://input(원시 본문) - PUT, PATCH, DELETE 유형의 요청에 사용
참고
php://input (원시 본문)은 v4.4.2부터 확인됩니다.
기타 유용한 메서드
보안 클래스의 대부분의 메서드를 직접 사용할 필요는 없습니다. 다음은 CSRF 보호와 관련이 없지만 유용할 수 있는 메서드들입니다.
sanitizeFilename()
디렉토리 탐색 시도 및 기타 보안 위협을 방지하기 위해 파일 이름을 정제하려고 시도합니다. 사용자 입력으로 제공된 파일에 특히 유용합니다. 첫 번째 매개변수는 정제할 경로입니다.
사용자 입력에 상대 경로(예: file/in/some/approved/folder.txt)를 포함하는 것이 허용되는 경우, 두 번째 선택적 매개변수 $relativePath를 true로 설정할 수 있습니다.
<?php
$path = $security->sanitizeFilename($request->getVar('filepath'));
이 메서드는 보안 헬퍼의 sanitize_filename() 함수에 대한 별칭입니다.