PHP 객체의 복제 특성

today 2018-11-04 face Posted by appkr turned_in Learn & Think forum 0

PHP 객체를 다른 변수에 할당(대입)하면, 객체 자체가 메모리 복제되어 새로운 변수에 할당되는 것이 아니라, 원본 객체가 담긴 메모리 번지만 참조됩니다(Like Pointer in C language).

  • $foo$foo2Foo 클래스의 인스턴스가 담긴, 같은 메모리 번지를 가리킵니다.
  • $foo3$foo 인스턴스로 부터 복제했으므로, 복제 시점의 $foo의 상태를 그대로 가져오지만, 서로 다른 객체입니다.
  • $foo4Foo 클래스의 새로운 인스턴스이므로, 당연히 $foo, $foo2, $foo3와 다른 객체입니다.

SQL Injection 방어

today 2018-10-20 face Posted by appkr turned_in Learn & Think forum 0

Exploit of a mom 그림출처: Exploits of a Mom

Cut#1 (학교) 여기 학교인데요. 전화 드린 이유는, 학교에 컴퓨터 문제가 좀 있어서요.
Cut#2 (엄마) 우리 애가 사고쳤나요?
     (학교) 예... 일종의...
Cut#3 (학교) 그런데, 정말로 아들 이름을 "Robert'); DROP TABLE students;--" 로 지으신게 맞나요?
      (엄마) 예 맞아요. 그래서 집에서는 우리 애를 "리틀 바비 테이블"이라고 불러요.
Cut#4 (학교) ;;; 기뻐하실 지 모르겠지만, 학생 테이블의 레코드 전체를 잃어 버렸어요.
      (엄마) 적어도 이번 사건 덕분에 DB에 입력할 값을 잘 필터링 해야 한다는 사실 정도는 배우셨겠군요~
• • •

프레임웍을 쓰면 쉽게 안전성을 확보할 수 있는데요. 그럼에도 불구하고, 아래 코드의 queryStoresByParams() 함수처럼 Raw 쿼리를 써야 할 때가 있죠. 이 때 SQL Injection을 당하지 않도록 주의해야 합니다.

<?php // 회사 코드에서 일부 발췌

class StoreRetriever
{
    // 라라벨 프레임웍이 제공하는 엘로퀀트 ORM과 쿼리 빌더를 이용하는 경우
    // PDO와 Prepared Statement를 이용하므로 SQL Injection은 자동 방어됨
    public function retrieveStoresByParams(StoreSearchParamDto $dto, array $eagerLoads = [])
    {
        $builder = !empty($eagerLoads)
            ? Store::query()->with($eagerLoads)->select('stores.*')
            : Store::query()->select('stores.*');
        $this->applySearchParams($builder, $dto);
        $this->applyOrderBy($builder, $dto);

        return $builder->paginate($dto->getSize(), ['*'], 'page', $dto->getPage());
    }

    // Raw 쿼리를 쓸 때 사용자로부터 받은 문자열을 직접 쿼리에 끼워 넣으면 SQL Injection에 무방비 상태가 됨
    // e.g. $query[] = "and stores.created_at >= {$from}";
    //
    // 여기서 Raw 쿼리를 왜 썼는가? "성능". 소위 말하는 "Query Model"
    // PHP7 with Xdebug on Docker, 테이블 조인 5개 & 12,000 레코드 조회시 API 응답 시간
    // 	 - retrieveStoresByParams(): around N sec 
    //   - queryStoresByParams(): under N/10 sec
    public function queryStoresByParams(StoreSearchParamDto $dto, array $columns = ['stores.*'])
    {
        $columnString = implode(',', $columns);
        $query[] = "select {$columnString} from stores";
        $bindings = [];

        $query[] = "where 1 = 1";

        $from = $dto->getFrom();
        if ($from !== null) {
            $query[] = "and stores.created_at >= :from";
            $bindings['from'] = $from;
        }

        // ...

        return \DB::select(implode(' ', $query), $bindings);
    }

    private function applySearchParams(Builder $builder, StoreSearchParamDto $dto)
    {
        $from = $dto->getFrom();
        if ($from !== null) {
            $builder->where('stores.created_at', '>=', $from);
        }
    
        // ...
    }
    
    private function applyOrderBy(Builder $builder, StoreSearchParamDto $dto)
    {
        foreach ($dto->getOrderBy() as $order) {
            $builder->orderBy($order['sortKey'], $order['sortDirection']);
        }
    }
}

Linux "top" cheatsheet

today 2018-10-13 face Posted by appkr turned_in Cheatsheet forum 0

보통 개발용 컴퓨터에서는 htop처럼 좀 더 편리한 프로세스 모니터 도구를 사용하지만, 운영 서버에는 서비스에 불필요한 바이너리는 설치하지 않는게 좋죠. 해서, 특히 서비스 장애와 같이 급박한 상황에 운영 서버에 SSH in 해서 top을 이용하려면 바보가 된 느낌을 종종 받습니다.

이 포스트에서는 바닐라 리눅스 환경에서 사용 빈도가 높은 top 사용법만 정리해봤습니다. 우분투 16.04를 사용했는데요, CentOS도 같을 거라 생각합니다 CentOS는 조금 다릅니다, 댓글로 남깁니다.

$ cat /etc/issue
# Ubuntu 16.04.3 LTS \n \l

$ top

전체 요약

  • Help
    • h: 도움말
  • Display
    • Z: 칼라 설정
    • t: CPU 통계 뷰 토글
    • m: 메모리 통계 뷰 토글
    • 0: Zero value(0) 뷰 토글
    • b: 하이라이트 토글
    • x: 활성 필드 표시 토글
    • y: 활성 프로세스 토글
    • c: 커맨드 상세 표시 토글
    • V: 프로세스간 부모/자식 관계 표시
  • Action
    • W: 설정 저장
    • k: 프로세스 중단하기
  • Navigation & Sorting (default: desc)
    • <, >: 활성 필드 네비게이션
    • N: PID 필드를 활성으로 변경
    • M: %MEM 필드를 활성으로 변경
    • P: %CPU 필드를 활성으로 변경
    • T: TIME+ 필드를 활성으로 변경
  • Search & Filter
    • L: 검색
    • o: 필터, e.g. COMMAND=apache, !COMMAND=apache, %MEM>0.1, ..
    • u: 사용자 이름으로 필터
    • ctrl + O: 현재 적용된 필터
    • =: 필터 해제

라라벨 ApplicationContext 컨테이너 구현

today 2018-07-09 face Posted by appkr turned_in Work & Play forum 0

사전에서 context(컨텍스트)를 찾아보면 어떤 사건이 발생했을 때의 주변 상황 정도로 설명하고 있습니다. 컴퓨터 소프트웨어에서도 컨텍스트 스위칭, 로그 컨텍스트, 애플리케이션 컨텍스트 등 컨텍스트라는 단어를 많이 사용합니다.

라라벨은 IoC를 위한 Service Container1가 있으며, 여기에 어떤 데이터 타입이든 바인딩할 수 있고, 애플리케이션 실행 중에 아래처럼 꺼내 쓸 수 있습니다.

$container = app();
$container->bind('foo', function () {
	return 'bar';
});
$container->make('foo'); // 'bar'

그런데, 아래와 같은 문제가 있죠2. 작은 애플리케이션에서는 이렇게 쓴다고 전혀 문제없지만, 애플리케이션이 커지면 결국 개발자에게 큰 부담으로 다가옵니다.

Strong Dependency

이번 포스트에서는 라라벨 애플리케이션에서 애플리케이션 실행 시점의 주변 상황을 담아 놓고 필요할 때 꺼내 쓰기 위한 데이터 컨테이너를 구현했던 썰을 풀어보려합니다. ApplicationContext라 이름 지었고 Java 언어의 ThreadLocal3과 비슷한 역할을 한다고 보면 됩니다. 주로 아래와 같은 상황에 유용하게 사용되기를 바라며 만들었어요.

  • 프레임워크 영역(=main)의 데이터를 코어 영역(=app)으로 전달할 때
  • 로깅 및 감사
  • 프로세스간 컨텍스트 릴레이 등등 (e.g. Api -> Queue, App -> External, …)
keyboard_arrow_up