Not forwarded from login page - violates the following Content Security Policy directive

Nextcloud version (eg, 20.0.5): 23.0.0
Operating system and version (eg, Ubuntu 20.04): Docker on Ubuntu 20.04
Apache or nginx version (eg, Apache 2.4.25): Docker image: nextcloud:23
PHP version (eg, 7.4): Docker image: nextcloud:23

The issue you are facing:

Unable to “flawlessly” login. I enter my login credentials, but then I seem to be stuck on the login page.
But when I enter the URL of my Nextcloud installation in another tab, I’m logged in and see the dashboard.

On the “stuck” tab, in the browser console, there’s the following error:

Refused to send form data to 'https://×××××.×××/login' because it violates the following Content Security Policy directive: "form-action 'self' https://×××××.×××".

(Instead of ×××××.×××, there’s the real hostname.)

I found that I might solve this issue by changing lib/public/AppFramework/Http/ContentSecurityPolicy.php and changing this array:

        protected $allowedFormActionDomains = [
                '\'self\'',
                'https://×××××.×××/login',
                '*.×××××.×××',
        ];

Didn’t change anything.

This does not happen with all browsers. Only with recent chromium based browsers (Brave, Vivaldi, Edge, Opera). Firefox 95 seems to ignore that.

The output of your config.php file in /path/to/nextcloud (make sure you remove any identifiable information!):

<?php
$CONFIG = array (
  'htaccess.RewriteBase' => '/',
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'apps_paths' => 
  array (
    0 => 
    array (
      'path' => '/var/www/html/apps',
      'url' => '/apps',
      'writable' => false,
    ),
    1 => 
    array (
      'path' => '/var/www/html/custom_apps',
      'url' => '/custom_apps',
      'writable' => true,
    ),
  ),
  'instanceid' => 'ocp...',
  'passwordsalt' => '...',
  'secret' => '....',
  'trusted_domains' => 
  array (
    0 => 'nc.5y5.one',
  ),
  'datadirectory' => '/var/www/html/data',
  'dbtype' => 'mysql',
  'version' => '23.0.0.10',
  'overwrite.cli.url' => 'http://×××××××××××.×××',
  'dbname' => 'nextcloud',
  'dbhost' => 'db',
  'dbport' => '',
  'dbtableprefix' => 'oc_',
  'mysql.utf8mb4' => true,
  'dbuser' => 'nextcloud',
  'dbpassword' => '.....',
  'installed' => true,
  'objectstore' => 
  array (
    'class' => '\\OC\\Files\\ObjectStore\\S3',
    'arguments' => 
    array (
      'bucket' => 'nc-...',
      'key' => '.....',
      'secret' => '.....',
      'region' => 'eu-central-1',
      'hostname' => 's3.eu-central-1.wasabisys.com',
      'port' => '',
      'objectPrefix' => 'urn:oid:',
      'autocreate' => false,
      'use_ssl' => true,
      'use_path_style' => false,
      'legacy_auth' => false,
    ),
  ),
  'maintenance' => false,
  'mail_smtpmode' => 'smtp',
  'mail_smtphost' => 'mail.xyz',
  'mail_sendmailmode' => 'smtp',
  'mail_smtpport' => '465',
  'mail_from_address' => 'senden',
  'mail_domain' => 'foo',
  'mail_smtpsecure' => 'ssl',
  'mail_smtpauth' => 1,
  'mail_smtpname' => 'foo',
  'mail_smtppassword' => 'secret',
  'twofactor_enforced' => 'false',
  'twofactor_enforced_groups' => 
  array (
  ),
  'twofactor_enforced_excluded_groups' => 
  array (
  ),
);

CSP is quite browser dependend.
One can only find out the blocking rule by disabeling things. That could be in your .htaccess file, or above in the httpd settings.
You have to evaluate on a per rule base to find out.

If you do not have acces to the above httpd code, try to add this rule to your .htaccess file in the root of the NC install:

Header unset Content-Security-Policy

Hm.
I don’t have a .htaccess file. I’m using Caddy as a webserver; TLS terminates on Caddy.

As far as Apache goes - I’m using the Docker image from Nextcloud. With however Apache is preconfigured.

And for the rule - isn’t the error message quite clear?

Refused to send form data to 'https://×××××.×××/login' because it violates the following Content Security Policy directive: "form-action 'self' https://×××××.×××".

When I change protected $allowedFormActionDomains = […] in ContentSecurityPolicy.php, I see that I’m at the right place - the error message in the console changes, accordingly to what I put there.

I’ve now added Header unset Content-Security-Policy to .htaccess. But isn’t that a bit too extreme? I mean, CSP does make sense, doesn’t it?

Apache uses .htaccess.
Yes, disabling is not what you want, but if it then works, the CSP rules are definitively cause.
In fact, Nextcloud creates this CSP rules. If defined earlier, you get double declarations. And for some rules the effect is that there is no rule anymore. Therefore I had to disable nearly all CSP that was set for the main domain (and subdomains).
You can seee what rules are active for your cloud here: https://gf.dev/csp-test

In my case I have done this for the cloud subdomain only:
Header always add Strict-Transport-Security “max-age=31536000; preload”
Header unset X-Content-Type-Options
Header unset X-XSS-Protection
Header unset X-Download-Options
Header unset Referrer-Policy
Header unset Content-Security-Policy
And this works for all our NC installs that get A+ in security tests. Test the results with the link above.

According to the error message, it should be “form-action”, which is causing issues.

https://gf.dev/csp-test shows the following CSP header (I split it on “;” to have multiple lines):

default-src 'none';
base-uri 'none';
manifest-src 'self';
script-src 'nonce-aDlQNUowdHNEcHVlUjJlcVdZNVZ1NGlRQkRiK1M0bXc0R0ZtVU50TzdHMD06ODZHSlh4SUJXSzNNQWpQZkk4b0d5c3ZLU1ZPcUlkUG4xRTRGQnFKM3ZpTT0=' blob:;
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: https://*.tile.openstreetmap.org;
font-src 'self' data:;
connect-src 'self' blob: stun.nextcloud.com:443;
media-src 'self' blob:;
frame-src 'self' https://nc.MY.DOMAIN;
child-src blob: 'self';
frame-ancestors 'self';
worker-src 'self' blob:;
form-action https://nc.MY.DOMAIN/login *.MY.DOMAIN 'self' https://nc.MY.DOMAIN

Originally, before I made changes, it was:

form-action 'self' https://nc.MY.DOMAIN

I’ve changed ContentSecurityPolicy.php to have:

/** @var array Domains which can be used as target for forms */
        protected $allowedFormActionDomains = [
                'https://nc.MY.DOMAIN/login',
                '*.MY.DOMAIN',
                '\'self\'',
        ];

I added the two lines “'https://nc.MY.DOMAIN/login',” and “'*.MY.DOMAIN',”.

Doesn’t work - either way.

What the heck… The domain is: https://nc.5y5.one/
I’d of course be willing to create a test account, if that would help.

In my case I’ve only:
form-action ‘self’
at the end.

And the connect-src is in my case (extra without port):
connect-src ‘self’ blob: stun.nextcloud.com:443 stun.nextcloud.com;

And the frame-src
frame-src ‘self’;

The rest is the same as in your settings.

But your domain is missing quite a lot of security.
There is no DNSSEC
Missing HSTS-policy

And in the report I see the CSP is not as above:
default-src ‘self’;
script-src ‘self’ ‘nonce-WnJ5am5BMnVxL2pVdC9MaHJJTXJoM2orTUZXQll5WitoU20zazdwNlVZQT06TGZXWDBsL043NEduMXFpeXhjcDQ5QmVzV3liRldoTTR5RXpENVBRS2Z2WT0=’;
style-src ‘self’ ‘unsafe-inline’;
frame-src *;
img-src * data: blob:;
font-src ‘self’ data:;
media-src *;
connect-src *;
object-src ‘none’;
base-uri ‘self’;

By default, the PHP file contains:

/** @var array Domains which can be used as target for forms */
        protected $allowedFormActionDomains = [
                '\'self\'',
        ];

With this, the generated header was:

form-action 'self' https://nc.5y5.one

The login form is on https://nc.5y5.one/login.

TBH, I don’t quite get, how this actually can be an issue - I mean, I am staying on the same domain (nc.5y5.one).

Yes, HSTS was missing. Thanks, I’ve just added it.