Fix security vulnerbilities.

Fix possible length extension attack, predicable generators, timing attacks on hash comparision and improved formatting.
This commit is contained in:
dew-git 2019-10-10 15:21:41 -08:00
parent 50fa119f79
commit 811cdcdfcb
8 changed files with 455 additions and 332 deletions

View File

@ -4,27 +4,30 @@
/* Generic exception class /* Generic exception class
*/ */
if (!class_exists('OAuthException', false)) { if (!class_exists('OAuthException', false)) {
class OAuthException extends Exception { class OAuthException extends Exception
// pass { }
}
} }
class OAuthConsumer { class OAuthConsumer
{
public $key; public $key;
public $secret; public $secret;
function __construct($key, $secret, $callback_url=NULL) { function __construct($key, $secret, $callback_url = NULL)
{
$this->key = $key; $this->key = $key;
$this->secret = $secret; $this->secret = $secret;
$this->callback_url = $callback_url; $this->callback_url = $callback_url;
} }
function __toString() { function __toString()
{
return "OAuthConsumer[key=$this->key,secret=$this->secret]"; return "OAuthConsumer[key=$this->key,secret=$this->secret]";
} }
} }
class OAuthToken { class OAuthToken
{
// access tokens and request tokens // access tokens and request tokens
public $key; public $key;
public $secret; public $secret;
@ -37,7 +40,8 @@ class OAuthToken {
* key = the token * key = the token
* secret = the token secret * secret = the token secret
*/ */
function __construct($key, $secret) { function __construct($key, $secret)
{
$this->key = $key; $this->key = $key;
$this->secret = $secret; $this->secret = $secret;
} }
@ -46,14 +50,16 @@ class OAuthToken {
* generates the basic string serialization of a token that a server * generates the basic string serialization of a token that a server
* would respond to request_token and access_token calls with * would respond to request_token and access_token calls with
*/ */
function to_string() { function to_string()
{
return "oauth_token=" . return "oauth_token=" .
OAuthUtil::urlencode_rfc3986($this->key) . OAuthUtil::urlencode_rfc3986($this->key) .
"&oauth_token_secret=" . "&oauth_token_secret=" .
OAuthUtil::urlencode_rfc3986($this->secret); OAuthUtil::urlencode_rfc3986($this->secret);
} }
function __toString() { function __toString()
{
return $this->to_string(); return $this->to_string();
} }
} }
@ -62,7 +68,8 @@ class OAuthToken {
* A class for implementing a Signature Method * A class for implementing a Signature Method
* See section 9 ("Signing Requests") in the spec * See section 9 ("Signing Requests") in the spec
*/ */
abstract class OAuthSignatureMethod { abstract class OAuthSignatureMethod
{
/** /**
* Needs to return the name of the Signature Method (ie HMAC-SHA1) * Needs to return the name of the Signature Method (ie HMAC-SHA1)
* @return string * @return string
@ -89,7 +96,8 @@ abstract class OAuthSignatureMethod {
* @param string $signature * @param string $signature
* @return bool * @return bool
*/ */
public function check_signature($request, $consumer, $token, $signature) { public function check_signature($request, $consumer, $token, $signature)
{
$built = $this->build_signature($request, $consumer, $token); $built = $this->build_signature($request, $consumer, $token);
return ($built == $signature); return ($built == $signature);
} }
@ -102,12 +110,15 @@ abstract class OAuthSignatureMethod {
* character (ASCII code 38) even if empty. * character (ASCII code 38) even if empty.
* - Chapter 9.2 ("HMAC-SHA1") * - Chapter 9.2 ("HMAC-SHA1")
*/ */
class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod
function get_name() { {
function get_name()
{
return "HMAC-SHA1"; return "HMAC-SHA1";
} }
public function build_signature($request, $consumer, $token) { public function build_signature($request, $consumer, $token)
{
$base_string = $request->get_signature_base_string(); $base_string = $request->get_signature_base_string();
$request->base_string = $base_string; $request->base_string = $base_string;
@ -130,8 +141,10 @@ class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
* over a secure channel such as HTTPS. It does not use the Signature Base String. * over a secure channel such as HTTPS. It does not use the Signature Base String.
* - Chapter 9.4 ("PLAINTEXT") * - Chapter 9.4 ("PLAINTEXT")
*/ */
class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod
public function get_name() { {
public function get_name()
{
return "PLAINTEXT"; return "PLAINTEXT";
} }
@ -144,7 +157,8 @@ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
* Please note that the second encoding MUST NOT happen in the SignatureMethod, as * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
* OAuthRequest handles this! * OAuthRequest handles this!
*/ */
public function build_signature($request, $consumer, $token) { public function build_signature($request, $consumer, $token)
{
$key_parts = array( $key_parts = array(
$consumer->secret, $consumer->secret,
($token) ? $token->secret : "" ($token) ? $token->secret : ""
@ -166,8 +180,10 @@ class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
* specification. * specification.
* - Chapter 9.3 ("RSA-SHA1") * - Chapter 9.3 ("RSA-SHA1")
*/ */
abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod
public function get_name() { {
public function get_name()
{
return "RSA-SHA1"; return "RSA-SHA1";
} }
@ -185,7 +201,8 @@ abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
// Either way should return a string representation of the certificate // Either way should return a string representation of the certificate
protected abstract function fetch_private_cert(&$request); protected abstract function fetch_private_cert(&$request);
public function build_signature($request, $consumer, $token) { public function build_signature($request, $consumer, $token)
{
$base_string = $request->get_signature_base_string(); $base_string = $request->get_signature_base_string();
$request->base_string = $base_string; $request->base_string = $base_string;
@ -204,7 +221,8 @@ abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
return base64_encode($signature); return base64_encode($signature);
} }
public function check_signature($request, $consumer, $token, $signature) { public function check_signature($request, $consumer, $token, $signature)
{
$decoded_sig = base64_decode($signature); $decoded_sig = base64_decode($signature);
$base_string = $request->get_signature_base_string(); $base_string = $request->get_signature_base_string();
@ -225,7 +243,8 @@ abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
} }
} }
class OAuthRequest { class OAuthRequest
{
private $parameters; private $parameters;
private $http_method; private $http_method;
private $http_url; private $http_url;
@ -234,9 +253,10 @@ class OAuthRequest {
public static $version = '1.0'; public static $version = '1.0';
public static $POST_INPUT = 'php://input'; public static $POST_INPUT = 'php://input';
function __construct($http_method, $http_url, $parameters=NULL) { function __construct($http_method, $http_url, $parameters = NULL)
{
@$parameters or $parameters = array(); @$parameters or $parameters = array();
$parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); $parameters = array_merge(OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
$this->parameters = $parameters; $this->parameters = $parameters;
$this->http_method = $http_method; $this->http_method = $http_method;
$this->http_url = $http_url; $this->http_url = $http_url;
@ -246,7 +266,8 @@ class OAuthRequest {
/** /**
* attempt to build up a request from what was passed to the server * attempt to build up a request from what was passed to the server
*/ */
public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { public static function from_request($http_method = NULL, $http_url = NULL, $parameters = NULL)
{
$scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
? 'http' ? 'http'
: 'https'; : 'https';
@ -270,9 +291,12 @@ class OAuthRequest {
// It's a POST request of the proper content-type, so parse POST // It's a POST request of the proper content-type, so parse POST
// parameters and add those overriding any duplicates from GET // parameters and add those overriding any duplicates from GET
if ($http_method == "POST" if (
&& @strstr($request_headers["Content-Type"], $http_method == "POST"
"application/x-www-form-urlencoded") && @strstr(
$request_headers["Content-Type"],
"application/x-www-form-urlencoded"
)
) { ) {
$post_data = OAuthUtil::parse_parameters( $post_data = OAuthUtil::parse_parameters(
file_get_contents(self::$POST_INPUT) file_get_contents(self::$POST_INPUT)
@ -288,12 +312,11 @@ class OAuthRequest {
); );
$parameters = array_merge($parameters, $header_parameters); $parameters = array_merge($parameters, $header_parameters);
} }
} }
// fix for friendica redirect system // fix for friendica redirect system
$http_url = substr($http_url, 0, strpos($http_url,$parameters['pagename'])+strlen($parameters['pagename'])); $http_url = substr($http_url, 0, strpos($http_url, $parameters['pagename']) + strlen($parameters['pagename']));
unset( $parameters['pagename'] ); unset($parameters['pagename']);
return new OAuthRequest($http_method, $http_url, $parameters); return new OAuthRequest($http_method, $http_url, $parameters);
} }
@ -301,12 +324,15 @@ class OAuthRequest {
/** /**
* pretty much a helper function to set up the request * pretty much a helper function to set up the request
*/ */
public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters = NULL)
{
@$parameters or $parameters = array(); @$parameters or $parameters = array();
$defaults = array("oauth_version" => OAuthRequest::$version, $defaults = array(
"oauth_version" => OAuthRequest::$version,
"oauth_nonce" => OAuthRequest::generate_nonce(), "oauth_nonce" => OAuthRequest::generate_nonce(),
"oauth_timestamp" => OAuthRequest::generate_timestamp(), "oauth_timestamp" => OAuthRequest::generate_timestamp(),
"oauth_consumer_key" => $consumer->key); "oauth_consumer_key" => $consumer->key
);
if ($token) if ($token)
$defaults['oauth_token'] = $token->key; $defaults['oauth_token'] = $token->key;
@ -315,7 +341,8 @@ class OAuthRequest {
return new OAuthRequest($http_method, $http_url, $parameters); return new OAuthRequest($http_method, $http_url, $parameters);
} }
public function set_parameter($name, $value, $allow_duplicates = true) { public function set_parameter($name, $value, $allow_duplicates = true)
{
if ($allow_duplicates && isset($this->parameters[$name])) { if ($allow_duplicates && isset($this->parameters[$name])) {
// We have already added parameter(s) with this name, so add to the list // We have already added parameter(s) with this name, so add to the list
if (is_scalar($this->parameters[$name])) { if (is_scalar($this->parameters[$name])) {
@ -330,15 +357,18 @@ class OAuthRequest {
} }
} }
public function get_parameter($name) { public function get_parameter($name)
{
return isset($this->parameters[$name]) ? $this->parameters[$name] : null; return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
} }
public function get_parameters() { public function get_parameters()
{
return $this->parameters; return $this->parameters;
} }
public function unset_parameter($name) { public function unset_parameter($name)
{
unset($this->parameters[$name]); unset($this->parameters[$name]);
} }
@ -346,7 +376,8 @@ class OAuthRequest {
* The request parameters, sorted and concatenated into a normalized string. * The request parameters, sorted and concatenated into a normalized string.
* @return string * @return string
*/ */
public function get_signable_parameters() { public function get_signable_parameters()
{
// Grab all parameters // Grab all parameters
$params = $this->parameters; $params = $this->parameters;
@ -366,7 +397,8 @@ class OAuthRequest {
* and the parameters (normalized), each urlencoded * and the parameters (normalized), each urlencoded
* and the concated with &. * and the concated with &.
*/ */
public function get_signature_base_string() { public function get_signature_base_string()
{
$parts = array( $parts = array(
$this->get_normalized_http_method(), $this->get_normalized_http_method(),
$this->get_normalized_http_url(), $this->get_normalized_http_url(),
@ -381,7 +413,8 @@ class OAuthRequest {
/** /**
* just uppercases the http method * just uppercases the http method
*/ */
public function get_normalized_http_method() { public function get_normalized_http_method()
{
return strtoupper($this->http_method); return strtoupper($this->http_method);
} }
@ -389,7 +422,8 @@ class OAuthRequest {
* parses the url and rebuilds it to be * parses the url and rebuilds it to be
* scheme://host/path * scheme://host/path
*/ */
public function get_normalized_http_url() { public function get_normalized_http_url()
{
$parts = parse_url($this->http_url); $parts = parse_url($this->http_url);
$port = @$parts['port']; $port = @$parts['port'];
@ -400,7 +434,8 @@ class OAuthRequest {
$port or $port = ($scheme == 'https') ? '443' : '80'; $port or $port = ($scheme == 'https') ? '443' : '80';
if (($scheme == 'https' && $port != '443') if (($scheme == 'https' && $port != '443')
|| ($scheme == 'http' && $port != '80')) { || ($scheme == 'http' && $port != '80')
) {
$host = "$host:$port"; $host = "$host:$port";
} }
return "$scheme://$host$path"; return "$scheme://$host$path";
@ -409,11 +444,12 @@ class OAuthRequest {
/** /**
* builds a url usable for a GET request * builds a url usable for a GET request
*/ */
public function to_url() { public function to_url()
{
$post_data = $this->to_postdata(); $post_data = $this->to_postdata();
$out = $this->get_normalized_http_url(); $out = $this->get_normalized_http_url();
if ($post_data) { if ($post_data) {
$out .= '?'.$post_data; $out .= '?' . $post_data;
} }
return $out; return $out;
} }
@ -421,9 +457,10 @@ class OAuthRequest {
/** /**
* builds the data one would send in a POST request * builds the data one would send in a POST request
*/ */
public function to_postdata($raw = false) { public function to_postdata($raw = false)
{
if ($raw) if ($raw)
return($this->parameters); return $this->parameters;
else else
return OAuthUtil::build_http_query($this->parameters); return OAuthUtil::build_http_query($this->parameters);
} }
@ -431,15 +468,15 @@ class OAuthRequest {
/** /**
* builds the Authorization: header * builds the Authorization: header
*/ */
public function to_header($realm=null) { public function to_header($realm = null)
{
$first = true; $first = true;
if($realm) { if ($realm) {
$out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
$first = false; $first = false;
} else } else
$out = 'Authorization: OAuth'; $out = 'Authorization: OAuth';
$total = array();
foreach ($this->parameters as $k => $v) { foreach ($this->parameters as $k => $v) {
if (substr($k, 0, 5) != "oauth") continue; if (substr($k, 0, 5) != "oauth") continue;
if (is_array($v)) { if (is_array($v)) {
@ -455,12 +492,14 @@ class OAuthRequest {
return $out; return $out;
} }
public function __toString() { public function __toString()
{
return $this->to_url(); return $this->to_url();
} }
public function sign_request($signature_method, $consumer, $token) { public function sign_request($signature_method, $consumer, $token)
{
$this->set_parameter( $this->set_parameter(
"oauth_signature_method", "oauth_signature_method",
$signature_method->get_name(), $signature_method->get_name(),
@ -470,7 +509,8 @@ class OAuthRequest {
$this->set_parameter("oauth_signature", $signature, false); $this->set_parameter("oauth_signature", $signature, false);
} }
public function build_signature($signature_method, $consumer, $token) { public function build_signature($signature_method, $consumer, $token)
{
$signature = $signature_method->build_signature($this, $consumer, $token); $signature = $signature_method->build_signature($this, $consumer, $token);
return $signature; return $signature;
} }
@ -478,33 +518,35 @@ class OAuthRequest {
/** /**
* util function: current timestamp * util function: current timestamp
*/ */
private static function generate_timestamp() { private static function generate_timestamp()
{
return time(); return time();
} }
/** /**
* util function: current nonce * util function: current nonce
*/ */
private static function generate_nonce() { private static function generate_nonce()
$mt = microtime(); {
$rand = mt_rand(); return bin2hex(random_bytes(16));
return md5($mt . $rand); // md5s look nicer than numbers
} }
} }
class OAuthServer { class OAuthServer
{
protected $timestamp_threshold = 300; // in seconds, five minutes protected $timestamp_threshold = 300; // in seconds, five minutes
protected $version = '1.0'; // hi blaine protected $version = '1.0'; // hi blaine
protected $signature_methods = array(); protected $signature_methods = array();
protected $data_store; protected $data_store;
function __construct($data_store) { function __construct($data_store)
{
$this->data_store = $data_store; $this->data_store = $data_store;
} }
public function add_signature_method($signature_method) { public function add_signature_method($signature_method)
{
$this->signature_methods[$signature_method->get_name()] = $this->signature_methods[$signature_method->get_name()] =
$signature_method; $signature_method;
} }
@ -515,7 +557,8 @@ class OAuthServer {
* process a request_token request * process a request_token request
* returns the request token on success * returns the request token on success
*/ */
public function fetch_request_token(&$request) { public function fetch_request_token(&$request)
{
$this->get_version($request); $this->get_version($request);
$consumer = $this->get_consumer($request); $consumer = $this->get_consumer($request);
@ -536,7 +579,8 @@ class OAuthServer {
* process an access_token request * process an access_token request
* returns the access token on success * returns the access token on success
*/ */
public function fetch_access_token(&$request) { public function fetch_access_token(&$request)
{
$this->get_version($request); $this->get_version($request);
$consumer = $this->get_consumer($request); $consumer = $this->get_consumer($request);
@ -556,19 +600,21 @@ class OAuthServer {
/** /**
* verify an api call, checks all the parameters * verify an api call, checks all the parameters
*/ */
public function verify_request(&$request) { public function verify_request(&$request)
{
$this->get_version($request); $this->get_version($request);
$consumer = $this->get_consumer($request); $consumer = $this->get_consumer($request);
$token = $this->get_token($request, $consumer, "access"); $token = $this->get_token($request, $consumer, "access");
$this->check_signature($request, $consumer, $token); $this->check_signature($request, $consumer, $token);
return array($consumer, $token); return [$consumer, $token];
} }
// Internals from here // Internals from here
/** /**
* version 1 * version 1
*/ */
private function get_version(&$request) { private function get_version(&$request)
{
$version = $request->get_parameter("oauth_version"); $version = $request->get_parameter("oauth_version");
if (!$version) { if (!$version) {
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
@ -584,7 +630,8 @@ class OAuthServer {
/** /**
* figure out the signature with some defaults * figure out the signature with some defaults
*/ */
private function get_signature_method(&$request) { private function get_signature_method(&$request)
{
$signature_method = $signature_method =
@$request->get_parameter("oauth_signature_method"); @$request->get_parameter("oauth_signature_method");
@ -594,8 +641,10 @@ class OAuthServer {
throw new OAuthException('No signature method parameter. This parameter is required'); throw new OAuthException('No signature method parameter. This parameter is required');
} }
if (!in_array($signature_method, if (!in_array(
array_keys($this->signature_methods))) { $signature_method,
array_keys($this->signature_methods)
)) {
throw new OAuthException( throw new OAuthException(
"Signature method '$signature_method' not supported " . "Signature method '$signature_method' not supported " .
"try one of the following: " . "try one of the following: " .
@ -608,7 +657,8 @@ class OAuthServer {
/** /**
* try to find the consumer for the provided request's consumer key * try to find the consumer for the provided request's consumer key
*/ */
private function get_consumer(&$request) { private function get_consumer(&$request)
{
$consumer_key = @$request->get_parameter("oauth_consumer_key"); $consumer_key = @$request->get_parameter("oauth_consumer_key");
if (!$consumer_key) { if (!$consumer_key) {
throw new OAuthException("Invalid consumer key"); throw new OAuthException("Invalid consumer key");
@ -625,10 +675,13 @@ class OAuthServer {
/** /**
* try to find the token for the provided request's token key * try to find the token for the provided request's token key
*/ */
private function get_token(&$request, $consumer, $token_type="access") { private function get_token(&$request, $consumer, $token_type = "access")
{
$token_field = @$request->get_parameter('oauth_token'); $token_field = @$request->get_parameter('oauth_token');
$token = $this->data_store->lookup_token( $token = $this->data_store->lookup_token(
$consumer, $token_type, $token_field $consumer,
$token_type,
$token_field
); );
if (!$token) { if (!$token) {
throw new OAuthException("Invalid $token_type token: $token_field"); throw new OAuthException("Invalid $token_type token: $token_field");
@ -640,7 +693,8 @@ class OAuthServer {
* all-in-one function to check the signature on a request * all-in-one function to check the signature on a request
* should guess the signature method appropriately * should guess the signature method appropriately
*/ */
private function check_signature(&$request, $consumer, $token) { private function check_signature(&$request, $consumer, $token)
{
// this should probably be in a different method // this should probably be in a different method
$timestamp = @$request->get_parameter('oauth_timestamp'); $timestamp = @$request->get_parameter('oauth_timestamp');
$nonce = @$request->get_parameter('oauth_nonce'); $nonce = @$request->get_parameter('oauth_nonce');
@ -667,8 +721,9 @@ class OAuthServer {
/** /**
* check that the timestamp is new enough * check that the timestamp is new enough
*/ */
private function check_timestamp($timestamp) { private function check_timestamp($timestamp)
if( ! $timestamp ) {
if (!$timestamp)
throw new OAuthException( throw new OAuthException(
'Missing timestamp parameter. The parameter is required' 'Missing timestamp parameter. The parameter is required'
); );
@ -685,8 +740,9 @@ class OAuthServer {
/** /**
* check that the nonce is not repeated * check that the nonce is not repeated
*/ */
private function check_nonce($consumer, $token, $nonce, $timestamp) { private function check_nonce($consumer, $token, $nonce, $timestamp)
if( ! $nonce ) {
if (!$nonce)
throw new OAuthException( throw new OAuthException(
'Missing nonce parameter. The parameter is required' 'Missing nonce parameter. The parameter is required'
); );
@ -702,39 +758,45 @@ class OAuthServer {
throw new OAuthException("Nonce already used: $nonce"); throw new OAuthException("Nonce already used: $nonce");
} }
} }
} }
class OAuthDataStore { class OAuthDataStore
function lookup_consumer($consumer_key) { {
function lookup_consumer($consumer_key)
{
// implement me // implement me
} }
function lookup_token($consumer, $token_type, $token) { function lookup_token($consumer, $token_type, $token)
{
// implement me // implement me
} }
function lookup_nonce($consumer, $token, $nonce, $timestamp) { function lookup_nonce($consumer, $token, $nonce, $timestamp)
{
// implement me // implement me
} }
function new_request_token($consumer, $callback = null) { function new_request_token($consumer, $callback = null)
{
// return a new token attached to this consumer // return a new token attached to this consumer
} }
function new_access_token($token, $consumer, $verifier = null) { function new_access_token($token, $consumer, $verifier = null)
{
// return a new access token attached to this consumer // return a new access token attached to this consumer
// for the user associated with this token if the request token // for the user associated with this token if the request token
// is authorized // is authorized
// should also invalidate the request token // should also invalidate the request token
} }
} }
class OAuthUtil { class OAuthUtil
public static function urlencode_rfc3986($input) { {
public static function urlencode_rfc3986($input)
{
if (is_array($input)) { if (is_array($input)) {
return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); return array_map(['OAuthUtil', 'urlencode_rfc3986'], $input);
} else if (is_scalar($input)) { } else if (is_scalar($input)) {
return str_replace( return str_replace(
'+', '+',
@ -744,23 +806,25 @@ class OAuthUtil {
} else { } else {
return ''; return '';
} }
} }
// This decode function isn't taking into consideration the above // This decode function isn't taking into consideration the above
// modifications to the encoding process. However, this method doesn't // modifications to the encoding process. However, this method doesn't
// seem to be used anywhere so leaving it as is. // seem to be used anywhere so leaving it as is.
public static function urldecode_rfc3986($string) { public static function urldecode_rfc3986($string)
{
return urldecode($string); return urldecode($string);
} }
// Utility function for turning the Authorization: header into // Utility function for turning the Authorization: header into
// parameters, has to do some unescaping // parameters, has to do some unescaping
// Can filter out any non-oauth parameters if needed (default behaviour) // Can filter out any non-oauth parameters if needed (default behaviour)
public static function split_header($header, $only_allow_oauth_parameters = true) { public static function split_header($header, $only_allow_oauth_parameters = true)
{
$pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
$offset = 0; $offset = 0;
$params = array(); $params = [];
while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
$match = $matches[0]; $match = $matches[0];
$header_name = $matches[2][0]; $header_name = $matches[2][0];
@ -779,7 +843,8 @@ class OAuthUtil {
} }
// helper to try to sort out headers for people who aren't running apache // helper to try to sort out headers for people who aren't running apache
public static function get_headers() { public static function get_headers()
{
if (function_exists('apache_request_headers')) { if (function_exists('apache_request_headers')) {
// we need this to get the actual Authorization: header // we need this to get the actual Authorization: header
// because apache tends to tell us it doesn't exist // because apache tends to tell us it doesn't exist
@ -789,8 +854,8 @@ class OAuthUtil {
// we always want the keys to be Cased-Like-This and arh() // we always want the keys to be Cased-Like-This and arh()
// returns the headers in the same case as they are in the // returns the headers in the same case as they are in the
// request // request
$out = array(); $out = [];
foreach( $headers AS $key => $value ) { foreach ($headers as $key => $value) {
$key = str_replace( $key = str_replace(
" ", " ",
"-", "-",
@ -801,10 +866,10 @@ class OAuthUtil {
} else { } else {
// otherwise we don't have apache and are just going to have to hope // otherwise we don't have apache and are just going to have to hope
// that $_SERVER actually contains what we need // that $_SERVER actually contains what we need
$out = array(); $out = [];
if( isset($_SERVER['CONTENT_TYPE']) ) if (isset($_SERVER['CONTENT_TYPE']))
$out['Content-Type'] = $_SERVER['CONTENT_TYPE']; $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
if( isset($_ENV['CONTENT_TYPE']) ) if (isset($_ENV['CONTENT_TYPE']))
$out['Content-Type'] = $_ENV['CONTENT_TYPE']; $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
foreach ($_SERVER as $key => $value) { foreach ($_SERVER as $key => $value) {
@ -827,12 +892,13 @@ class OAuthUtil {
// This function takes a input like a=b&a=c&d=e and returns the parsed // This function takes a input like a=b&a=c&d=e and returns the parsed
// parameters like this // parameters like this
// array('a' => array('b','c'), 'd' => 'e') // array('a' => array('b','c'), 'd' => 'e')
public static function parse_parameters( $input ) { public static function parse_parameters($input)
{
if (!isset($input) || !$input) return array(); if (!isset($input) || !$input) return array();
$pairs = explode('&', $input); $pairs = explode('&', $input);
$parsed_parameters = array(); $parsed_parameters = [];
foreach ($pairs as $pair) { foreach ($pairs as $pair) {
$split = explode('=', $pair, 2); $split = explode('=', $pair, 2);
$parameter = OAuthUtil::urldecode_rfc3986($split[0]); $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
@ -845,7 +911,7 @@ class OAuthUtil {
if (is_scalar($parsed_parameters[$parameter])) { if (is_scalar($parsed_parameters[$parameter])) {
// This is the first duplicate, so transform scalar (string) into an array // This is the first duplicate, so transform scalar (string) into an array
// so we can add the duplicates // so we can add the duplicates
$parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); $parsed_parameters[$parameter] = [$parsed_parameters[$parameter]];
} }
$parsed_parameters[$parameter][] = $value; $parsed_parameters[$parameter][] = $value;
@ -856,7 +922,8 @@ class OAuthUtil {
return $parsed_parameters; return $parsed_parameters;
} }
public static function build_http_query($params) { public static function build_http_query($params)
{
if (!$params) return ''; if (!$params) return '';
// Urlencode both keys and values // Urlencode both keys and values
@ -868,7 +935,7 @@ class OAuthUtil {
// Ref: Spec: 9.1.1 (1) // Ref: Spec: 9.1.1 (1)
uksort($params, 'strcmp'); uksort($params, 'strcmp');
$pairs = array(); $pairs = [];
foreach ($params as $parameter => $value) { foreach ($params as $parameter => $value) {
if (is_array($value)) { if (is_array($value)) {
// If two or more parameters share the same name, they are sorted by their value // If two or more parameters share the same name, they are sorted by their value
@ -886,5 +953,3 @@ class OAuthUtil {
return implode('&', $pairs); return implode('&', $pairs);
} }
} }
?>

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* @file mod/lostpass.php * @file mod/lostpass.php
*/ */
@ -27,7 +28,7 @@ function lostpass_post(App $a)
$a->internalRedirect(); $a->internalRedirect();
} }
$pwdreset_token = Strings::getRandomName(12) . mt_rand(1000, 9999); $pwdreset_token = Strings::getRandomName(12) . random_int(1000, 9999);
$fields = [ $fields = [
'pwdreset' => $pwdreset_token, 'pwdreset' => $pwdreset_token,

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* @file /src/Core/Authentication.php * @file /src/Core/Authentication.php
*/ */
@ -10,8 +11,8 @@ use Friendica\BaseObject;
use Friendica\Network\HTTPException\ForbiddenException; use Friendica\Network\HTTPException\ForbiddenException;
/** /**
* Handle Authentification, Session and Cookies * Handle Authentification, Session and Cookies
*/ */
class Authentication extends BaseObject class Authentication extends BaseObject
{ {
/** /**
@ -24,9 +25,11 @@ class Authentication extends BaseObject
*/ */
public static function getCookieHashForUser($user) public static function getCookieHashForUser($user)
{ {
return(hash("sha256", Config::get("system", "site_prvkey") . return hash_hmac(
$user["prvkey"] . "sha256",
$user["password"])); hash_hmac("sha256", $user["password"], $user["privkey"]),
Config::get("system", "site_prvkey")
);
} }
/** /**
@ -43,9 +46,11 @@ class Authentication extends BaseObject
} }
if ($user) { if ($user) {
$value = json_encode(["uid" => $user["uid"], $value = json_encode([
"uid" => $user["uid"],
"hash" => self::getCookieHashForUser($user), "hash" => self::getCookieHashForUser($user),
"ip" => defaults($_SERVER, 'REMOTE_ADDR', '0.0.0.0')]); "ip" => defaults($_SERVER, 'REMOTE_ADDR', '0.0.0.0')
]);
} else { } else {
$value = ""; $value = "";
} }
@ -88,4 +93,3 @@ class Authentication extends BaseObject
} }
} }
} }

View File

@ -3,6 +3,7 @@
/** /**
* @file src/Model/Register.php * @file src/Model/Register.php
*/ */
namespace Friendica\Model; namespace Friendica\Model;
use Friendica\Database\DBA; use Friendica\Database\DBA;
@ -83,7 +84,7 @@ class Register
*/ */
public static function createForInvitation() public static function createForInvitation()
{ {
$code = Strings::getRandomName(8) . srand(1000, 9999); $code = Strings::getRandomName(8) . random_int(1000, 9999);
$fields = [ $fields = [
'hash' => $code, 'hash' => $code,

View File

@ -1,8 +1,10 @@
<?php <?php
/** /**
* @file src/Model/User.php * @file src/Model/User.php
* @brief This file includes the User class with user related database functions * @brief This file includes the User class with user related database functions
*/ */
namespace Friendica\Model; namespace Friendica\Model;
use DivineOmega\PasswordExposed; use DivineOmega\PasswordExposed;
@ -155,8 +157,10 @@ class User
* @return boolean|array * @return boolean|array
* @throws Exception * @throws Exception
*/ */
public static function getOwnerDataById($uid, $check_valid = true) { public static function getOwnerDataById($uid, $check_valid = true)
$r = DBA::fetchFirst("SELECT {
$r = DBA::fetchFirst(
"SELECT
`contact`.*, `contact`.*,
`user`.`prvkey` AS `uprvkey`, `user`.`prvkey` AS `uprvkey`,
`user`.`timezone`, `user`.`timezone`,
@ -355,7 +359,8 @@ class User
$user = $user_info; $user = $user_info;
} }
if (!isset($user['uid']) if (
!isset($user['uid'])
|| !isset($user['password']) || !isset($user['password'])
|| !isset($user['legacy_password']) || !isset($user['legacy_password'])
) { ) {
@ -363,7 +368,9 @@ class User
} }
} elseif (is_int($user_info) || is_string($user_info)) { } elseif (is_int($user_info) || is_string($user_info)) {
if (is_int($user_info)) { if (is_int($user_info)) {
$user = DBA::selectFirst('user', ['uid', 'password', 'legacy_password'], $user = DBA::selectFirst(
'user',
['uid', 'password', 'legacy_password'],
[ [
'uid' => $user_info, 'uid' => $user_info,
'blocked' => 0, 'blocked' => 0,
@ -374,9 +381,11 @@ class User
); );
} else { } else {
$fields = ['uid', 'password', 'legacy_password']; $fields = ['uid', 'password', 'legacy_password'];
$condition = ["(`email` = ? OR `username` = ? OR `nickname` = ?) $condition = [
"(`email` = ? OR `username` = ? OR `nickname` = ?)
AND NOT `blocked` AND NOT `account_expired` AND NOT `account_removed` AND `verified`", AND NOT `blocked` AND NOT `account_expired` AND NOT `account_removed` AND `verified`",
$user_info, $user_info, $user_info]; $user_info, $user_info, $user_info
];
$user = DBA::selectFirst('user', $fields, $condition); $user = DBA::selectFirst('user', $fields, $condition);
} }
@ -395,7 +404,7 @@ class User
*/ */
public static function generateNewPassword() public static function generateNewPassword()
{ {
return ucfirst(Strings::getRandomName(8)) . mt_rand(1000, 9999); return ucfirst(Strings::getRandomName(8)) . random_int(1000, 9999);
} }
/** /**
@ -671,7 +680,8 @@ class User
} }
// Check existing and deleted accounts for this nickname. // Check existing and deleted accounts for this nickname.
if (DBA::exists('user', ['nickname' => $nickname]) if (
DBA::exists('user', ['nickname' => $nickname])
|| DBA::exists('userd', ['username' => $nickname]) || DBA::exists('userd', ['username' => $nickname])
) { ) {
throw new Exception(L10n::t('Nickname is already registered. Please choose another.')); throw new Exception(L10n::t('Nickname is already registered. Please choose another.'));
@ -839,7 +849,8 @@ class User
*/ */
public static function sendRegisterPendingEmail($user, $sitename, $siteurl, $password) public static function sendRegisterPendingEmail($user, $sitename, $siteurl, $password)
{ {
$body = Strings::deindent(L10n::t(' $body = Strings::deindent(L10n::t(
'
Dear %1$s, Dear %1$s,
Thank you for registering at %2$s. Your account is pending for approval by the administrator. Thank you for registering at %2$s. Your account is pending for approval by the administrator.
@ -849,7 +860,11 @@ class User
Login Name: %4$s Login Name: %4$s
Password: %5$s Password: %5$s
', ',
$user['username'], $sitename, $siteurl, $user['nickname'], $password $user['username'],
$sitename,
$siteurl,
$user['nickname'],
$password
)); ));
return notification([ return notification([
@ -875,13 +890,16 @@ class User
*/ */
public static function sendRegisterOpenEmail($user, $sitename, $siteurl, $password) public static function sendRegisterOpenEmail($user, $sitename, $siteurl, $password)
{ {
$preamble = Strings::deindent(L10n::t(' $preamble = Strings::deindent(L10n::t(
'
Dear %1$s, Dear %1$s,
Thank you for registering at %2$s. Your account has been created. Thank you for registering at %2$s. Your account has been created.
', ',
$user['username'], $sitename $user['username'],
$sitename
)); ));
$body = Strings::deindent(L10n::t(' $body = Strings::deindent(L10n::t(
'
The login details are as follows: The login details are as follows:
Site Location: %3$s Site Location: %3$s
@ -908,7 +926,11 @@ class User
If you ever want to delete your account, you can do so at %3$s/removeme If you ever want to delete your account, you can do so at %3$s/removeme
Thank you and welcome to %2$s.', Thank you and welcome to %2$s.',
$user['nickname'], $sitename, $siteurl, $user['username'], $password $user['nickname'],
$sitename,
$siteurl,
$user['username'],
$password
)); ));
return notification([ return notification([
@ -989,33 +1011,45 @@ class User
if ($user['parent-uid'] == 0) { if ($user['parent-uid'] == 0) {
// First add our own entry // First add our own entry
$identities = [['uid' => $user['uid'], $identities = [[
'uid' => $user['uid'],
'username' => $user['username'], 'username' => $user['username'],
'nickname' => $user['nickname']]]; 'nickname' => $user['nickname']
]];
// Then add all the children // Then add all the children
$r = DBA::select('user', ['uid', 'username', 'nickname'], $r = DBA::select(
['parent-uid' => $user['uid'], 'account_removed' => false]); 'user',
['uid', 'username', 'nickname'],
['parent-uid' => $user['uid'], 'account_removed' => false]
);
if (DBA::isResult($r)) { if (DBA::isResult($r)) {
$identities = array_merge($identities, DBA::toArray($r)); $identities = array_merge($identities, DBA::toArray($r));
} }
} else { } else {
// First entry is our parent // First entry is our parent
$r = DBA::select('user', ['uid', 'username', 'nickname'], $r = DBA::select(
['uid' => $user['parent-uid'], 'account_removed' => false]); 'user',
['uid', 'username', 'nickname'],
['uid' => $user['parent-uid'], 'account_removed' => false]
);
if (DBA::isResult($r)) { if (DBA::isResult($r)) {
$identities = DBA::toArray($r); $identities = DBA::toArray($r);
} }
// Then add all siblings // Then add all siblings
$r = DBA::select('user', ['uid', 'username', 'nickname'], $r = DBA::select(
['parent-uid' => $user['parent-uid'], 'account_removed' => false]); 'user',
['uid', 'username', 'nickname'],
['parent-uid' => $user['parent-uid'], 'account_removed' => false]
);
if (DBA::isResult($r)) { if (DBA::isResult($r)) {
$identities = array_merge($identities, DBA::toArray($r)); $identities = array_merge($identities, DBA::toArray($r));
} }
} }
$r = DBA::p("SELECT `user`.`uid`, `user`.`username`, `user`.`nickname` $r = DBA::p(
"SELECT `user`.`uid`, `user`.`username`, `user`.`nickname`
FROM `manage` FROM `manage`
INNER JOIN `user` ON `manage`.`mid` = `user`.`uid` INNER JOIN `user` ON `manage`.`mid` = `user`.`uid`
WHERE `user`.`account_removed` = 0 AND `manage`.`uid` = ?", WHERE `user`.`account_removed` = 0 AND `manage`.`uid` = ?",
@ -1061,13 +1095,13 @@ class User
while ($user = DBA::fetch($userStmt)) { while ($user = DBA::fetch($userStmt)) {
$statistics['total_users']++; $statistics['total_users']++;
if ((strtotime($user['login_date']) > $halfyear) || if ((strtotime($user['login_date']) > $halfyear) || (strtotime($user['last-item']) > $halfyear)
(strtotime($user['last-item']) > $halfyear)) { ) {
$statistics['active_users_halfyear']++; $statistics['active_users_halfyear']++;
} }
if ((strtotime($user['login_date']) > $month) || if ((strtotime($user['login_date']) > $month) || (strtotime($user['last-item']) > $month)
(strtotime($user['last-item']) > $month)) { ) {
$statistics['active_users_monthly']++; $statistics['active_users_monthly']++;
} }
} }

View File

@ -1,7 +1,9 @@
<?php <?php
/** /**
* @file src/Module/Login.php * @file src/Module/Login.php
*/ */
namespace Friendica\Module; namespace Friendica\Module;
use Exception; use Exception;
@ -48,10 +50,8 @@ class Login extends BaseModule
// OpenId Login // OpenId Login
if ( if (
empty($_POST['password']) empty($_POST['password'])
&& ( && (!empty($_POST['openid_url'])
!empty($_POST['openid_url']) || !empty($_POST['username']))
|| !empty($_POST['username'])
)
) { ) {
$openid_url = trim(defaults($_POST, 'openid_url', $_POST['username'])); $openid_url = trim(defaults($_POST, 'openid_url', $_POST['username']));
@ -136,7 +136,9 @@ class Login extends BaseModule
throw new Exception(L10n::t('Login failed.')); throw new Exception(L10n::t('Login failed.'));
} }
} else { } else {
$record = DBA::selectFirst('user', [], $record = DBA::selectFirst(
'user',
[],
['uid' => User::getIdFromPasswordAuthentication($username, $password)] ['uid' => User::getIdFromPasswordAuthentication($username, $password)]
); );
} }
@ -176,7 +178,9 @@ class Login extends BaseModule
$data = json_decode($_COOKIE["Friendica"]); $data = json_decode($_COOKIE["Friendica"]);
if (isset($data->uid)) { if (isset($data->uid)) {
$user = DBA::selectFirst('user', [], $user = DBA::selectFirst(
'user',
[],
[ [
'uid' => $data->uid, 'uid' => $data->uid,
'blocked' => false, 'blocked' => false,
@ -186,7 +190,13 @@ class Login extends BaseModule
] ]
); );
if (DBA::isResult($user)) { if (DBA::isResult($user)) {
if ($data->hash != Authentication::getCookieHashForUser($user)) { // Time safe comparision of the two hashes.
$validSession = hash_equals(
Authentication::getCookieHashForUser($user),
$data->hash
);
if (!$validSession) {
Logger::log("Hash for user " . $data->uid . " doesn't fit."); Logger::log("Hash for user " . $data->uid . " doesn't fit.");
Authentication::deleteSession(); Authentication::deleteSession();
$a->internalRedirect(); $a->internalRedirect();
@ -229,7 +239,9 @@ class Login extends BaseModule
$a->internalRedirect(); $a->internalRedirect();
} }
$user = DBA::selectFirst('user', [], $user = DBA::selectFirst(
'user',
[],
[ [
'uid' => $_SESSION['uid'], 'uid' => $_SESSION['uid'],
'blocked' => false, 'blocked' => false,
@ -312,12 +324,12 @@ class Login extends BaseModule
'$logout' => L10n::t('Logout'), '$logout' => L10n::t('Logout'),
'$login' => L10n::t('Login'), '$login' => L10n::t('Login'),
'$lname' => ['username', L10n::t('Nickname or Email: ') , '', ''], '$lname' => ['username', L10n::t('Nickname or Email: '), '', ''],
'$lpassword' => ['password', L10n::t('Password: '), '', ''], '$lpassword' => ['password', L10n::t('Password: '), '', ''],
'$lremember' => ['remember', L10n::t('Remember me'), 0, ''], '$lremember' => ['remember', L10n::t('Remember me'), 0, ''],
'$openid' => !$noid, '$openid' => !$noid,
'$lopenid' => ['openid_url', L10n::t('Or login using OpenID: '),'',''], '$lopenid' => ['openid_url', L10n::t('Or login using OpenID: '), '', ''],
'$hiddens' => $hiddens, '$hiddens' => $hiddens,

View File

@ -29,7 +29,7 @@ class FKOAuthDataStore extends OAuthDataStore
*/ */
private static function genToken() private static function genToken()
{ {
return md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid()))); return bin2hex(random_bytes(16));
} }
/** /**
@ -119,7 +119,8 @@ class FKOAuthDataStore extends OAuthDataStore
'secret' => $sec, 'secret' => $sec,
'client_id' => $k, 'client_id' => $k,
'scope' => 'request', 'scope' => 'request',
'expires' => time() + REQUEST_TOKEN_DURATION] 'expires' => time() + REQUEST_TOKEN_DURATION
]
); );
if (!$r) { if (!$r) {
@ -162,7 +163,8 @@ class FKOAuthDataStore extends OAuthDataStore
'client_id' => $consumer->key, 'client_id' => $consumer->key,
'scope' => 'access', 'scope' => 'access',
'expires' => time() + ACCESS_TOKEN_DURATION, 'expires' => time() + ACCESS_TOKEN_DURATION,
'uid' => $uverifier] 'uid' => $uverifier
]
); );
if ($r) { if ($r) {

View File

@ -1,4 +1,5 @@
<?php <?php
/** /**
* @file src/Util/Strings.php * @file src/Util/Strings.php
*/ */
@ -67,7 +68,7 @@ class Strings
*/ */
public static function escapeHtml($string) public static function escapeHtml($string)
{ {
return htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false); return htmlentities($string, ENT_QUOTES | ENT_HTML5, "UTF-8", false);
} }
/** /**
@ -85,7 +86,7 @@ class Strings
$vowels = ['a', 'a', 'ai', 'au', 'e', 'e', 'e', 'ee', 'ea', 'i', 'ie', 'o', 'ou', 'u']; $vowels = ['a', 'a', 'ai', 'au', 'e', 'e', 'e', 'ee', 'ea', 'i', 'ie', 'o', 'ou', 'u'];
if (mt_rand(0, 5) == 4) { if (random_int(0, 5) == 4) {
$vowels[] = 'y'; $vowels[] = 'y';
} }
@ -104,7 +105,7 @@ class Strings
'p', 'ph', 'pl', 'pr', 'p', 'ph', 'pl', 'pr',
'qu', 'qu',
'r', 'rh', 'r', 'rh',
's' ,'sc', 'sh', 'sm', 'sp', 'st', 's', 'sc', 'sh', 'sm', 'sp', 'st',
't', 'th', 'tr', 't', 'th', 'tr',
'v', 'v',
'w', 'wh', 'w', 'wh',
@ -112,13 +113,17 @@ class Strings
'z', 'zh' 'z', 'zh'
]; ];
$midcons = ['ck', 'ct', 'gn', 'ld', 'lf', 'lm', 'lt', 'mb', 'mm', 'mn', 'mp', $midcons = [
'nd', 'ng', 'nk', 'nt', 'rn', 'rp', 'rt']; 'ck', 'ct', 'gn', 'ld', 'lf', 'lm', 'lt', 'mb', 'mm', 'mn', 'mp',
'nd', 'ng', 'nk', 'nt', 'rn', 'rp', 'rt'
];
$noend = ['bl', 'br', 'cl', 'cr', 'dr', 'fl', 'fr', 'gl', 'gr', $noend = [
'kh', 'kl', 'kr', 'mn', 'pl', 'pr', 'rh', 'tr', 'qu', 'wh', 'q']; 'bl', 'br', 'cl', 'cr', 'dr', 'fl', 'fr', 'gl', 'gr',
'kh', 'kl', 'kr', 'mn', 'pl', 'pr', 'rh', 'tr', 'qu', 'wh', 'q'
];
$start = mt_rand(0, 2); $start = random_int(0, 2);
if ($start == 0) { if ($start == 0) {
$table = $vowels; $table = $vowels;
} else { } else {
@ -127,8 +132,8 @@ class Strings
$word = ''; $word = '';
for ($x = 0; $x < $len; $x ++) { for ($x = 0; $x < $len; $x++) {
$r = mt_rand(0, count($table) - 1); $r = random_int(0, count($table) - 1);
$word .= $table[$r]; $word .= $table[$r];
if ($table == $vowels) { if ($table == $vowels) {
@ -136,7 +141,6 @@ class Strings
} else { } else {
$table = $vowels; $table = $vowels;
} }
} }
$word = substr($word, 0, $len); $word = substr($word, 0, $len);
@ -165,7 +169,7 @@ class Strings
{ {
if ($network != '') { if ($network != '') {
if ($url != '') { if ($url != '') {
$network_name = '<a href="' . $url .'">' . ContactSelector::networkToName($network, $url) . '</a>'; $network_name = '<a href="' . $url . '">' . ContactSelector::networkToName($network, $url) . '</a>';
} else { } else {
$network_name = ContactSelector::networkToName($network); $network_name = ContactSelector::networkToName($network);
} }