CSRF issues with Plone 5.1.2.1

The ongoing and growing pain with Plone 5.1, plone.protect and the CSRF are just frustrating and making Plone 5.1 more and more project risk - both for customers and integrators.

I created a Plone 5.1.2.1 site with just two add-ons which provide simple Dexterity types...Plone content was added to the site over plone.restapi.

Now even simple view for the Plone homepage with the document_view causes CSRF issues

2018-05-29 10:42:04 INFO plone.protect   File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/Zope2-2.13.27-py2.7.egg/ZServer/PubCore/ZServerPublisher.py", line 31, in __init__
    response=b)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/Zope2-2.13.27-py2.7.egg/ZPublisher/Publish.py", line 455, in publish_module
    environ, debug, request, response)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/Zope2-2.13.27-py2.7.egg/ZPublisher/Publish.py", line 249, in publish_module_standard
    response = publish(request, module_name, after_list, debug=debug)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/Zope2-2.13.27-py2.7.egg/ZPublisher/Publish.py", line 143, in publish
    notify(PubBeforeCommit(request))

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.event-3.5.2-py2.7.egg/zope/event/__init__.py", line 31, in notify
    subscriber(event)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.component-4.4.1-py2.7.egg/zope/component/event.py", line 27, in dispatch
    component_subscribers(event, None)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.component-4.4.1-py2.7.egg/zope/component/_api.py", line 139, in subscribers
    return sitemanager.subscribers(objects, interface)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.interface-4.4.3-py2.7-linux-x86_64.egg/zope/interface/registry.py", line 442, in subscribers
    return self.adapters.subscribers(objects, provided)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.interface-4.4.3-py2.7-linux-x86_64.egg/zope/interface/adapter.py", line 607, in subscribers
    subscription(*objects)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.transformchain-1.2.2-py2.7.egg/plone/transformchain/zpublisher.py", line 86, in applyTransformOnSuccess
    transformed = applyTransform(event.request)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.transformchain-1.2.2-py2.7.egg/plone/transformchain/zpublisher.py", line 75, in applyTransform
    transformed = transformer(request, result, encoding)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.transformchain-1.2.2-py2.7.egg/plone/transformchain/transformer.py", line 50, in __call__
    newResult = handler.transformIterable(result, encoding)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.protect-3.1.3-py2.7.egg/plone/protect/auto.py", line 186, in transformIterable
    if not self.check():

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.protect-3.1.3-py2.7.egg/plone/protect/auto.py", line 211, in check
    return self._check()

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.protect-3.1.3-py2.7.egg/plone/protect/auto.py", line 283, in _check
    '\n'.join(traceback.format_stack()),

aborting transaction due to no CSRF protection on url http://dev.zopyx.de:5080/dynasupport/news/aggregator/summary_view
2018-05-29 10:42:06 INFO plone.protect   File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/Zope2-2.13.27-py2.7.egg/ZServer/PubCore/ZServerPublisher.py", line 31, in __init__
    response=b)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/Zope2-2.13.27-py2.7.egg/ZPublisher/Publish.py", line 455, in publish_module
    environ, debug, request, response)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/Zope2-2.13.27-py2.7.egg/ZPublisher/Publish.py", line 249, in publish_module_standard
    response = publish(request, module_name, after_list, debug=debug)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/Zope2-2.13.27-py2.7.egg/ZPublisher/Publish.py", line 143, in publish
    notify(PubBeforeCommit(request))

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.event-3.5.2-py2.7.egg/zope/event/__init__.py", line 31, in notify
    subscriber(event)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.component-4.4.1-py2.7.egg/zope/component/event.py", line 27, in dispatch
    component_subscribers(event, None)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.component-4.4.1-py2.7.egg/zope/component/_api.py", line 139, in subscribers
    return sitemanager.subscribers(objects, interface)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.interface-4.4.3-py2.7-linux-x86_64.egg/zope/interface/registry.py", line 442, in subscribers
    return self.adapters.subscribers(objects, provided)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/zope.interface-4.4.3-py2.7-linux-x86_64.egg/zope/interface/adapter.py", line 607, in subscribers
    subscription(*objects)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.transformchain-1.2.2-py2.7.egg/plone/transformchain/zpublisher.py", line 86, in applyTransformOnSuccess
    transformed = applyTransform(event.request)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.transformchain-1.2.2-py2.7.egg/plone/transformchain/zpublisher.py", line 75, in applyTransform
    transformed = transformer(request, result, encoding)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.transformchain-1.2.2-py2.7.egg/plone/transformchain/transformer.py", line 50, in __call__
    newResult = handler.transformIterable(result, encoding)

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.protect-3.1.3-py2.7.egg/plone/protect/auto.py", line 186, in transformIterable
    if not self.check():

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.protect-3.1.3-py2.7.egg/plone/protect/auto.py", line 211, in check
    return self._check()

  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.protect-3.1.3-py2.7.egg/plone/protect/auto.py", line 283, in _check
    '\n'.join(traceback.format_stack()),

aborting transaction due to no CSRF protection on url http://dev.zopyx.de:5080/dynasupport/front-page/document_view

-aj

I'm a bit more baffled on why it thinks it need to show a traceback. It adds no information on where the writes are coming from.

Anyhow, you can either print the registered objects in plone.protect around https://github.com/plone/plone.protect/blob/master/plone/protect/auto.py#L278 or try Lukas' patches in https://github.com/plone/plone.protect/issues/71 .
Either should help you see what is being written.

afaik document_view shouldn't do any writing at all; does any of the addons add an event handler of some kind?

There is only one policy package with these external dependencies...the rest of the installation is bare Plone 5

    install_requires=[
        # -*- Extra requirements: -*-
        'plone.api',
        'plone.app.dexterity',
        'setuptools',
        'plone.directives.form',
        'collective.geolocationbehavior',
        'z3c.jbot',
    ],
    extras_require={
        'test': [
            'plone.app.testing',
            # Plone KGS does not use this version, because it would break
            # Remove if your package shall be part of coredev.
            # plone_coredev tests as of 2016-04-01.
            'plone.testing>=5.0.0',
            'plone.app.contenttypes',
            'plone.app.robotframework[debug]',
        ],
    },
```

I checked the instance dict of the front-page document as it is stored inside the ZODB and inside plone.protect (it's the object that falsifies the safe flag in plone.protect.auto.

The instance dict suddenly has a new attribute _plone.tg which seems to have its origin in plone.app.multilingual.

The debugger confirms that plone.app.multilingual adds this attribute during the GET request:

> addAttributeTG(context, None)
> /home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.app.multilingual-5.2.0-py2.7.egg/plone/app/multilingual/itg.py(55)addAttributeTG()
-> setattr(obj, ATTRIBUTE_NAME, tg)

So why is this happening to me? I assume that there is something missing in the interaction of plone.restapi and plone.app.multilingual...likely interactive Plone usage causes the _plone.tg attribute to be written during content creation while the related step is somehow missing when you create/update content over plone.restapi.

Making sense?

Andreas

I can concur, that @lukasgraf's script is indeed very helpful. It helped me out many times! Use the contextmanager at a browser view or other place where you assume, that some unpriviliged write access happens. Without any good guess, I think it could be put in ZPublisher's publishTraverse method.

I have fixed all places in our Plone 4 site, but I can also confirm, that Plone 5 still shows CSRF warnings from time to time. Haven't just the time to look these incidences through.