Confluence 에 심각한 보안 취약점이 발견되었으니 사용자분들은 업그레이드 하세요.!
 
1
0
-1


안녕하세요.


현재 특정 서버에 접근할 수 있는 Client API 를 사용해서 인증하고, 토큰을 획득한 이후에 서버와 통신해서 데이터를 읽고 쓰는 부분을 작업하다 원하는 결과가 안 나와 문의드리고자 합니다.


사용하고 있는 API 가 $client->authenticate() 를 진행하면 true/false 로만 리턴되며, 인증이 완료된 $client 객체를 명령어셋이 있는 new Download 객체에 주입하지 않으면 작동하지 않도록 되어 있습니다.


1. DownloadService 에서 아래와 같이 만들었습니다. 이렇게 할 경우 문제점은 액션마다 인증을 수행하기 때문에 서버에 잦은 인증 시도가 발생합니다.

namespace App\Services;

class DownloadService
{
	public function __construct()
	{
		$client = new Client('Download'); // Download 세션
		$client->authenticate('id', 'passwd'); // 인증
		$download = new Download($client); // $client 객체 주입
	}
}


2. 서버에 잦은 인증 시도를 줄이고자 singleton 바인딩을 사용하면 되지 않을까라는 생각에 다음과 같이 작업했습니다.


최초 Download 인스턴스 생성/인증까지 진행해서 singleton 으로 사용되는건가라고 생각했으나, 잘못 이해했던 것인지 1번과 똑같은 결과가 나왔습니다.


아마 제가 서비스 컨테이너와 프로바이더에 대한 이해, PHP obejct by reference 에 대한 이해가 부족해서 처음부터 잘못 접근했던 것 같다는 생각이 듭니다.


namespace App\Providers;

class DownloadServiceProvider extends ServiceProvider
{
	public function register()
	{
		$this->app->singleton(Download::class, function ($app) {
			$client = new Client('Download');
			$client->authenticate('id', 'passwd');
			return new Download($client);
		});
	}
}
namespace App\Services;

class DownloadService
{
	private $download;
	public function __construct(Download $download)
	{
		$this->download = $download;
	}
}


두 가지 방법으로 진행했으나 해결이 되지 않아 어떤 방향으로 진행하면 해결이 가능할지에 대한 의견이나 검색 키워드에 대한 도움을 구하고자 합니다.

    CommentAdd your comment...

    1 answer

    1.  
      1
      0
      -1
      1. stateless 토큰 인증 기능을 제공하지 않는 (이상한;;) API 인듯합니다. 토큰 인증을 이용하는 경우 대략적인 Pseudo는 맨 아래에 있습니다.

      2. 토큰을 제공하지 않는 대신 Cookie를 이용한 Stateful 인증을 사용하는 API라면, authenticate() API 응답으로 받은 Set-Cookie 헤더 값을, 쿠키에 지정된 expires 시각까지 캐시에 저장해두고, 서비스 프로바이더에서 조립하여 주입하는 HttpClient의 Cookie 헤더를 셋팅해 주는 방법이 있을 듯 합니다.

      3. PHP 프로세스는 HTTP 요청을 받는 순간 시작했다가 응답을 주면 죽는 라이프 싸이클을 가지고 있으므로, 싱글톤이 딱 이 시간동안만 의미가 있습니다. 한 요청 처리 라이프 싸이클 내에 조립된 객체가 여러 번 사용되지 않는다면, 싱글톤이나 매번 객체를 생성하는거나 큰 차이가 없습니다.

      4. 반면, PHP에서도 웹 요청 처리가 아니라, 무한 루프를 도는 워커라면 싱글톤이 의미가 있습니다.


      class FooApiServiceProvider extends ServiceProvider
      {
          public function register()
          {
              $this->bindTokenProvider();
              $this->bindFooApi();
          }
      
          private function bindTokenProvider()
          {
              $this->app->bind(TokenProvider::class, function (Application $app) {
                  return new TokenProvider(설정 또는 환경변수에서 읽은 UserName, Password);
              });
          }
      
          private function bindFooApi()
          {
              $this->app->bind(FooApi::class, function (Application $app) {
                  $tokenProvider = $app->make(TokenProvider::class);
                  $httpClient = new GuzzleClient([
                      'base_path' => 설정 또는 환경변수에서 읽은 호스트,
                      'headers' => [
                          // 앱 부트할 때마다 토큰이 포함된 객체를 조립하지 않아도 된다면,
                          // $tokenProvider를 FooApi에 주입하여, 
                          // FooApi가 불릴 때만 토큰을 얻도록 수정
                          'Authorization' => "bearer {$tokenProvider->getToken()}"
                      ],
                  ]);
      
                  return new FooApi($httpClient);
              });
          }
      }
      
      class TokenProvider
      {
          const CACHE_KEY = 'fooapi.accesstoken';
          const CACHE_TTL = 120;
      
          private $userName;
          private $password;
      
          public function __construct() {...}
      
          public function getToken()
          {
              if (캐시에 CACHE_KEY가 있으면) {
                  return 캐시에서 읽은 값;
              }
      
              인증 API에 $userName, $password를 제출하고 토큰을 받는다.
              받은 토큰을 CACHE_TTL 동안 캐시에 저장한다.
              return 토큰;
          }
      }
      
      class FooService {
          private $httpClient;
      
          public function __construct() {...}
      
          public function getProtectedResource() {...}
      }



      1. Gusto

        김주원 님, 이전 질문에 이어 친절한 답변 고맙습니다.

        1. 남겨주신 프로시저 참고하여 작업해보도록 하겠습니다.
        2. 글을 올린 이후 서치한 내용에서도 PHP 라이프 사이클이라고 표현하지는 않았지만 모든 REQUEST 에 대해 객체를 조립하고, 해체하는 작업을 한다는 내용을 찾았습니다. 기본적인 PHP 이해가 중요하다는 것을 다시 상기하게 되네요.

        작업 이후에 적용 내용에 대해 다시 말씀드릴 수 있도록 하겠습니다.


        고맙습니다!

      CommentAdd your comment...