Security work going on in ownCloud

Besides a lot of the performance work that was lately done as well as the stability and architectural improvements we work on, we are also striving to make ownCloud even more secure by improving our API as well as introducing new hardening features. In this blog post I am going to feature some of these changes.

Those include:

Please notice that this is only a preliminary list of the most important user visible security changes. There are obviously a ton of other changes that I didn’t cover in this blog post. Furthermore, as usual with any development version, the final build may not contain any of these features if we experience any major problems with it in the testing phase.

”data/.htaccess” is updated after each update

In a default ownCloud setup the primary storage is stored on the filesystem pretty similar to the way they are stored in the internal ownCloud filesystem. So if a user carla uploads a file invoice.xls to a folder customers it is likely to be stored under /var/www/owncloud/data/carla/files/customers/invoice.xls. That said, this is simplified as there are scenarios where this is not the case such as shared folders. The data will however still be in the data directory just in another subfolder.

While we generally recommend to move the “data” directory outside of the web root (i.e. /var/www/owncloud/data/), we realize that this is not always a possibility especially considering shared hosting environments. Thus ownCloud allows having this data also in a potential insecure location. As a protection against data leakage (as anybody could access the files using a web browser otherwise) we ship the following .htaccess folder disallowing access to these folders via the web:

# Generated by ownCloud on 2015-05-22 12:09:20
# line below if for Apache 2.4
<ifModule mod_authz_core.c>
Require all denied
</ifModule>

# line below if for Apache 2.2
<ifModule !mod_authz_core.c>
deny from all
Satisfy All
</ifModule>

# section for Apache 2.2 and 2.4
IndexIgnore *

As you can see we are effectively forbidding access to this directory using appropriate .htaccess rules for Apache 2.2 as well as it’s successor 2.4. However, older versions of ownCloud come with .htaccess files that do miss these directives for Apache 2.4 or rely on components such as having the Apache 2.4 compatibility module “mod_access_compat” installed. If an administrator updated an older ownCloud instance to a newer Apache server there would be thus the potential risk of data leakage.

However, the administrative interface of ownCloud will since quite some version warn administrators if the data directory is exposed to the public due to a configuration problem. However, as software provider we have to take our users’ security very seriously and thus automatically help administrators as much as possible to secure their instance.

With ownCloud 8.1 the existing .htaccess file in the data directory will thus for enhanced security get updated after each update. Administrators are encouraged to not perform any custom modifications to these files and for an even more secure experience move the data folder outside of the web root.

SabreDAV web interface has been removed

The SabreDAV web interface at remote.php/webdav/ was primarily used as a debugging tool by developers allowing us to test our integration with SabreDAV without actually starting a WebDAV client or craft requests ourselves. For end-users the actual use-case of this endpoint was rather limited.

SabreDAV web interface

With ownCloud 8.1 we have updated our used SabreDAV version to 2.1 which comes with a new browser plugin. This new browser plugin has added much more functionalities such as displaying the specific attributes of a file (which are user-controllable), while this indeed is super nifty for debugging purposes the potential risk of a Social Engineering attempt using this interface was from our opinion too big so that we decided to not activate the SabreDAV file browser plugin anymore.

Furthermore, in the past we have discovered some potential vulnerabilities within the file browser plugin and further analysis by us has found some other problems. (fruux/sabre-dav#610fruux/sabe-dav#611)

This does not mean that SabreDAV is insecure or – beware – a bad library, it just implies that with a solution such as ownCloud we are facing challenges that smaller projects do not face and every line of code increases the overall attack vector.

Trusted domains are now a hard requirement

The trusted_domains setting of ownCloud are with ownCloud 8.1 a new hard-requirement. In previous versions it was possible to remove the configuration switch and it would accept any hostname as valid. We experienced a lot of misuse of this configuration as there was initial a confusion about the setting resulting in users of ownCloud spreading wrong information and dangerous tips and tricks.

In a nutshell: A trusted domain is a domain that the ownCloud server accepts connections at. So if you host “demo.owncloud.org” the trusted domain will be “demo.owncloud.org” and not every client address.

To protect our users it is thus not possible anymore to omit the “trusted_domain” setting. For a more throughout details about the trusted domains setting I recommend reading my post “A tale about trusted_domains”.

Failed login log entries supports reverse proxy settings

The fact that ownCloud supports reverse proxies properly is often not known by administrators. Thus in most scenarios the IP address of the proxy server is used by the ownCloud for logging and audit purposes. It is however possible to configure a proxy mode that allows reading the IP address from specified headers. In such cases the actual client IP address is then logged.

In the sample configuration (config/config.sample.php) the following relevant switches can be found:

<?php

…

/**
 * List of trusted proxy servers
 */
'trusted_proxies' => array('203.0.113.45', '198.51.100.128'),

/**
 * Headers that should be trusted as client IP address in combination with
 * `trusted_proxies`
 */
'forwarded_for_headers' => array('HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR'),

…

In above configuration the server would read the X-Forwarded-For header and then the Forwarded-For header if a request originates from one of the defined trusted_proxies (203.0.113.45 or 198.51.100.128) and use this as client IP address. A request not fulfilling those will just use the $_SERVER['REMOTE_ADDR'] address provided by PHP.

Given these changes and the fact we were able to modify the failed login entry line a little bit, older ownCloud releases would create an entry such as the following:

Login failed: '$loginName' (Remote IP: '$remoteAddr', X-Forwarded-For: '$forwardedFor')

With ownCloud 8.1 $remoteAddr will now get resolved to the proxy address and thus the X-Forwarded-For entry is not longer required:

Login failed: '$loginName' (Remote IP: '$remoteAddr')

This is not the only case where ownCloud 8.1 will be able to properly use the client address and in reverse proxy environments it is highly recommended to configure these settings.

Request ID supports mod_unique_id

Each request to an ownCloud instance gets a request ID associated which is used for example for logging purposes. Until now the request ID was simply a random identifier. With ownCloud 8.1 we will officially support “mod_unique_id”, this means the request ID will not longer be generated by the ownCloud server but by the Apache server instead. To enable this feature “mod_unique_id” just needs to be enabled.

This allows administrators to better correlate any log information with the ownCloud logs making it easier to track any potential security incident over bigger setups. Furthermore “mod_unique_id” is in proper setup environments guaranteeing uniqueness while the default ownCloud request ID may get reused. (as it only guarantees randomness when generating)

The “Download from link” feature has been in ownCloud for a long time and allowed users to specify an URL and ownCloud would connect to the given URL, download the site and store it in the users’ folder. This was especially useful when one had to download huge remote files such as ISOs and the current local network bandwidth was not sufficient to achieve this.

Download from Link feature in ownCloud 8.0

This feature has been removed in 8.1 since we realized that in quite some deployment scenarios, especially when it comes to huge company networks, it is not really desired to have such a feature. Often this would allow users to gain more insights into the network architecture or access to sensitive internal services if the deployment was not properly protected against such scenarios. Thus we decided that the low amount of users that actually use this feature are not worth potentially making all other deployments potentially insecure.

As our modular and unique application approach allows developers to create a huge bandwidth of applications there are already third-party applications such as “ocDownloader (NG)” filling the gap. Using third-party applications users that require such features can easily get those while the “core” code of ownCloud can stay slim and allows us core developers to focus on it’s stability.

When operating a web application it is often desirable to have the basic HTTP security headers enabled to prevent security pitfalls. Some of these basic security headers are served by ownCloud already in a default environment. These includes:

  • X-Content-Type-Options: nosniff
    • Instructs some browsers to not sniff the mimetype of files. This is used for example to prevent browsers to interpret text files as JavaScript.
  • X-XSS-Protection: 1; mode=block
    • Enforces the browsers to enable their browser side Cross-Site-Scripting filter.
  • X-Robots-Tag: none
    • Instructs search machines to not index these page.
  • X-Frame-Options: SAMEORIGIN
    • Prevents to embed the ownCloud instance within an iframe from other domains to prevent Clickjacking and other similar attacks.

However, these headers are added by the applications code in PHP and thus not served on static resources and rely on the fact that there is no way to bypass the intended response code path.

For optimal security administrators are encouraged to serve these basic HTTP headers by the web server to enforce them on response. To do this Apache has to be configured to use the .htaccess file as well as the following Apache modules needs to be enabled:

  • mod_headers
  • mod_env

Administrators can verify whether this security change is active by accessing a static resource served by the web server and verify that above mentioned security headers are shipped. This can for example be done via cURL using the CLI, in the following example the first accessed server would take advantage of this enhancement while the second would. The difference has been marked using a diff:

➜  ~  curl -s -D - https://demo.owncloud.org/core/img/logo-icon.svg -o /dev/null
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 26 May 2015 16:07:45 GMT
Content-Type: image/svg+xml
Content-Length: 2893
Connection: keep-alive
Last-Modified: Mon, 09 Mar 2015 08:27:14 GMT
ETag: "b4d-510d6cd1f9c80"
cache-control: max-age=7200
Accept-Ranges: bytes
Age: 6322
➜  ~  curl -s -D - http://localhost/core/img/logo-icon.svg -o /dev/null
HTTP/1.1 200 OK
Date: Tue, 26 May 2015 16:08:32 GMT
Server: Apache/2.4.10 (Unix) PHP/5.6.8
Last-Modified: Fri, 22 May 2015 11:41:02 GMT
ETag: "b5e-516aa22768f80"
Accept-Ranges: bytes
Content-Length: 2910
+ X-Content-Type-Options: nosniff
+ X-XSS-Protection: 1; mode=block
+ X-Robots-Tag: none
+ X-Frame-Options: SAMEORIGIN
Content-Type: image/svg+xml

This changes also applies to the HTTPS enforcement of ownCloud that now has to happen on the web server layer and not anymore in the ownCloud configuration. While this is slightly less comfortable it ensures an overall better security level.

Enhancement of root certificate handling

Proper HTTPS requests with PHP are hard. First all of the offered APIs are somewhat lacking and cumbersome to use. Secondly, and even more important, most hosts are improperly setup and missing the proper certificate chain. And if they offer one it is often very likely to be terribly outdated.

Very often we received bug reports by users experiencing problems with their ownCloud instance which in the end were triggered by their distributions and PHP settings. In the end this problems became very hard to debug and thus we decided to ship a root certificate bundle with ownCloud itself.

The root certificate chain contains the certificates shipped by Mozilla and we regularly sync it with the upstream certificates. It can be found under config/ca-bundle.crt and in case administrators want to include a custom root certificate they can insert it into this file. To take effect on all user accounts an ownCloud update is required though since all user root certificates needs to get regenerated as well.

This also fixes a problem in older ownCloud releases with regard to the personal certificate handling, if a user had imported custom SSL certificates only those were used and not the system certificates since with the API imposed by PHP it was only possible to specify a custom root certificate chain. If the chain of the user was used the official root certificates were not existent leading to a lot of other problems.

OC_User_HTTP backend replaced by user_webdavauth

ownCloud supports by default a lot of external user databases, such as FTP, LDAP, IMAP and a HTTP backend authenticating via Basic Auth.

The HTTP backend OC_User_HTTP has been deprecated with ownCloud 8.1 as it offers the same functionality as the “user_webdavauth” application shipped with ownCloud as well. Even more, the latter one can easily be configured via web interface and does not require adjusting the configuration file itself.

Administrators using the OC_User_HTTP backend are encouraged to migrate to the “user_webdavauth” application. This can be done by removing the entry from the configuration file and then enabling the “WebDAV authentication” application and configure it in the admin interface:

user_webdavauth configuration

Random-number generator returns all base64 characters

While this is more of a nitpick than a security hardening the documentation of our cryptographically secure pseudo-random number generator (CSPRNG) claimed to return a string containing all base64 characters. This was however not exactly the case as the equals sign (=) got silently dropped.

This is however especially important for developers as any generated random-number now needs to be properly encoded if used in URLs. One common occurrence is the CSRF token within applications that do not use AJAX requests on CSRF protected routes.

One should note that it is best practice to always ensure that all parameters are properly encoded before using it to prevent problems with unexpected data.

OC.generateUrl encodes parameters now automatically

As we experienced some problems with developers using not properly encoded data such as the above mentioned CSRF token we changed the JavaScript OC.generateUrl function to properly encode any passed input, the following example explains the problem pretty good:

OC.generateUrl(
	'apps/myApp/accounts/{accountId}',
	{
		accountId: 'MyAccountId"><script>alert(1)</script>',
	}
)

With ownCloud 8.1 this will return the following URL: /index.php/apps/myApp/accounts/MyAccountId%22%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E, older releases will instead return /index.php/apps/myApp/accounts/MyAccountId"><script>alert(1)</script>. In quite some cases this can unwillingly lead to either simply broken applications or even worse an actual bug with security implications.

Developers that rely on this behaviour can still achieve this by modifying the third options parameter and disabling the escaping as following:

OC.generateUrl(
	'apps/myApp/accounts/{accountId}',
	{
		accountId: 'MyAccountId"><script>alert(1)</script>',
	},
	{
		escape: false,
	}
)

Code checker checks for strict comparisons in PHP code

While our coding guidelines already a longer time enforces the usage of strict comparisons within our code-base we can now enforce this using the built-in code checker in ownCloud. In fact it is now able to throw warnings when it encounters an usage of non-strict comparisons.

To understand the PHP strict comparison problem it is required to understand that PHP tries to make the life of developers as easy as possible and thus even allows you to loosely compare strings with an integer. So a string with the value of 9 would equal an integer with a value of 9.

While in the first sight this behaviour seems desirable and useful there are a few very big caveats. For once it is what happens when PHP tries to cast a string into a number. It will simply result in a 0. In the following example the code would return “Correct password!” even if the user provided an invalid numerical password as PHP tries to convert the password into an integer.

<?php
$input = json_decode('{"password":0}');

if($input->password == 'aVeryLongAndSecretPasswordThatYouShallNotKnow') {
	echo('Correct password!');
}

To avoid this developers are advocated to use strict comparisons using === and !== to avoid this potential pitfalls. Using one of those both operators will prevent PHP from trying to cast the parameter to another type and perform a strict comparison.

A full list of the PHP type comparison behaviour can be found on php.net.

Constructor of OC\Files\View prevents directory traversals

We’re aiming hard to have a “security by default” and “defense-in-depth” model in our code-base. Thus the internal ownCloud filesystem is built to prevent directory traversals by forbidding potential dangerous character sequences such as ../ or ..\. This check was however only performed on the single methods and not on the constructor of the class.

A view represents something like a chroot within ownCloud, developers can instantiate a view on folders and users cannot escape from it or access data from without it. When an view was however initially constructed there was no check for those malicious characters.

This has lead to potential issues in the past when developers misused the class and to prevent future problems we have applied the same security to the constructor now as we do for the single methods.

Stricter Content-Security-Policy and more choices for developers

Content-Security-Policy (CSP) is one of the most useful and powerful web features introduced in the recent years to the web. Basically with CSP applications can instruct the browser to follow a specified security model, this can include to not execute any inline scripts or not load remote resources.

Using a CSP compliant browser (mainly everything that is not IE) allows applications to maintain your very owns data’s security even if a Cross-Site-Scripting bug exists. With earlier versions of ownCloud there was a policy that applied to the whole instance. This had the disadvantage that the whole application received permissions it would probably not even require. Thus with ownCloud 8.1 we tightened the default policy, the policy as of today is:

  • default-src 'none'
    • If no policy applies forbid the action.
  • script-src 'self' 'unsafe-eval'
    • Execute only scripts served from the same domain and allow eval(). Inline JavaScript is forbidden.
  • style-src 'self' 'unsafe-inline'
    • Execute only style sheets served from the same domain. Inline CSS is allowed.
  • img-src 'self'
    • Load only images served from the same domain.
  • font-src 'self'
    • Load only fonts served from the same domain.
  • connect-src 'self'
    • Allow the browser to only perform actions such as AJAX requests against the same domain.
  • media-src 'self'
    • Load only videos and audio elements served from the same domain.

However, as a developer using the AppFramework it is now possible to modify the policy, in the following example the policy would now also allow loading of images and media elements from any domain:

<?php
namespace OCA\MyApp\Controller;

use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\ContentSecurityPolicy;

class PageController extends Controller {

    public function index() {
        $response = new TemplateResponse('myapp', 'main');
        $csp = new ContentSecurityPolicy();
        $csp->addAllowedImageDomain('*');
            ->addAllowedMediaDomain('*');
        $response->setContentSecurityPolicy($csp);
        return $response;
    }

}

Applications not using the AppFramework will still be subject to the policy used by older ownCloud versions.

More details about CSP can be found in my blog post “Content-Security-Policy and ownCloud” or our developer documentation.

New security guidance and tips & tricks

It’s easy to introduce security problems or have a system improperly configured, thus for ownCloud 8.1 we will have a new “Tips & tricks” section giving tips to administrators how to properly configure their instances. This offers advises going from monitoringperformance tuning, over to theming and of course hardening and security guidance:

Tips & tricks in the ownCloud admin interface

Furthermore the security warnings will now be more precise and show even more hints to administrators where applicable:

Security warnings in the ownCloud admin interface

Security is hard and an always ongoing process and I am sure that with future releases we will be able to improve us even more. We are also always welcoming and looking for new contributors of any sort. If you think you can actively help us to make ownCloud even more secure you are more than welcome in “#owncloud-security” on the freenode IRC network.

Receive all new posts as email

Subscribe to my newsletter to get all new blog posts right into your inbox.


Posted

in

,

by

Tags:

Comments

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Create a website or blog at WordPress.com