Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

\Zend\Http\Request - Uncaught InvalidArgumentException when the request method is not considered valid. #6385

Closed
masom opened this issue Jun 17, 2014 · 9 comments
Assignees
Milestone

Comments

@masom
Copy link

masom commented Jun 17, 2014

Our production site hit this when a third-party scanner hit us.

The request parsing seems to happen during the application init before userland code can interact with the exception.

Is there a way to catch the exception / ignore it?

ZF 2.2.7

Uncaught Exception: Uncaught exception 'Zend\Http\Exception\InvalidArgumentException' with message 'Invalid HTTP method passed' in /mnt/deploy/crated/releases/e791dc9ddef37597f54e2657fbdb95a195beff32/vendor/zendframework/zendframework/library/Zend/Http/Request.php:141


Sample stack trace


…vendor/zendframework/zendframework/library/Zend/Http/PhpEnvironment/

Request.php(240): Zend\Http\Request->setMethod('XGET')
…vendor/zendframework/zendframework/library/Zend/Http/PhpEnvironment/

Request.php(82): Zend\Http\PhpEnvironment\Request->setServer(Object(Zend\Stdlib\Parameters))
…/vendor/zendframework/zendframework/library/Zend/Mvc/Service/

RequestFactory.php(32): Zend\Http\PhpEnvironment\Request->__construct()
[internal function]: Zend\Mvc\Service\RequestFactory->createService(Object(Zend\ServiceManager\ServiceManager), 'request', 'Request')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(905): call_user_func(Array, Object(Zend\ServiceManager\ServiceManager), 'request', 'Request')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(1035): Zend\ServiceManager\ServiceManager->createServiceViaCallback(Array, 'request', 'Request')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(609): Zend\ServiceManager\ServiceManager->createFromFactory('request', 'Request')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(569): Zend\ServiceManager\ServiceManager->doCreate('Request', 'request')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(503): Zend\ServiceManager\ServiceManager->create(Array)
…5a195beff32/vendor/zendframework/zendframework/library/Zend/Mvc/

Application.php(112): Zend\ServiceManager\ServiceManager->get('Request')
…dor/zendframework/zendframework/library/Zend/Mvc/Service/

ApplicationFactory.php(29): Zend\Mvc\Application->__construct(Array, Object(Zend\ServiceManager\ServiceManager))
[internal function]: Zend\Mvc\Service\ApplicationFactory->createService(Object(Zend\ServiceManager\ServiceManager), 'application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(905): call_user_func(Array, Object(Zend\ServiceManager\ServiceManager), 'application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(1035): Zend\ServiceManager\ServiceManager->createServiceViaCallback(Array, 'application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(609): Zend\ServiceManager\ServiceManager->createFromFactory('application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(569): Zend\ServiceManager\ServiceManager->doCreate('Application', 'application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(503): Zend\ServiceManager\ServiceManager->create(Array)
…5a195beff32/vendor/zendframework/zendframework/library/Zend/Mvc/

Application.php(254): Zend\ServiceManager\ServiceManager->get('Application')
…eploy/crated/releases/e791dc9ddef37597f54e2657fbdb95a195beff32/public/

index.php(18): Zend\Mvc\Application::init(Array)
…k/zendframework/library/Zend/ServiceManager/

ServiceManager.php:912 Stack trace:(
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(1035): Zend\ServiceManager\ServiceManager->createServiceViaCallback(Array, 'request', 'Request')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(609): Zend\ServiceManager\ServiceManager->createFromFactory('request', 'Request')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(569): Zend\ServiceManager\ServiceManager->doCreate('Request', 'request')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(503): Zend\ServiceManager\ServiceManager->create(Array)
…5a195beff32/vendor/zendframework/zendframework/library/Zend/Mvc/

Application.php(112): Zend\ServiceManager\ServiceManager->get('Request')
…dor/zendframework/zendframework/library/Zend/Mvc/Service/

ApplicationFactory.php(29): Zend\Mvc\Application->__construct(Array, Object(Zend\ServiceManager\ServiceManager))
[internal function]: Zend\Mvc\Service\ApplicationFactory->createService(Object(Zend\ServiceManager\ServiceManager), 'application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(905): call_user_func(Array, Object(Zend\ServiceManager\ServiceManager), 'application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(1035): Zend\ServiceManager\ServiceManager->createServiceViaCallback(Array, 'application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(609): Zend\ServiceManager\ServiceManager->createFromFactory('application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(569): Zend\ServiceManager\ServiceManager->doCreate('Application', 'application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(503): Zend\ServiceManager\ServiceManager->create(Array)
…5a195beff32/vendor/zendframework/zendframework/library/Zend/Mvc/

Application.php(254): Zend\ServiceManager\ServiceManager->get('Application')
…eploy/crated/releases/e791dc9ddef37597f54e2657fbdb95a195beff32/public/

index.php(18): Zend\Mvc\Application::init(Array)
…k/zendframework/library/Zend/ServiceManager/

ServiceManager.php:912 Stack trace:(
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(1035): Zend\ServiceManager\ServiceManager->createServiceViaCallback(Array, 'application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(609): Zend\ServiceManager\ServiceManager->createFromFactory('application', 'Application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(569): Zend\ServiceManager\ServiceManager->doCreate('Application', 'application')
…ndor/zendframework/zendframework/library/Zend/ServiceManager/

ServiceManager.php(503): Zend\ServiceManager\ServiceManager->create(Array)
…5a195beff32/vendor/zendframework/zendframework/library/Zend/Mvc/

Application.php(254): Zend\ServiceManager\ServiceManager->get('Application')
…eploy/crated/releases/e791dc9ddef37597f54e2657fbdb95a195beff32/public/

index.php(18): Zend\Mvc\Application::init(Array)
{main}
@stefanotorresi
Copy link
Contributor

I'm experiencing this issue too, but in a different way. I've seen an increase of 500 errors in my server logs lately, due to spam bots often sending requests with invalid HTTP methods (e.g. QUIT).

It's not a big problem with display_errors disabled, but it's annoying nonetheless.

It would be nice if it was possible to just return a sane response (i.e. 400 or 405 for HTTP requests) in case a request could not be correctly created.

Maybe short circuiting to MvcEvent::EVENT_FINISH even before the MvcEvent setup?

@masom
Copy link
Author

masom commented Jun 23, 2014

Short circuiting would be great but I'm not sure the Request class should even throw an exception if the HTTP method is not recognized. From the RFC: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

9 Method Definitions

The set of common methods for HTTP/1.1 is defined below.
Although this set can be expanded, additional methods cannot be assumed to share the same semantics for separately extended clients and servers.```

@weierophinney
Copy link
Member

I agree with @masom -- we shouldn't likely raise an exception here by default, as the standard indicates that custom methods may be allowed and/or the spec could be amended to add methods.

That said, I'd love to also have a listener ala @stefanotorresi, registered by default via the "route" event at high priority that would check against the standard set of request methods, and return a 405 response if it falls outside that range. This would short-circuit the most expensive operations by default, but not be so noisy as to create PHP log messages. Configuration would allow opting out of the listener, as well as defining the methods allowed.

Essentially, this might look as follows:

namespace Zend\Mvc;

use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;

class HttpMethodListener extends AbstractListenerAggregate
{
    protected $allowedMethods = array(
        'DELETE',
        'GET',
        'HEAD',
        'OPTIONS',
        'PATCH',
        'POST',
        'PUT',
    );

    protected $enabled = true;

    public function __construct($enabled = true, array $allowedMethods = null)
    {
        $this->enabled = (bool) $enabled;

        if ($this->enabled && is_array($allowedMethods)) {
            $this->allowedMethods = $allowedMethods;
        }
    }

    public function attach(EventManagerInterface $events)
    {
        if (! $this->enabled) {
            return;
        }

        $this->listeners[] = $events->attach(
            MvcEvent::EVENT_ROUTE,
            array($this, 'onRoute'),
            10000
        );
    }

    public function onRoute(MvcEvent $e)
    {
        $request = $e->getRequest();
        if (! $request
            || ! method_exists($request, 'getMethod')
        ) {
            return;
        }

        $method = $request->getMethod();

        if (in_array($method, $this->allowedMethods)) {
            return;
        }

        $response = $e->getResponse();
        $response->setStatusCode(405);
        return $response;
    }
}

With a factory like this:

class HttpMethodListenerFactory
{
    public function __invoke($services)
    {
        $config = $services->get('Config');
        if (! isset($config['http_methods_listener'])) {
            return new HttpMethodListener();
        }

        $config  = $config['http_methods_listener'];
        $enabled = array_key_exists('enabled', $config)
            ? $config['enabled']
            : true;
        $allowedMethods = (isset($config['allowed_methods']) && is_array($config['allowed_methods']))
            ? $config['allowed_methods']
            : null;
        return new HttpMethodListener($enabled, $allowedMethods);
    }
}

@masom
Copy link
Author

masom commented Jun 23, 2014

@weierophinney 👍

@Ocramius
Copy link
Member

Ocramius commented Aug 6, 2014

Handled in #6385

@Ocramius Ocramius closed this as completed Aug 6, 2014
@cordoval
Copy link

this link goes back to the same issue link

@Ocramius
Copy link
Member

this link goes back to the same issue link

What does that mean?

@cordoval
Copy link

Handled in #6385 points back to this same url

@Ocramius
Copy link
Member

Oh, right! Linking #6409 instead.

Thanks for noticing!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants