Security Guidelines
Introduction
These security guidelines are for both core and application developers. They:
-
highlight some of the most common security problems and how to prevent them.
-
give you some best practices and tips about security when developing with ownCloud.
Please use them to assess how secure your application is.
Program defensively: for instance always check for CSRF or escape strings, even if you do not need it. Doing so prevents future problems where you might miss a change that leads to a security hole.
All application Framework security features depend on the call of the
controller through OCA\\AppFramework\\App::main
. If the controller
method executes directly, security checks are not performed!
General
Source Code Analysis
Before releasing an application and after security-related changes, the complete source code must be scanned. We currently use RIPS to perform scans. Affected Software:
-
Core
-
All apps in core
-
All apps in the marketplace
Architecture
Security Related Comments in Source Code
-
Security-related comments in source code are forbidden. Source code means PHP code and especially JavaScript code. Security-related comments are:
-
Usernames and passwords
-
Descriptions of processes and algorithms
-
-
Before deploying your code, use a minifier for JavaScript and CSS files.
HTTP or HTTPS
-
Only use HTTPS for rendering content.
-
Avoid switching between HTTP and HTTPS, which creates mixed-content pages.
Security Related Actions
-
All security-related actions must take place on the server. This includes validation, authentication, and authorization. Authorization implementations on the client side are only useful for providing a better user experience.
-
Don’t hard-code passwords or encryption keys in the source code. They have to be in config files and should be user-generated.
Least Privilege Principle
-
Every application should only have the rights that it needs.
-
An application should not access core database tables. If it needs data from these tables, it should call an API endpoint to retrieve it.
Error Messages and Error Pages
-
Don’t show sensitive information on error pages or in error messages. Sensitive information includes:
-
Username/password
-
E-Mail addresses
-
Version numbers
-
Paths
-
-
Don’t show overly detailed information in error messages or on error pages.
Example:
If a user can’t login, don’t show an error like:
Your password is wrong
. Instead, show a message such as:There was an error with your credentials
. If you printYour password is wrong
then an attacker knows the username was a valid one in the ownCloud installation. -
Consider implementing a CAPTCHA to prevent brute force attacks, after five failed login attempts.
Session ID Transport
-
Don’t use a session id as a GET Parameter, because these persist in browser history. Use cookies instead.
New Session ID After a Successful Login
-
After a successful login, regenerate the session id to prevent session fixation attacks.
-
If you have to switch between HTTPS and HTTP, you should change the session id, because an attacker could have already read the session id.
Access Protection With Authorization Checks
-
Every request to the server must check if the user has the authorization to perform this request. We do not recommend running these on the client-side, as they can be avoided. However, client-side checks can improve the user’s experience.
Best Practices
Use of the eval Function
-
Don’t use either PHP’s or JavaScript’s
eval
functions — especially not with user-supplied data.
Input Validation
-
All user-supplied data,
$_SERVER
, and$_COOKIE
variables must be validated. All these contain data which can be changed (or forged) by the client. -
Sanitize any supplied script code.
Example:
If you expect to receive an integer id as a GET parameter, then always
explicitly cast it into an integer using the cast operator (int)
,
because all $_REQUEST
parameters are strings. However, if you expect
text as a parameter, use
PHP’s htmlspecialchars function with
ENT_QUOTES
or strip_tags
to prevent Cross-site Scripting (XSS) attacks.
<?php
$neu = htmlspecialchars("<a href='test'>Test</a>", ENT_QUOTES);
echo $neu; // <a href='test'>Test</a>
<?php
$text = '<p>Test-Absatz.</p><!-- Kommentar --> <a href="#fragment">Anderer Text</a>';
echo strip_tags($text);
echo "\n";
Output:
Test-Absatz. Anderer Text
<p>Test-Absatz.</p> <a href="#fragment">Anderer Text</a>
Do the validation before all other actions.
Path Traversal and Path Manipulation
-
Don’t use user-supplied data to build path names, if you need to access the file system. You have to check the input parameters for null bytes (
\0
), the links to the current and parent directory on UNIX/Linux filesystems (.
and..
), and empty strings.
Prevent Command Injection
-
Use PHP’s escapeshellarg() function, if your input parameters are arguments for exec(), popen(), system(), or the backtick (``) operator.
<?php system('ls '.escapeshellarg($dir));
-
If you do not know how many arguments your application receives, then use the PHP function escapeshellcmd() to escape the whole command.
<?php $command = './configure '.$_POST['configure_options']; $escaped_command = escapeshellcmd($command); system($escaped_command);
Output Escaping
-
All input parameters printed out in the response should be escaped.
-
Do not use
print_unescaped()
in ownCloud templates, usep()
instead. -
Use
$jQuery.text()
, if you have to output text in JavaScript . -
Use
$jQuery.html()
, if you want to output HTML, . A better option is to use a tool like HTMLPurifier.
High Sensitive Information in GET Request
-
You should not use sensitive information, like passwords or usernames, in unprotected requests.
-
All requests containing sensitive information should be protected with HTTPS.
Prevent HTTP-Header-Injection (HTTP Response Splitting)
-
To prevent HTTP Response Splitting, check all request variables for
%0d
(CR) and%0a
(LF), if they are parameters provided to PHP’s header() function. This is because an attacker can deface your website, such as redirect the request to a phishing site or executing an XSS attack, by performing header manipulation.
Changes on the Document Object Model (DOM)
Don’t use unvalidated user input, if your code changes the DOM.
You should never trust user input.
Prevent SQL-Injection
-
Use the escape functions for your database to prevent SQL Injection attacks, if you have to pass parameters to a SQL query. In ownCloud you must use the QueryBuilder.
Data Storage
Persistent Storages on Client Side
-
Don’t save highly sensitive data in persistent storage on the client side. Persistent data storage includes:
Release all Resources in Case of an Error
-
All resources, such as database and file locks, must be released when errors occur. Doing so prevents the server from being subject to denial-of-service (DOS) attacks.
Cryptography
Symmetric Encryption Methods
-
If you use symmetric encryption methods in your code, use the following encryption types:
-
AES with a key length of 256
-
SERPENT with a key length of 256
-
-
For block ciphers use the following modes:
-
CFB (cipher feedback mode)
-
CBC (cipher block chaining mode)
-
CFB mode requires an initialization vector (IV) to the respective cipher function. Whereas in CBC mode, supplying one is optional. The IV must be unique and must be the same when encrypting and decrypting. Use the PHP crypt library with libmcrypt greater 2.4.x.
Asymmetric Encryption Methods
-
If you use asymmetric encryption methods, use RSA encryption with a key length of 4096.
Hash Algorithms
-
If you need a hash function in PHP, use the SHA512 hash algorithm.
-
You can use PHP’s crypt() function, but only with a strong salt.
-
Don’t use MD5, SHA1 or SHA256. These types of algorithms are designed to be very fast and efficient. However, with modern techniques and computer equipment, it has become trivial to brute force the output of these algorithms to discover the original input.
Cookies
Secure Flag
-
If you use HTTPS to protect requests, then use the secure flag for your cookies.
HTTP Only
-
If you do not have to access your cookie content in JavaScript, then set the HttpOnly flag on every cookie.
Passwords
The following chapter is not only for developers but also for admins and end-users.
Charset of Passwords
-
The charset of a password should contain characters, numbers, and special characters.
-
Characters should be both upper and lowercase.
Password Length
-
All passwords should have a minimum length of eight characters and contain numbers and special characters. These requirements must be validated by the application.
Password Quality
-
If the user can choose his password for the first time, the quality of a password should be displayed graphically.
Password Input
-
If a user can input his password into an input field, the input field must be of type
password
. -
If an error occurs, don’t fill the password field automatically when displaying an error message.
Save Passwords
-
Don’t save passwords in clear text. Use a salted hash
User Interface
Input Auto-completion
-
Auto-complete must be disabled for all input fields which receive sensitive data. Sensitive data includes:
-
Username
-
Password
-
Credit card information
-
Banking information
-
-
For text input fields use
autocomplete="off"
or use a dynamically generated field name. -
For password fields use:
<input name="pass" type="password" autocomplete="new-password" />
Attack Vectors
Auth bypass / Privilege escalations
Auth bypass/privilege escalations happen when users can perform unauthorized actions. ownCloud offers three simple checks:
-
OCP\JSON::checkLoggedIn(): Checks if the logged in user is logged in
-
OCP\JSON::checkAdminUser(): Checks if the logged in user has admin privileges
-
OCP\JSON::checkSubAdminUser(): Checks if the logged in user has group admin privileges
These checks are already automatically performed, by the application framework, for each request. If they are not required, they have to be explicitly turned off by using annotations above your controller method. Additionally, always check /if the user has the right to perform that action.
Clickjacking
Clickjacking tricks the user to click into an invisible iframe to perform an arbitrary action (e.g., deleting a user account).
To prevent such attacks ownCloud sends the X-Frame-Options header to all template responses. Don’t remove this header unless you need to!
This functionality is built into ownCloud when ownCloud templates or Twig Templates are used.
Code executions / File inclusions
Code execution means that an attacker can include an arbitrary PHP file. This PHP file runs with all the privileges granted to the normal application and can do an enormous amount of damage. Code executions and file inclusions can be easily prevented by never allowing user-input to run through the following functions:
-
include()
-
require()
-
require_once()
-
eval()
-
fopen()
Never allow the user to upload files into a folder which is reachable from the URL!
DON’T
<?php
require("/includes/" . $_GET['file']);
If you have to pass user input to a potentially dangerous function, double check to be sure that there is no other option available. If there is no other option, sanitize every user parameter and ask people to audit your sanitize functions.
Cross Site Request Forgery (CSRF)
Using CSRF one can trick a user into executing a request that he did not want to make. Thus every POST and GET request needs to be protected against it. The only places where no CSRF checks are needed are in the main template, which is rendering the application, or in externally callable interfaces.
Submitting a form is also a POST/GET request!
To prevent CSRF in an app, be sure to call the following method at the top of all your files:
<?php
OCP\JSON::callCheck();
If you are using the application Framework, every controller method is automatically checked for CSRF unless you explicitly exclude it by setting the @NoCSRFRequired
annotation before the controller method.
Cross Site Scripting (XSS)
Cross-site scripting happens when user input is passed directly to templates. A potential attacker might be able to inject HTML or JavaScript into the page to steal the user’s session, log keyboard entries, or perform DDOS attacks on other websites and other malicious actions.
Despite the fact that ownCloud uses Content-Security-Policy to prevent the execution of inline JavaScript code developers are still required to prevent XSS. CSP is another layer of defense that is not implemented in all web browsers.
To prevent XSS vulnerabilities in your application, you have to sanitize both the templates and all JavaScript scripts which perform DOM manipulation.
Templates
Let’s assume you use the following example in your application:
<?php
echo $_GET['username'];
An attacker might now easily send the user a link to
app.php?username=<script src="attacker.tld"></script>
, to take control
of the user account. The same problem occurs when outputting content
from the database, or any other location that is writable by users.
Another attack vector that is often overlooked is XSS vulnerabilities in
href
attributes. HTML allows for executing JavaScript in href
attributes like this:
<a href="javascript:alert('xss')">
To prevent XSS in your app, never use echo
, print()
or <\%=
, use
p()
instead. Doing so sanitizes input. Also validate URLs to start
with the expected protocol (starts with http
for instance)!
Should you ever need to print something unescaped, double check if it is necessary. If there is no other way (e.g., when including sub-templates) use print_unescaped with care.
JavaScript
Avoid manipulating HTML directly via JavaScript. Doing so often leads to XSS vulnerabilities since people often forget to sanitize variables. For example:
var html = '<li>' + username + '</li>"';
If you want to use JavaScript for something like this use escapeHTML to sanitize the variables:
var html = '<li>' + escapeHTML(username) + '</li>';
An even better way to make your application safer is to use the jQuery built-in function $.text(), instead of $.html().
DON’T
messageTd.html(username);
DO
messageTd.text(username);
It may also be wise to choose a proper JavaScript framework, like AngularJS, which automatically handles JavaScript escaping for you.
Directory Traversal
Very often, developers forget about sanitizing the file path (such as
removing all \\
and /
). Doing so allows an attacker to traverse
through directories on the server and opens several potential attack
vendors, which include privilege escalations, code executions, and
file disclosures.
DON’T
<?php
$username = OC_User::getUser();
fopen("/data/" . $username . "/" . $_GET['file'] . ".txt");
DO
<?php
$username = OC_User::getUser();
$file = str_replace(array('/', '\\'), `, $_GET['file']);
fopen("/data/" . $username . "/" . $file . ".txt");
PHP also interprets the backslash (\) in paths, don’t forget to replace it too!
Shell Injection
Shell Injection occurs if PHP code executes shell commands (e.g., running a latex compiler). Before doing this, check if there is a PHP library that already provides the needed functionality. If you really need to execute a command be aware that you have to escape every user parameter passed to one of these functions:
-
exec()
-
shell_exec()
-
passthru()
-
proc_open()
-
system()
-
popen()
Please require/request additional programmers to audit your escape function.
Without escaping the user input, this allows an attacker to execute arbitrary shell commands on your server. PHP offers the following functions to escape user input:
-
escapeshellarg(): Escape a string to be used as a shell argument
-
escapeshellcmd(): Escape shell metacharacters
DON’T
<?php
system('ls '.$_GET['dir']);
DO
<?php
system('ls '.escapeshellarg($_GET['dir']));
Sensitive data exposure
Always store user data or configuration files in safe locations, e.g., owncloud/data/ and not in the web root, where they are accessible by anyone using a web browser.
SQL Injection
SQL Injection occurs when SQL query strings are concatenated with variables. To prevent this, always use prepared queries:
<?php
$sql = 'SELECT * FROM `users` WHERE `id` = ?';
$query = \OCP\DB::prepare($sql);
$params = array(1);
$result = $query->execute($params);
If the application Framework is used, write SQL queries like this in the class that extends the Mapper:
<?php
// inside a child mapper class
$sql = 'SELECT * FROM `users` WHERE `id` = ?';
$params = array(1);
$result = $this->execute($sql, $params);
Unvalidated redirects
This is more of an annoyance than a critical security vulnerability since it may be used for social engineering or phishing. Before redirecting, always validate the URL if the requested URL is on the same domain or is an allowed resource.
DON’T
<?php
header('Location:'. $_GET['redirectURL']);
DO
<?php
header('Location: https://example.com'. $_GET['redirectURL']);
Getting Help
If you need help to ensure that a function is secure, please ask on our chat system for details.