php-annotated-monthly-January-2019

Php_annotated_monthly 이미지

JetBrains에서 제공하는 월간 PHP Annotated Monthly입니다.

이 중에서 몇 가지 제 취향껏 골라 그 안의 내용도 좀 뒤져보고 개발새발 번역해서 소개합니다.


News & Releases

Nikita Popov Joined PhpStorm Team

https://blog.jetbrains.com/phpstorm/2019/01/nikita-popov-joins-phpstorm-team/

Nikita하면 (제 머릿속에) 떠오르는 두 사람 중 하나. 그 니키타가 아닌 Nikita Popov가 PhpStorm 팀에 합류했다는 소식입니다.

그가 어떤 일을 했는지 대충 소개하자면,

Nikita is the author of generators, variadic functions and argument unpacking, AST as an internal structure in PHP, engine exceptions, uniform variable syntax, and many other PHP contributions.
He worked with Dmitry Stogov and Xinchen Hui on NG project, which later became PHP 7.
Nikita is also known for PHP Parser which laid the groundwork for many other tools such as PHPStan, Psalm, BetterReflection, Infection, SuperClosure, and PHP-DI, just to name a few.

PHP 5.6.40

http://php.net/ChangeLog-5.php#5.6.40

PHP5.6이 이미 작년 12월부터 unsupported 되었음에도 불구하고, 심각한 보안 문제가 발생하면 PHP 팀이 업데이트 해줄 수도 있다는 이야기입니다. 정이 많네요, 이 친구들.

심각한 오류가 무었이었는지 한번 봅시다.

http://php.net/ChangeLog-5.php#5.6.40

Heap buffer overflow 문제가 주를 이루고 있네요. Mbstring 쪽 bug fix를 보면,

  • Fixed bug #77370 (Buffer overflow on mb regex functions - fetch_token).
  • Fixed bug #77371 (heap buffer overflow in mb regex functions - compile_string_node).
  • Fixed bug #77381 (heap buffer overflow in multibyte match_at).
  • Fixed bug #77382 (heap buffer overflow due to incorrect length in expand_case_fold_string).
  • Fixed bug #77385 (buffer overflow in fetch_token).
  • Fixed bug #77394 (Buffer overflow in multibyte case folding - unicode).
  • Fixed bug #77418 (Heap overflow in utf32be_mbc_to_code).

5.6.39 버전을 쓰시는 분들은 얼른 업데이트 하시길 빌어요. 아니죠, PHP7.1 이상으로 올리시는 것은 어떨까요?

  • PHP 7.3.1
  • PHP 7.2.14
  • PHP 7.1.26

PHP Internals

RFC: Typed Properties 2.0

https://wiki.php.net/rfc/typed_properties_v2

위에 언급한 Nikita Popov가 언급하길, Typed Properties가 이제 쓸 준비가 되었고 master에 머지됐다고 합니다.

RFC에 올라온 기존 방식의 코드 예시를 보면, return type을 고정하기 위해선 함수 선언부에 return type을 지정해야 했고, IDE에서 자동 완성을 지원하기 위해 아래와 같이 property에 type hint를 넣어야 했습니다.

class User {
/** @var int $id */
private $id;
/** @var string $name */
private $name;

public function __construct(int $id, string $name) {
$this->id = $id;
$this->name = $name;
}

public function getId(): int {
return $this->id;
}
public function setId(int $id): void {
$this->id = $id;
}

...
}

이를 좀 더 간략하고 편하게 작성할 수 있게 됐습니다. default value, nullable 선언도 가능합니다. (var 선언도 아직 지원하는 군요??)

class Example {
// All types with the exception of "void" and "callable" are supported
public int $scalarType;
protected ClassName $classType;
private ?ClassName $nullableClassType;

// Types are also legal on static properties
public static iterable $staticProp;

// Types can also be used with the "var" notation
var bool $flag;

// Typed properties may have default values (more below)
public string $str = "foo";
public ?string $nullableStr = null;

// The type applies to all properties in one declaration
public float $x, $y;
// equivalent to:
public float $x;
public float $y;
}

RFC에 다양한 예시가 포함되어 있으니 확인하시길 바랍니다.

이 RFC에 대해 Rasmus Schultz란 사람이 PHP Typed Properties: Think Twice.란 글을 썼습니다. 이 분 좀 트위터를 뒤져보니 조금 보수적인 관점으로 최근의 PHP의 방향에 불만을 갖고 계신 것 같습니다. 이런 의견도 무시하지 말아야겠죠. 이 글에선 기존에 PHP를 사용하는 관점에서 typed property를 사용하면 어떤 단점이 있는지 알고 써야 한다는 입장인데, OOP를 지향하는 프로그래머들에게 많은 비판을 받기도 합니다. 저런 예시는 나쁜 코드일 뿐이라고 말이죠. 저도 딱히 Rasmus의 의견에는 동조하기 어렵습니다.

또한 트위터에서 Bob Weinand 사람이 올린 글에서 소개된 재미있는 코드도 소개가 됐는데요.

<?php declare(strict_types = 1);

$a = new class { public ?string $v; };
$typedVariable = &$a->v;

$typedVariable = 'foo'; // Ok
$typedVariable = 123; // TypeError

이렇게 꼼수를 쓰면 PHP에서도 typed 변수를 쓸 수 있게 됩니다. 이 아이디어를 응용한 azjezz/typed란 라이브러리도 나왔다는 군요.

[PHP] FFI

https://wiki.php.net/rfc/ffi

지난 12월의 글에서 언급된 스펙인데요. 보안이나 안정성에 여전히 의문이 들지만 이 제안은 결국 통과됐다는 이야기입니다. 다음 메이저 업데이트에 포함될 가능성이 높습니다.

Tools, Symfony, Laravel, Yii, Async PHP

흥미로운 소식이 많습니다…만 필요하신 분들이 알아서 보시기를…

Security

보안 관련 소식입니다.

Exploit PHP Remotely – WAF Rule & Filter Bypass

https://www.secjuice.com/php-rce-bypass-filters-sanitization-waf/

PHP에서 필터, 입력값 검증, WAF(Web Application Firewall) 규칙을 우회하여 원격 코드 실행을 악용하는 방법을 보여줍니다. 저자는 CloudFlare를 예시로 들었는데, 특별히 문제가 있다기 보다는 쉽고 널리 알려졌기 때문이고, 다른 WAF도 마찬가지라고 합니다.

이 문서에서는 system 함수를 파라미터로 넘겨서 실행하는 악질적인 코드를 만들어 실행하는 과정을 보여줍니다.

<?php

if (preg_match('/system|exec|passthru/', $_GET['code'])) { // Filter
echo 'invalid syntax';
} else {
eval($_GET(['code']));
}

파라미터로 ...?code=system("cat /etc/passwd"); 이런 코드를 넘겨 실행할 수 있게 시도하고,
이걸 막는 코드(preg_match문 참고)를 추가하고,
이를 다시 우회하는 코드를 만들어가고 있습니다.

WAF 회피 기술에 대한 연재글에서 더 확인해보세요.

Find bypass disable_functions automatically

https://x-c3ll.github.io//posts/find-bypass-disable_functions/

취약점이 발견된 함수 실행으로 OS 명령어를 실행하기 위해, disable_functions를 우회할 수 있는 함수를 자동으로 찾는 방법을 보여줍니다. (스스로 이건 좀 순진한 접근 방식이라고는 합니다만)

찾는 방법은 이렇습니다.

  • 모든 PHP 함수의 파라미터를 추출합니다(PHP.net에 공개되지 않은 것도 있으므로)
  • 올바른 파라미터로 함수를 실행하고 trace합니다
  • trace에서 execve와 같은 잠재적으로 위험한 함수를 찾습니다

guardrailsio/awesome-php-security

https://github.com/guardrailsio/awesome-php-security

PHP 보안에 관련한 자료를 모아둔 awesome 시리즈입니다.

ollyxar/php-malware-detector

정규식으로 문제가 되는 구문을 간단히 찾아볼 수 있게 구현한 라이브러리입니다.

public static $suspicious = [
'non-printable' => '/(function|return|base64_decode).{,256}[^\x09-\x0d\x20-\x7E]{3}/',
'evaluation' => '/\b(eval|create_function|system|assert|passthru|proc_open|(pcntl_|shell_)?exec|call_user_func(_array)?)\b\s*(\/\*(.*)\*\/)?\s*\(/',
'networking' => '/\b(fputs|file_get_contents|fsockopen|curl_exec|curl_multi_exec)\b\s*\(/',
'double-variable' => '/\${?\$[0-9a-zA-z]+/',
'var-as-function' => '/\$_(GET|POST|COOKIE|REQUEST|SERVER)\s*\[[^\]]+\]\s*\(/',
'callback' => '/\b(eval|assert|passthru|exec|include|system|pcntl_exec|shell_exec|base64_decode|`|array_map|ob_start|call_user_func(_array)?)\s*\(\s*(base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))/',
'callback-2' => '/\b(array_(diff|intersect)_u(key|assoc)|array_(udiff|filter|reduce|walk(_recursive)?)|assert_options|uasort|uksort|usort|preg_replace_callback|iterator_apply)\s*\(\s*.*[,]+(base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))/',
'preg_replace-e' => '/\b(preg_replace)\s*\(\s*((("|\').*(\/e)("|\'))|base64_decode|php:\/\/input|str_rot13|gz(inflate|uncompress)|getenv|pack|\\\\?\$_(GET|REQUEST|POST|COOKIE|SERVER))/',
'obfuscation' => '/((\\\\x[0-9a-zA-z]{2,3}){3})|((chr\([\d]+\)\s?\.\s?){5})|((\$|"|\')[0-9a-zA-z]+(\'|")?+\s?\.\s?){4}|(\bexplode\b\s*\(\s*\bchr\b\()/',
'php-params' => '/ini_(get|set|restore)\s*\(\s*[\'"](safe_mode|open_basedir|disable_(function|classe)s|safe_mode_exec_dir|safe_mode_include_dir|register_globals|allow_url_include)/',
'include-images' => '/include\s*(\(|"|\')\s*[^\.]+\.(png|jpg|gif|bmp|ico)/',
'register-function' => '/register_[a-z]+_function\s*\(\s*[\'"]\s*(eval|assert|passthru|exec|include|system|shell_exec|`)/',
'extraction' => '/\bextract\b\s*\(\s*\$_(GET|REQUEST|POST|COOKIE|SERVER|)/'
];

기타 읽을 만한 글

New in PHP 7.4

https://stitcher.io/blog/new-in-php-74

PHP 7.4에 새로 추가된 기능을 간단히 잘 정리해두었습니다.

Tips to speed up PHPUnit tests

https://laravel-news.com/tips-to-speed-up-phpunit-tests

Unit Test를 빠르게 실행할 수 있는 여러가지 TIP이 있습니다.

  • ParaTest라는 병렬 실행 라이브러리 사용
  • 실패한 테스트만 다시 돌리기
  • 느린 테스트를 Group으로 지정하고 따로 돌리기
  • 필터하기
  • Password hash rounds를 bcrypt가 허용하는 최소 횟수인 4까지 줄여놓기
  • In-memory database 사용
  • Xdebug 비활성화 하기
  • 가장 좋은 것은, 느린 테스트를 고치기!

Tips on how to get Faster Code Coverage

https://thephp.cc/news/2019/01/faster-code-coverage

더 빠르게 코드 커버리지를 구하는 방법

Writing serverless Hello World in PHP on AWS Lambda using serverless.com.

https://akrabat.com/serverless-php-on-aws-lambda/

Sergey Zhuk: How to speedup code reviews

더 빠르게 코드리뷰를 하는 방법입니다. PR을 올리는 방법이 중요하다는데요.

아래의 카테고리로 PR할 것을 구분해서 보내고

  • Features - functional changes.
  • Structure refactoring - changes in classes, interfaces, moving methods between classes.
  • Simple refactoring - may be done by your IDE, e.g. extracting variables/methods/constants, simplifying conditionals).
  • Renaming and moving classes - reorganizing namespaces.
  • Removing unused (dead) code.
  • Code style fixes - e.g. using autowiring, removing redundant doc-blocks.
  • Formatting fixes.

각 카테고리 별로 코드리뷰 전략을 다르게 가져갑니다.

  • Feature changes: fulfillment of business requirements and design.
  • Structure refactoring: backward compatibility and design improvements.
  • Simple refactoring: readability improvements. Because these changes are mostly may be done by IDE.
  • Renaming/removing classes: whether the namespaces structure has become better.
  • Removing unused code: backward compatibility.
  • Code style fixes: in most cases instant merge.
  • Formatting fixes in most cases instant merge.

PR을 보내기 전에 명심할 것은,

  • 당신의 코드를 읽기 좋게 최적화 하십시오. 코드는 쓰이는 것보다 더 많이 읽힙니다
  • 변경 사항의 컨텍스트 정보를 제공하기 위한 설명을 적어야 합니다
  • Request 보내기 전에 당신의 코드를 리뷰하세요. 당신의 코드가 아닌 것처럼 리뷰하세요. 간혹 당신이 놓친 걸 찾을 수 있을 겁니다. 이 과정이 PR 거절/수정 주기를 줄여줄 것입니다.

Nikola Poša: Some naming antipatterns

https://blog.nikolaposa.in.rs/2019/01/06/better-naming-convention/

네이밍 안티 패턴을 다룹니다.

tips on naming이란 글도 함께 읽어보세요.

Marcel Pociot: Semantic versioning explained

https://marcelpociot.de/blog/semantic-versioning-explained

시맨틱 버저닝을 설명합니다.

Marcel Pociot: Polishing your code

https://marcelpociot.de/blog/polish-your-code

여러분의 코드를 갈고 닦으세요!

John Mackenzie: My Modern PHP Development Setup

https://johnmackenzie.co.uk/post/my-modern-php-development-setup/

저자의 Modern PHP 개발 환경 설정이라고 합니다.

  • Use a Makefile and make good use of it
  • Run everything in Docker
  • Use CS Fixer To Adhere To Coding Standards
  • Use PHP Stan
  • Syncronise your team IDE’s with .editorconfig

phpapprentice.com

https://phpapprentice.com/

이제 막 PHP를 배우는 초심자를 위한 온라인 도서입니다. 마스코트가 아기 코끼리인가 봅니다.