NelmioSecurityBundle icon indicating copy to clipboard operation
NelmioSecurityBundle copied to clipboard

Service dependency injection for controller action: Getting CSP nonce in controller

Open Adambean opened this issue 1 year ago • 10 comments

Hello,

Does this bundle expose any services for use with a controller action via dependency injection? debug:autowiring didn't appear to reveal any.

The purpose for this is I need to provide a ReCAPTCHA bundle form instance with a nonce from this CSP provider.

Adambean avatar May 18 '24 19:05 Adambean

You can use ContentSecurityPolicyListener::getNonce('script') to get a script nonce that will be included in the generated CSP header. You can find the listener in the container as nelmio_security.csp_listener.

martijnc avatar May 31 '24 21:05 martijnc

Thanks for your reply @martijnc.

Is the "nelmio_security.csp_listener" service available to dependency injection? I don't see anything from "nelmio_security" available when searching for it with debug:autowiring --all, and it can't be invoked from the container via get(). (ContentSecurityPolicyListener::getNonce() can't be called statically.)

Adambean avatar Jun 01 '24 13:06 Adambean

The nelmio_security.csp_listener service isn't marked public so get()-ing it from the container may not work because it's likely being inlined. But you should be able to inject it into your service with DI.

Can you find it with debug:container nelmio_security.csp_listener?

martijnc avatar Jun 01 '24 19:06 martijnc

I see 26 services containing "nelmio_security" via debug:container, including "nelmio_security.csp_listener" as "Nelmio\SecurityBundle\EventListener\ContentSecurityPolicyListener". Unfortunately none of these are available for auto-wiring (not present in debug:autowiring), therefore they can't be used for DI.

I did attempt to declare "Nelmio\SecurityBundle\EventListener\ContentSecurityPolicyListener" in my services configuration to be available for auto-wiring however the service doesn't appear to be compatible with that:

Cannot autowire service "Nelmio\SecurityBundle\EventListener\ContentSecurityPolicyListener": argument "$report" of method "__construct()" references class "Nelmio\SecurityBundle\ContentSecurityPolicy\DirectiveSet" but no such service exists.

I think perhaps I've missed an approach to DI that predates auto-wiring? I went from the Symfony 2/3 days where using $this->get() or $this->container->get() up to Symfony 6/7 whereby auto-wiring was the thing to do. I'll read the service container documentation to see what I've missed.

Adambean avatar Jun 02 '24 09:06 Adambean

Because autowiring for the service does not work, you need to wire it manually via the service configuration. See "Explicitly Configuring Services and Arguments "on the documentation page you linked. You don't need to redefine the ContentSecurityPolicyListener service, you need to reference the existing one by its ID.

If your service is App\Security\ServiceThatNeedsTheNonce with the listener as the first constructor argument, the configuration for it would look something like this:

services:
    App\Security\ServiceThatNeedsTheNonce:
        arguments:
            - '@nelmio_security.csp_listener'

martijnc avatar Jun 02 '24 10:06 martijnc

That seemed to work. I had to also include the other arguments that I had previously given with auto-wiring, but the CSP listener now goes into my class as expected. Thanks for the workaround.

Is there a particular reason why the CSP listener class would not support auto-wiring, or is it a pending improvement?

Adambean avatar Jun 02 '24 17:06 Adambean

It's just old code predating autowiring.. These days I guess there would not be service names at all and rather just use classes with autowiring, but tbh I am not 100% sure about the latest bundle best practices.

Anyway could be fixed for sure, but it's work :)

Seldaek avatar Jul 01 '24 15:07 Seldaek

You can use ContentSecurityPolicyListener::getNonce('script') to get a script nonce that will be included in the generated CSP header. You can find the listener in the container as nelmio_security.csp_listener.

No, it gives me: An exception has been thrown during the rendering of a template ("Non-static method Nelmio\SecurityBundle\EventListener\ContentSecurityPolicyListener::getNonce() cannot be called statically").

This works:

services:
    App\Security\ServiceThatNeedsTheNonce:
        arguments:
            - '@nelmio_security.csp_listener'

And I suggest to add it to the docs: https://symfony.com/bundles/NelmioSecurityBundle/current/index.html#nonce-for-inline-script-handling

DavidPetrasek avatar Feb 15 '25 13:02 DavidPetrasek

@DavidPetrasek feel free to open a PR with any improvements you have, the docs are here https://github.com/nelmio/NelmioSecurityBundle/edit/master/src/Resources/doc/index.rst

Seldaek avatar Feb 24 '25 15:02 Seldaek

@Seldaek https://github.com/nelmio/NelmioSecurityBundle/pull/367

DavidPetrasek avatar Mar 03 '25 09:03 DavidPetrasek