blog.stackframe.dev

[PHP] GET, POST로 배열 전달

PHP에서는 GET이나 POST 인자에 문자열이 아닌 배열이 들어올 수 있다. 매개변수 뒷부분에 []를 사용하면 해당 인자는 배열로 만들어지게 된다. 간단하게 아래의 예시를 보자:

https://example.org/?test[]=arg1&test[]=arg2

PHP를 조금이라도 할 줄 아는 사람이라면 $arr[] = 'item'; 같이 배열 뒤에 값을 추가하는 방법을 알 것이다. 위의 GET 인자도 동일하게 작용한다. text 매개변수가 arg1과 arg2 문자열이 들어있는 배열을 가진다:

array(1) {
  ["test"]=>
  array(2) {
    [0]=>
    string(4) "arg1"
    [1]=>
    string(4) "arg2"
  }
}

단순히 0부터 시작하는 배열만 만들 수 있는게 아니다. 대괄호 안에 문자열이 들어간다면 해당 문자열을 키로 사용하는 배열이 만들어진다:

https://example.org/?test[stack]=arg1&test[frame]=arg2

array(1) {
  ["test"]=>
  array(2) {
    ["stack"]=>
    string(4) "arg1"
    ["frame"]=>
    string(4) "arg2"
  }
}

심지어 다차원 배열까지 생성이 가능하다:

https://example.org/?test[stack][frame][]=arg1&test[frame][buffer]=arg2&test[stack][frame][]=arg3

array(1) {
  ["test"]=>
  array(2) {
    ["stack"]=>
    array(1) {
      ["frame"]=>
      array(2) {
        [0]=>
        string(4) "arg1"
        [1]=>
        string(4) "arg3"
      }
    }
    ["frame"]=>
    array(1) {
      ["buffer"]=>
      string(4) "arg2"
    }
  }
}

HTML의

태그에서도 태그의 name 속성에 위와 동일한 규칙의 매개변수를 사용하면 POST에서도 동일하게 작동한다:

<form method="POST" action="/">
    <input name="number[]" type="number">
    <input name="number[]" type="number">
</form>

이것은 배열로 값을 넘길 수 있기 때문에 매우 편리하지만, 그와 동시에 보안 문제가 발생할 가능성도 높아진다. 데이터가 배열로 들어올 수 있다는 의미이기 때문이다. 만약 배열로 값이 들어올 것을 예상하지 못하고 검증없이 사용한다면 어떻게될까? 최소 에러 발생에 최악의 경우 보안이 뚫리는 사고가 발생할 수 있다. 이걸 이용하여 PHP 5.3 때 strcmp() 함수 인자로 배열이 들어오면 NULL을 반환하여 0과 느슨한 비교를 하면 참이 되어 풀리는 CTF 문제가 있었다:

if(strcmp($_POST['pass'], $pass) == 0)

PHP 8부터 타입이 엄격해져서 내부 함수 인자에 정의된 타입과 다른 값이 들어온다면 warning이 아닌 TypeError 예외를 던지기에 위의 예시가 뚫리지는 않지만 에러 로그에 계속 기록될 것이고, 다른 곳에서 취약점이 발생할 가능성이 크다. 그러므로 언제나 사용자가 입력한 데이터를 사용할 때는 검증하고 또 검증하자. 나는 기본적으로 isset()과 함께 is_string(), is_int() 같은 함수를 사용하여 사용자 입력을 필터링한다.

댓글