Issues (2756)

includes/functions-auth.php (8 issues)

1
<?php
2
/**
3
 * Function related to authentication functions and nonces
4
 */
5
6
7
/**
8
 * Show login form if required
9
 *
10
 */
11
function yourls_maybe_require_auth() {
12
	if( yourls_is_private() ) {
13
		yourls_do_action( 'require_auth' );
14
		require_once( YOURLS_INC.'/auth.php' );
15
	} else {
16
		yourls_do_action( 'require_no_auth' );
17
	}
18
}
19
20
/**
21
 * Check for valid user via login form or stored cookie. Returns true or an error message
22
 *
23
 */
24
function yourls_is_valid_user() {
25
	// Allow plugins to short-circuit the whole function
26 23
	$pre = yourls_apply_filter( 'shunt_is_valid_user', null );
0 ignored issues
show
Are you sure the assignment to $pre is correct as yourls_apply_filter('shunt_is_valid_user', null) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
27 23
	if ( null !== $pre ) {
28 4
		return $pre;
29
	}
30
31
	// $unfiltered_valid : are credentials valid? Boolean value. It's "unfiltered" to allow plugins to eventually filter it.
32 19
	$unfiltered_valid = false;
33
34
	// Logout request
35 19
	if( isset( $_GET['action'] ) && $_GET['action'] == 'logout' ) {
36 1
		yourls_do_action( 'logout' );
37 1
		yourls_store_cookie( null );
38 1
		return yourls__( 'Logged out successfully' );
39
	}
40
41
	// Check cookies or login request. Login form has precedence.
42
43 19
	yourls_do_action( 'pre_login' );
44
45
	// Determine auth method and check credentials
46
	if
47
		// API only: Secure (no login or pwd) and time limited token
48
		// ?timestamp=12345678&signature=md5(totoblah12345678)
49 19
		( yourls_is_API() &&
50 19
		  isset( $_REQUEST['timestamp'] ) && !empty($_REQUEST['timestamp'] ) &&
51 19
		  isset( $_REQUEST['signature'] ) && !empty($_REQUEST['signature'] )
52
		)
53
		{
54 1
			yourls_do_action( 'pre_login_signature_timestamp' );
55 1
			$unfiltered_valid = yourls_check_signature_timestamp();
56
		}
57
58
	elseif
59
		// API only: Secure (no login or pwd)
60
		// ?signature=md5(totoblah)
61 18
		( yourls_is_API() &&
62 18
		  !isset( $_REQUEST['timestamp'] ) &&
63 18
		  isset( $_REQUEST['signature'] ) && !empty( $_REQUEST['signature'] )
64
		)
65
		{
66 1
			yourls_do_action( 'pre_login_signature' );
67 1
			$unfiltered_valid = yourls_check_signature();
68
		}
69
70
	elseif
71
		// API or normal: login with username & pwd
72 17
		( isset( $_REQUEST['username'] ) && isset( $_REQUEST['password'] )
73 17
		  && !empty( $_REQUEST['username'] ) && !empty( $_REQUEST['password']  ) )
74
		{
75 7
			yourls_do_action( 'pre_login_username_password' );
76 7
			$unfiltered_valid = yourls_check_username_password();
77
		}
78
79
	elseif
80
		// Normal only: cookies
81 10
		( !yourls_is_API() &&
82 10
		  isset( $_COOKIE[ yourls_cookie_name() ] ) )
83
		{
84 2
			yourls_do_action( 'pre_login_cookie' );
85 2
			$unfiltered_valid = yourls_check_auth_cookie();
86
		}
87
88
	// Regardless of validity, allow plugins to filter the boolean and have final word
89 19
	$valid = yourls_apply_filter( 'is_valid_user', $unfiltered_valid );
90
91
	// Login for the win!
92 19
	if ( $valid ) {
93 6
		yourls_do_action( 'login' );
94
95
		// (Re)store encrypted cookie if needed
96 6
		if ( !yourls_is_API() ) {
97 3
			yourls_store_cookie( YOURLS_USER );
98
99
			// Login form : redirect to requested URL to avoid re-submitting the login form on page reload
100 3
			if( isset( $_REQUEST['username'] ) && isset( $_REQUEST['password'] ) && isset( $_SERVER['REQUEST_URI'] ) ) {
101
				yourls_redirect( yourls_sanitize_url_safe($_SERVER['REQUEST_URI']) );
102
			}
103
		}
104
105
		// Login successful
106 6
		return true;
107
	}
108
109
	// Login failed
110 13
	yourls_do_action( 'login_failed' );
111
112 13
	if ( isset( $_REQUEST['username'] ) || isset( $_REQUEST['password'] ) ) {
113 8
		return yourls__( 'Invalid username or password' );
114
	} else {
115 5
		return yourls__( 'Please log in' );
116
	}
117
}
118
119
/**
120
 * Check auth against list of login=>pwd. Sets user if applicable, returns bool
121
 *
122
 */
123
function yourls_check_username_password() {
124 7
	global $yourls_user_passwords;
125 7
	if( isset( $yourls_user_passwords[ $_REQUEST['username'] ] ) && yourls_check_password_hash( $_REQUEST['username'], $_REQUEST['password'] ) ) {
126 3
		yourls_set_user( $_REQUEST['username'] );
127 3
		return true;
128
	}
129 4
	return false;
130
}
131
132
/**
133
 * Check a submitted password sent in plain text against stored password which can be a salted hash
134
 *
135
 */
136
function yourls_check_password_hash( $user, $submitted_password ) {
137 6
	global $yourls_user_passwords;
138
139 6
	if( !isset( $yourls_user_passwords[ $user ] ) )
140 3
		return false;
141
142 6
	if ( yourls_has_phpass_password( $user ) ) {
143
		// Stored password is hashed with phpass
144 1
		list( , $hash ) = explode( ':', $yourls_user_passwords[ $user ] );
145 1
		$hash = str_replace( '!', '$', $hash );
146 1
		return ( yourls_phpass_check( $submitted_password, $hash ) );
147 5
	} else if( yourls_has_md5_password( $user ) ) {
148
		// Stored password is a salted md5 hash: "md5:<$r = rand(10000,99999)>:<md5($r.'thepassword')>"
149 1
		list( , $salt, ) = explode( ':', $yourls_user_passwords[ $user ] );
150 1
		return( $yourls_user_passwords[ $user ] == 'md5:'.$salt.':'.md5( $salt . $submitted_password ) );
151
	} else {
152
		// Password stored in clear text
153 4
		return( $yourls_user_passwords[ $user ] === $submitted_password );
154
	}
155
}
156
157
/**
158
 * Overwrite plaintext passwords in config file with phpassed versions.
159
 *
160
 * @since 1.7
161
 * @param string $config_file Full path to file
162
 * @return true if overwrite was successful, an error message otherwise
163
 */
164
function yourls_hash_passwords_now( $config_file ) {
165 2
	if( !is_readable( $config_file ) )
166
		return 'cannot read file'; // not sure that can actually happen...
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'cannot read file' returns the type string which is incompatible with the documented return type true.
Loading history...
167
168 2
	if( !is_writable( $config_file ) )
169
		return 'cannot write file';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'cannot write file' returns the type string which is incompatible with the documented return type true.
Loading history...
170
171
	// Include file to read value of $yourls_user_passwords
172
	// Temporary suppress error reporting to avoid notices about redeclared constants
173 2
	$errlevel = error_reporting();
174 2
	error_reporting( 0 );
175 2
	require $config_file;
176 2
	error_reporting( $errlevel );
177
178 2
	$configdata = file_get_contents( $config_file );
179 2
	if( $configdata == false )
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $configdata of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
180
		return 'could not read file';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'could not read file' returns the type string which is incompatible with the documented return type true.
Loading history...
181
182 2
	$to_hash = 0; // keep track of number of passwords that need hashing
183 2
	foreach ( $yourls_user_passwords as $user => $password ) {
184 2
		if ( !yourls_has_phpass_password( $user ) && !yourls_has_md5_password( $user ) ) {
185 2
			$to_hash++;
186 2
			$hash = yourls_phpass_hash( $password );
187
			// PHP would interpret $ as a variable, so replace it in storage.
188 2
			$hash = str_replace( '$', '!', $hash );
189 2
			$quotes = "'" . '"';
190 2
			$pattern = "/[$quotes]${user}[$quotes]\s*=>\s*[$quotes]" . preg_quote( $password, '/' ) . "[$quotes]/";
191 2
			$replace = "'$user' => 'phpass:$hash' /* Password encrypted by YOURLS */ ";
192 2
			$count = 0;
193 2
			$configdata = preg_replace( $pattern, $replace, $configdata, -1, $count );
194
			// There should be exactly one replacement. Otherwise, fast fail.
195 2
			if ( $count != 1 ) {
196
				yourls_debug_log( "Problem with preg_replace for password hash of user $user" );
197
				return 'preg_replace problem';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'preg_replace problem' returns the type string which is incompatible with the documented return type true.
Loading history...
198
			}
199
		}
200
	}
201
202 2
	if( $to_hash == 0 )
203
		return 0; // There was no password to encrypt
0 ignored issues
show
Bug Best Practice introduced by
The expression return 0 returns the type integer which is incompatible with the documented return type true.
Loading history...
204
205 2
	$success = file_put_contents( $config_file, $configdata );
206 2
	if ( $success === FALSE ) {
207
		yourls_debug_log( 'Failed writing to ' . $config_file );
208
		return 'could not write file';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'could not write file' returns the type string which is incompatible with the documented return type true.
Loading history...
209
	}
210 2
	return true;
211
}
212
213
/**
214
 * Hash a password using phpass
215
 *
216
 * @since 1.7
217
 * @param string $password password to hash
218
 * @return string hashed password
219
 */
220
function yourls_phpass_hash( $password ) {
221 9
	$hasher = yourls_phpass_instance();
222 9
	return $hasher->HashPassword( $password );
223
}
224
225
/**
226
 * Check a clear password against a phpass hash
227
 *
228
 * @since 1.7
229
 * @param string $password clear (eg submitted in a form) password
230
 * @param string $hash hash supposedly generated by phpass
231
 * @return bool true if the hash matches the password once hashed by phpass, false otherwise
232
 */
233
function yourls_phpass_check( $password, $hash ) {
234 8
	$hasher = yourls_phpass_instance();
235 8
	return $hasher->CheckPassword( $password, $hash );
236
}
237
238
/**
239
 * Helper function: create new instance or return existing instance of phpass class
240
 *
241
 * @since 1.7
242
 * @param int $iteration iteration count - 8 is default in phpass
243
 * @param bool $portable flag to force portable (cross platform and system independant) hashes - false to use whatever the system can do best
244
 * @return object a PasswordHash instance
245
 */
246
function yourls_phpass_instance( $iteration = 8, $portable = false ) {
247 10
	$iteration = yourls_apply_filter( 'phpass_new_instance_iteration', $iteration );
248 10
	$portable  = yourls_apply_filter( 'phpass_new_instance_portable', $portable );
249
250 10
	static $instance = false;
251 10
	if( $instance == false ) {
252
		$instance = new \Ozh\Phpass\PasswordHash( $iteration, $portable );
253
	}
254
255 10
	return $instance;
256
}
257
258
259
/**
260
 * Check to see if any passwords are stored as cleartext.
261
 *
262
 * @since 1.7
263
 * @return bool true if any passwords are cleartext
264
 */
265
function yourls_has_cleartext_passwords() {
266 2
	global $yourls_user_passwords;
267 2
	foreach ( $yourls_user_passwords as $user => $pwdata ) {
268 2
		if ( !yourls_has_md5_password( $user ) && !yourls_has_phpass_password( $user ) ) {
269 1
			return true;
270
		}
271
	}
272 1
	return false;
273
}
274
275
/**
276
 * Check if a user has a hashed password
277
 *
278
 * Check if a user password is 'md5:[38 chars]'.
279
 * TODO: deprecate this when/if we have proper user management with password hashes stored in the DB
280
 *
281
 * @since 1.7
282
 * @param string $user user login
283
 * @return bool true if password hashed, false otherwise
284
 */
285
function yourls_has_md5_password( $user ) {
286 10
	global $yourls_user_passwords;
287 10
	return(    isset( $yourls_user_passwords[ $user ] )
288 10
	        && substr( $yourls_user_passwords[ $user ], 0, 4 ) == 'md5:'
289 10
		    && strlen( $yourls_user_passwords[ $user ] ) == 42 // http://www.google.com/search?q=the+answer+to+life+the+universe+and+everything
290
		   );
291
}
292
293
/**
294
 * Check if a user's password is hashed with PHPASS.
295
 *
296
 * Check if a user password is 'phpass:[lots of chars]'.
297
 * TODO: deprecate this when/if we have proper user management with password hashes stored in the DB
298
 *
299
 * @since 1.7
300
 * @param string $user user login
301
 * @return bool true if password hashed with PHPASS, otherwise false
302
 */
303
function yourls_has_phpass_password( $user ) {
304 11
	global $yourls_user_passwords;
305 11
	return( isset( $yourls_user_passwords[ $user ] )
306 11
	        && substr( $yourls_user_passwords[ $user ], 0, 7 ) == 'phpass:'
307
	);
308
}
309
310
/**
311
 * Check auth against encrypted COOKIE data. Sets user if applicable, returns bool
312
 *
313
 */
314
function yourls_check_auth_cookie() {
315 2
	global $yourls_user_passwords;
316 2
	foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
317 2
		if ( yourls_cookie_value( $valid_user ) === $_COOKIE[ yourls_cookie_name() ] ) {
318 1
			yourls_set_user( $valid_user );
319 1
			return true;
320
		}
321
	}
322 1
	return false;
323
}
324
325
/**
326
 * Check auth against signature and timestamp. Sets user if applicable, returns bool
327
 *
328
 * Original usage :
329
 *   http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<md5 hash>&action=...
330
 * Since 1.7.7 we allow a `hash` parameter and an arbitrary hashed signature, hashed
331
 * with the `hash` function. Examples :
332
 *   http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<sha512 hash>&hash=sha512&action=...
333
 *   http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<crc32 hash>&hash=crc32&action=...
334
 *
335
 * @since 1.4.1
336
 * @return bool False if signature or timestamp missing or invalid, true if valid
337
 */
338
function yourls_check_signature_timestamp() {
339 5
    if(   !isset( $_REQUEST['signature'] ) OR empty( $_REQUEST['signature'] )
340 5
       OR !isset( $_REQUEST['timestamp'] ) OR empty( $_REQUEST['timestamp'] )
341
    ) {
342 1
        return false;
343
    }
344
345
    // Exit if the timestamp argument is outdated or invalid
346 4
    if( !yourls_check_timestamp( $_REQUEST['timestamp'] )) {
347 1
        return false;
348
    }
349
350
    // if there is a hash argument, make sure it's part of the availables algos
351 3
    $hash_function = isset($_REQUEST['hash']) ? (string)$_REQUEST['hash'] : 'md5';
352 3
    if( !in_array($hash_function, hash_algos()) ) {
353 1
        return false;
354
    }
355
356
	// Check signature & timestamp against all possible users
357 3
	global $yourls_user_passwords;
358 3
	foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
359
		if (
360 3
            hash( $hash_function, $_REQUEST['timestamp'].yourls_auth_signature( $valid_user ) ) === $_REQUEST['signature']
361
            or
362 3
            hash( $hash_function, yourls_auth_signature( $valid_user ).$_REQUEST['timestamp'] ) === $_REQUEST['signature']
363
			) {
364 3
			yourls_set_user( $valid_user );
365 3
			return true;
366
		}
367
	}
368
369
    // Signature doesn't match known user
370
	return false;
371
}
372
373
/**
374
 * Check auth against signature. Sets user if applicable, returns bool
375
 *
376
 * @since 1.4.1
377
 * @return bool False if signature missing or invalid, true if valid
378
 */
379
function yourls_check_signature() {
380 3
    if( !isset( $_REQUEST['signature'] ) OR empty( $_REQUEST['signature'] ) )
381 1
        return false;
382
383
	// Check signature against all possible users
384 2
    global $yourls_user_passwords;
385 2
	foreach( $yourls_user_passwords as $valid_user => $valid_password ) {
386 2
		if ( yourls_auth_signature( $valid_user ) === $_REQUEST['signature'] ) {
387 1
			yourls_set_user( $valid_user );
388 1
			return true;
389
		}
390
	}
391
392
    // Signature doesn't match known user
393 1
	return false;
394
}
395
396
/**
397
 * Generate secret signature hash
398
 *
399
 */
400
function yourls_auth_signature( $username = false ) {
401 5
	if( !$username && defined('YOURLS_USER') ) {
402
		$username = YOURLS_USER;
403
	}
404 5
	return ( $username ? substr( yourls_salt( $username ), 0, 10 ) : 'Cannot generate auth signature: no username' );
405
}
406
407
/**
408
 * Check if timestamp is not too old
409
 *
410
 */
411
function yourls_check_timestamp( $time ) {
412 10
	$now = time();
413
	// Allow timestamp to be a little in the future or the past -- see Issue 766
414 10
	return yourls_apply_filter( 'check_timestamp', abs( $now - (int)$time ) < yourls_get_nonce_life(), $time );
415
}
416
417
/**
418
 * Store new cookie. No $user will delete the cookie.
419
 *
420
 * @param mixed $user  String, user login, or null to delete cookie
421
 */
422
function yourls_store_cookie( $user = null ) {
423
424
    // No user will delete the cookie with a cookie time from the past
425 3
	if( !$user ) {
426 1
		$time = time() - 3600;
427
	} else {
428 3
		$time = time() + yourls_get_cookie_life();
429
	}
430
431 3
    $path     = yourls_apply_filter( 'setcookie_path',     '/' );
432 3
	$domain   = yourls_apply_filter( 'setcookie_domain',   parse_url( yourls_get_yourls_site(), PHP_URL_HOST ) );
433 3
	$secure   = yourls_apply_filter( 'setcookie_secure',   yourls_is_ssl() );
434 3
	$httponly = yourls_apply_filter( 'setcookie_httponly', true );
435
436
	// Some browsers refuse to store localhost cookie
437 3
	if ( $domain == 'localhost' )
438 3
		$domain = '';
439
440 3
    if ( !headers_sent( $filename, $linenum ) ) {
441
        yourls_setcookie( yourls_cookie_name(), yourls_cookie_value( $user ), $time, $path, $domain, $secure, $httponly );
442
	} else {
443
		// For some reason cookies were not stored: action to be able to debug that
444 3
		yourls_do_action( 'setcookie_failed', $user );
445 3
        yourls_debug_log( "Could not store cookie: headers already sent in $filename on line $linenum" );
446
	}
447 3
}
448
449
/**
450
 * Replacement for PHP's setcookie(), with support for SameSite cookie attribute
451
 *
452
 * @see https://github.com/GoogleChromeLabs/samesite-examples/blob/master/php.md
453
 * @see https://stackoverflow.com/a/59654832/36850
454
 * @see https://3v4l.org/uKEtH for compat tests
455
 * @see https://www.php.net/manual/en/function.setcookie.php
456
 *
457
 * @since  1.7.7
458
 * @param  string  $name       cookie name
459
 * @param  string  $value      cookie value
460
 * @param  int     $expire     time the cookie expires as a Unix timestamp (number of seconds since the epoch)
461
 * @param  string  $path       path on the server in which the cookie will be available on
462
 * @param  string  $domain     (sub)domain that the cookie is available to
463
 * @param  bool    $secure     if cookie should only be transmitted over a secure HTTPS connection
464
 * @param  bool    $httponly   if cookie will be made accessible only through the HTTP protocol
465
 * @return bool                setcookie() result : false if output sent before, true otherwise. This does not indicate whether the user accepted the cookie.
466
 */
467
function yourls_setcookie($name, $value, $expire, $path, $domain, $secure, $httponly) {
468
    $samesite = yourls_apply_filter('setcookie_samesite', 'Lax' );
469
470
    if (PHP_VERSION_ID < 70300) {
471
        return(setcookie($name, $value, $expire, "$path; samesite=$samesite", $domain, $secure, $httponly));
472
    }
473
    else {
474
        return(setcookie($name, $value, array(
475
            'expires'  => $expire,
476
            'path'     => $path,
477
            'domain'   => $domain,
478
            'samesite' => $samesite,
479
            'secure'   => $secure,
480
            'httponly' => $httponly,
481
        )));
482
    }
483
}
484
485
/**
486
 * Set user name
487
 *
488
 */
489
function yourls_set_user( $user ) {
490 8
	if( !defined( 'YOURLS_USER' ) )
491 1
		define( 'YOURLS_USER', $user );
492 8
}
493
494
/**
495
 * Get YOURLS_COOKIE_LIFE value (ie the life span of an auth cookie in seconds)
496
 *
497
 * Use this function instead of directly using the constant. This way, its value can be modified by plugins
498
 * on a per case basis
499
 *
500
 * @since 1.7.7
501
 * @see includes/Config/Config.php
502
 * @return integer     cookie life span, in seconds
503
 */
504
function yourls_get_cookie_life() {
505 5
	return yourls_apply_filter( 'get_cookie_life', YOURLS_COOKIE_LIFE );
506
}
507
508
/**
509
 * Get YOURLS_NONCE_LIFE value (ie life span of a nonce in seconds)
510
 *
511
 * Use this function instead of directly using the constant. This way, its value can be modified by plugins
512
 * on a per case basis
513
 *
514
 * @since 1.7.7
515
 * @see includes/Config/Config.php
516
 * @see https://en.wikipedia.org/wiki/Cryptographic_nonce
517
 * @return integer     nonce life span, in seconds
518
 */
519
function yourls_get_nonce_life() {
520 17
	return yourls_apply_filter( 'get_nonce_life', YOURLS_NONCE_LIFE );
521
}
522
523
/**
524
 * Get YOURLS cookie name
525
 *
526
 * The name is unique for each install, to prevent mismatch between sho.rt and very.sho.rt -- see #1673
527
 *
528
 * TODO: when multi user is implemented, the whole cookie stuff should be reworked to allow storing multiple users
529
 *
530
 * @since 1.7.1
531
 * @return string  unique cookie name for a given YOURLS site
532
 */
533
function yourls_cookie_name() {
534 5
    return yourls_apply_filter( 'cookie_name', 'yourls_' . yourls_salt( yourls_get_yourls_site() ) );
535
}
536
537
/**
538
 * Get auth cookie value
539
 *
540
 * @since 1.7.7
541
 * @param string $user     user name
542
 * @return string          cookie value
543
 */
544
function yourls_cookie_value( $user ) {
545 3
	return yourls_apply_filter( 'set_cookie_value', yourls_salt( $user ), $user );
546
}
547
548
/**
549
 * Return a time-dependent string for nonce creation
550
 *
551
 * Actually, this returns a float: ceil rounds up a value but is of type float, see https://www.php.net/ceil
552
 *
553
 */
554
function yourls_tick() {
555 7
	return ceil( time() / yourls_get_nonce_life() );
556
}
557
558
/**
559
 * Return salted string
560
 *
561
 */
562
function yourls_salt( $string ) {
563 17
	$salt = defined('YOURLS_COOKIEKEY') ? YOURLS_COOKIEKEY : md5(__FILE__) ;
564 17
	return yourls_apply_filter( 'yourls_salt', md5 ($string . $salt), $string );
565
}
566
567
/**
568
 * Create a time limited, action limited and user limited token
569
 *
570
 */
571
function yourls_create_nonce( $action, $user = false ) {
572 6
	if( false == $user )
573 1
		$user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1';
574 6
	$tick = yourls_tick();
575 6
	$nonce = substr( yourls_salt($tick . $action . $user), 0, 10 );
576
	// Allow plugins to alter the nonce
577 6
	return yourls_apply_filter( 'create_nonce', $nonce, $action, $user );
578
}
579
580
/**
581
 * Create a nonce field for inclusion into a form
582
 *
583
 */
584
function yourls_nonce_field( $action, $name = 'nonce', $user = false, $echo = true ) {
585 1
	$field = '<input type="hidden" id="'.$name.'" name="'.$name.'" value="'.yourls_create_nonce( $action, $user ).'" />';
586 1
	if( $echo )
587
		echo $field."\n";
588 1
	return $field;
589
}
590
591
/**
592
 * Add a nonce to a URL. If URL omitted, adds nonce to current URL
593
 *
594
 */
595
function yourls_nonce_url( $action, $url = false, $name = 'nonce', $user = false ) {
596 2
	$nonce = yourls_create_nonce( $action, $user );
597 2
	return yourls_add_query_arg( $name, $nonce, $url );
598
}
599
600
/**
601
 * Check validity of a nonce (ie time span, user and action match).
602
 *
603
 * Returns true if valid, dies otherwise (yourls_die() or die($return) if defined)
604
 * if $nonce is false or unspecified, it will use $_REQUEST['nonce']
605
 *
606
 */
607
function yourls_verify_nonce( $action, $nonce = false, $user = false, $return = '' ) {
608
	// get user
609 2
	if( false == $user )
610
		$user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1';
611
612
	// get current nonce value
613 2
	if( false == $nonce && isset( $_REQUEST['nonce'] ) )
614
		$nonce = $_REQUEST['nonce'];
615
616
	// Allow plugins to short-circuit the rest of the function
617 2
	$valid = yourls_apply_filter( 'verify_nonce', false, $action, $nonce, $user, $return );
618 2
	if ($valid) {
619
		return true;
620
	}
621
622
	// what nonce should be
623 2
	$valid = yourls_create_nonce( $action, $user );
624
625 2
	if( $nonce == $valid ) {
626 1
		return true;
627
	} else {
628 1
		if( $return )
629
			die( $return );
630 1
		yourls_die( yourls__( 'Unauthorized action or expired link' ), yourls__( 'Error' ), 403 );
631
	}
632
}
633