메뉴 건너뛰기

자료실

DATA

PHP 7.3 이상

세션은 session.cookie_secure = true; session.cookie_samesite = "None"; 만으로 해결됩니다.

이외의 쿠키가 타 사이트에서 넘어올 때도 살아있어야 하면 setcookie() 시 $options 변수에 세팅합니다. [매뉴얼]

session_start_samesite()

session_start() 대신 session_start_samesite() 를 사용합니다.

PHP 7.3 이상에서 session.cookie_secure = true; session.cookie_httponly = false; session.cookie_samesite = "None"; 한 것과 동일한 효과를 냅니다.

PHP 7.3 이상으로 업데이트한 이후에는 session_start() 로 치환 가능합니다.

http 접속시 쿠키가 secure 로 구워지지 않으며, 기존에 쿠키가 있을 경우 session_start() 시에도 새로 굽지 않기 때문에 로그인 같은 곳에서 session_regenerate_id() 를 해서 쿠키를 다시 굽는 과정이 필요하고, session_regenerate_id_samesite() 로 대체할 수 있습니다.

if(!function_exists('session_start_samesite')) {
	function session_start_modify_cookie()
	{
		$headers = headers_list();
		krsort($headers);
		foreach ($headers as $header) {
			if (!preg_match('~^Set-Cookie: PHPSESSID=~', $header)) continue;
			$header = preg_replace('~; secure(; HttpOnly)?$~', '', $header) . '; secure; SameSite=None';
			header($header, false);
			break;
		}
	}

	function session_start_samesite($options = [])
	{
		$res = session_start($options);
		session_start_modify_cookie();
		return $res;
	}

	function session_regenerate_id_samesite($delete_old_session = false)
	{
		$res = session_regenerate_id($delete_old_session);
		session_start_modify_cookie();
		return $res;
	}
}

setcookie_samesite()

세션 외 쿠키도 타도메인간 이동에 필요할 경우에 사용합니다.

setcookie() 대신 setcookie_samesite() 를 사용합니다.

PHP 7.3 이상의 setcookie() 와 동일하게 $options 로 세팅할 수 있습니다.

PHP 7.3 이상으로 업데이트한 이후에는 setcookie() 로 치환 가능합니다.

오류처리시 원래의 E_WARNING 을 낼 수 없어 E_USER_WARNING 으로 대체했으니 set_error_handler() 를 정의해서 이용한다면, 해당 부분을 체크해야 합니다.

if(!function_exists('setcookie_samesite')) {
	function setcookie_samesite($name, $value = '', $expires = 0, $path = '', $domain = '', $secure = false, $httponly = false, $samesite = '')
	{
		if(is_array($expires)) {
			$e = $expires;
			foreach(['expires', 'path', 'domain', 'secure', 'httponly', 'samesite'] as $key) {
				if(isset($e[$key])) $$key = $e[$key];
			}
		}
		if (preg_match('~[=,; \t\r\n\x0b\x0c]~', $name)) {
			trigger_error('Cookie names cannot contain any of the following \'=,; \t\r\n\013\014\'', E_USER_WARNING);
			return false;
		}
		if (preg_match('~[,; \t\r\n\x0b\x0c]~', $path)) {
			trigger_error('Cookie paths cannot contain any of the following \',; \t\r\n\013\014\'', E_USER_WARNING);
			return false;
		}
		if (preg_match('~[,; \t\r\n\x0b\x0c]~', $domain)) {
			trigger_error('Cookie domains cannot contain any of the following \',; \t\r\n\013\014\'', E_USER_WARNING);
			return false;
		}
		$values = [];
		if (empty($value)) {
			$values[] = $name . '=delete';
			$values[] = 'expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0';
		} else {
			$values[] = $name . '=' . urlencode($value);
			if ($expires != 0) {
				$values[] = 'expires=' . substr(gmdate('r', $expires), 0, -5) . 'GMT';
				$values[] = 'Max-Age=' . ($expires - time());
			}
		}
		if ($path) $values[] = 'path=' . $path;
		if ($domain) $values[] = 'domain=' . $domain;
		if ($secure) $values[] = 'secure';
		if ($httponly) $values[] = 'HttpOnly';
		if ($samesite) $values[] = 'SameSite=' . $samesite;
		header('Set-Cookie: ' . implode('; ', $values), false);
		return true;
	}
}

form submit redirect

타도메인에서 넘어온 후 사이트 내에서 페이지 이동이 일어나면 쿠키가 살아나는점을 이용하는 임시방편입니다.

pg 에서 넘어오는 return url 같은 곳에 임시로 사용 가능합니다.

class XenoPostToForm
{
	public static function check() {
		return !isset($_COOKIE['PHPSESSID']) && count($_POST) && isset($_SERVER['HTTP_REFERER']) && !preg_match('~^https://'.preg_quote($_SERVER['HTTP_HOST'], '~').'/~', $_SERVER['HTTP_REFERER']);
	}

	public static function submit($posts) {
		echo '<html><head><meta charset="UTF-8"></head><body>';
		echo '<form id="f" name="f" method="post">';
		echo self::makeInputArray($posts);
		echo '</form>';
		echo '<script>';
				echo 'document.f.submit();';
				echo '</script></body></html>';
		exit;
	}

	public static function makeInputArray($posts) {
		$res = [];
		foreach($posts as $k => $v) {
			$res[] = self::makeInputArray_($k, $v);
		}
		return implode('', $res);
	}

	private static function makeInputArray_($k, $v) {
		if(is_array($v)) {
			$res = [];
			foreach($v as $i => $j) {
				$res[] = self::makeInputArray_($k.'['.htmlspecialchars($i).']', $j);
			}
			return implode('', $res);
		}
		return '<input type="hidden" name="'.$k.'" value="'.htmlspecialchars($v).'" />';
	}
}

if(XenoPostToForm::check()) XenoPostToForm::submit($_POST); // session_start(); 하기 전에