GETリクエストのボディはPHPで取得できるの?試してみた

定期的に業務で不正なリクエストがないか見ています。 その調査でふと、PHPはGETリクエストに含まれるボディの値が取得できてしまうのか、 取得可能であればそこも気にしなければなぁ、ということで調べてみました。

GETリクエストのボディ?

GETは指定したURIの情報を取得するためのもので、通常ボディは含めませんが、やろうと思えば指定は可能です。

A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

https://tools.ietf.org/html/rfc7231#section-4.3.1

RFCでも、特にダメですとは言われてはいない。 一部、リクエストを拒否する既存の実装があるかもね。くらい

環境

  • サーバ: PHP7.3 + PHP-FPM + nginx (docker)
  • リクエストの送信: Burp Suite

まずは、スーパーグローバル変数 $GET $POST $_REQUEST で確認

PHPファイルを用意する

以下のように、$GET, $POST, $_REQUESTで受け取れるPHPファイルを用意しました。

<?php
echo "\$_GET query_param: " . $_GET["query_param"] . "¥n";
echo "\$_GET body_param: " . $_GET["body_param"] . "¥n";
echo "\$_POST query_param: " . $_POST["query_param"] . "¥n";
echo "\$_POST body_param: " . $_POST["body_param"] . "¥n";
echo "\$_REQUEST query_param: " . $_REQUEST["query_param"] . "¥n";
echo "\$_REQUEST body_param: " . $_REQUEST["body_param"] . "¥n";

リクエストを準備する

POSTリクエス

POST /index.php?query_param=hoge HTTP/1.1
Host: www.example.com:8888
Content-Type: application/x-www-form-urlencoded
Origin: http://www.example.com:8888
Content-Length: 9

body_param=huga

GETリクエスト(body付き)

GET /index.php?query_param=hoge HTTP/1.1
Host: www.example.com:8888
Content-Type: application/x-www-form-urlencoded
Origin: http://www.example.com:8888
Content-Length: 9

body_param=huga

まずは、POSTリクエストの結果

HTTP/1.1 200 OK
Server: nginx/1.15.12
Date: Sun, 14 Jun 2020 03:02:41 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/7.3.11
Content-Length: 143

$_GET query_param: hoge
$_GET body_param:
$_POST query_param:
$_POST body_param: huga
$_REQUEST query_param: hoge
$_REQUEST body_param: huga
  • $_GETでリクエストBobyの値は取れませんでした。
  • $_POSTでクエリパラメータの値は取れませんでした。

まずは、予想通り

続いて今回確認したかった、GETリクエストの結果

HTTP/1.1 200 OK
Server: nginx/1.15.12
Date: Sun, 14 Jun 2020 03:07:42 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/7.3.11
Content-Length: 135

$_GET query_param: hoge
$_GET body_param:
$_POST query_param:
$_POST body_param:
$_REQUEST query_param: hoge
$_REQUEST body_param:

このようになりました。 GETメソッドが来た場合は、$GET, $POST, $_REQUESTではリクエストbodyの値は取得できないことがわかりました。

php://input を使ってみる

php://input は読み込み専用のストリームで、 リクエストの body 部から生のデータを読み込むことができます。

https://www.php.net/manual/ja/wrappers.php.php

これを使ってみます。

PHPファイルを用意

<?php

$hoge = file_get_contents('php://input');
echo $hoge;

リクエストを準備する

先ほどと同じやつです。

GET /index.php?query_param=hoge HTTP/1.1
Host: www.example.com:8888
Content-Type: application/x-www-form-urlencoded
Origin: http://www.example.com:8888
Content-Length: 15

body_param=huga

結果

HTTP/1.1 200 OK
Server: nginx/1.15.12
Date: Sun, 14 Jun 2020 03:14:09 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/7.3.11
Content-Length: 15

body_param=huga

リクエストボディの値が、そのままの形で取得できました! (POST, PUT, PATCH, DELETEでも確認してみましたが、結果は同じでした。)

まとめ

  • $GET $POST $_REQUEST では、GETリクエストのボディに含まれる値は取得できない
  • php://input を使うと、GETリクエストのボディに含まれる値はそのままの形で取得できる
    • おそらく、HTTPのメソッド関係なく取得できるものと思われます

ということがわかりました〜。

以上