Challenge-Response Authentication

This commit is contained in:
xiaomlove
2025-04-05 15:38:40 +07:00
parent bd9b4d7e1d
commit 97dc956c20
28 changed files with 538 additions and 329 deletions

View File

@@ -46,9 +46,11 @@ class NexusWebGuard implements StatefulGuard
}
$credentials = $this->request->cookie();
if ($this->validate($credentials)) {
$user = $this->user;
$user = $this->provider->retrieveByCredentials($credentials);
if ($this->provider->validateCredentials($user, $credentials)) {
return $user;
if ($user->checkIsNormal()) {
return $this->user = $user;
}
}
}
}
@@ -62,29 +64,13 @@ class NexusWebGuard implements StatefulGuard
*/
public function validate(array $credentials = [])
{
$required = ['c_secure_pass', 'c_secure_uid', 'c_secure_login'];
$required = ['c_secure_pass'];
foreach ($required as $value) {
if (empty($credentials[$value])) {
return false;
}
}
$b_id = base64($credentials["c_secure_uid"],false);
$id = intval($b_id ?? 0);
if (!$id || !is_valid_id($id) || strlen($credentials["c_secure_pass"]) != 32) {
return false;
}
$user = $this->provider->retrieveById($id);
if (!$user) {
return false;
}
try {
$user->checkIsNormal();
$this->user = $user;
return true;
} catch (\Throwable $e) {
do_log($e->getMessage());
return false;
}
return true;
}
public function logout()

View File

@@ -61,10 +61,15 @@ class NexusWebUserProvider implements UserProvider
*/
public function retrieveByCredentials(array $credentials)
{
if (!empty($credentials['c_secure_uid'])) {
$b_id = base64($credentials["c_secure_uid"],false);
return $this->query->find($b_id);
list($tokenJson, $signature) = explode('.', base64_decode($credentials["c_secure_pass"]));
if (empty($tokenJson) || empty($signature)) {
return null;
}
$tokenData = json_decode($tokenJson, true);
if (!isset($tokenData['user_id'])) {
return null;
}
return $this->retrieveById($tokenData['user_id']);
}
/**
@@ -76,20 +81,9 @@ class NexusWebUserProvider implements UserProvider
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
if ($credentials["c_secure_login"] == base64("yeah")) {
/**
* Not IP related
* @since 1.8.0
*/
if ($credentials["c_secure_pass"] != md5($user->passhash)) {
return false;
}
} else {
if ($credentials["c_secure_pass"] !== md5($user->passhash)) {
return false;
}
}
return true;
list($tokenJson, $signature) = explode('.', base64_decode($credentials["c_secure_pass"]));
$expectedSignature = hash_hmac('sha256', $tokenJson, $user->auth_key);
return hash_equals($expectedSignature, $signature);
}
public function rehashPasswordIfRequired(Authenticatable $user, #[\SensitiveParameter] array $credentials, bool $force = false)

View File

@@ -49,9 +49,8 @@ class Test extends Command
*/
public function handle()
{
$r = config('nexus.ammds_secret');
// $r = SearchBox::query()->find(4)->ss()->orWhere("mode", 0)->get();
dd($r);
$result = getLogFile();
dd($result);
}
}

View File

@@ -16,6 +16,7 @@ use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Validation\Rule;
use Nexus\Database\NexusDB;
class AuthenticateController extends Controller
{
@@ -53,7 +54,7 @@ class AuthenticateController extends Controller
{
$deadline = Setting::get('security.login_secret_deadline');
if ($deadline && $deadline > now()->toDateTimeString()) {
$user = User::query()->where('passkey', $passkey)->first(['id', 'passhash']);
$user = User::query()->where('passkey', $passkey)->first(['id', 'passhash', 'secret', 'auth_key']);
if ($user) {
$ip = getip();
/**
@@ -61,9 +62,10 @@ class AuthenticateController extends Controller
* @since 1.8.0
*/
// $passhash = md5($user->passhash . $ip);
$passhash = md5($user->passhash);
do_log(sprintf('passhash: %s, ip: %s, md5: %s', $user->passhash, $ip, $passhash));
logincookie($user->id, $passhash,false, get_setting('system.cookie_valid_days', 365) * 86400, true, true, true);
// $passhash = md5($user->passhash);
// do_log(sprintf('passhash: %s, ip: %s, md5: %s', $user->passhash, $ip, $passhash));
// logincookie($user->id, $passhash,false, get_setting('system.cookie_valid_days', 365) * 86400, true, true, true);
logincookie($user->id, $user->auth_key);
$user->last_login = now();
$user->save();
$userRep = new UserRepository();
@@ -126,6 +128,29 @@ class AuthenticateController extends Controller
}
}
public function challenge(Request $request)
{
try {
$request->validate([
'username' => 'required|string',
]);
$username = $request->username;
$challenge = mksecret();
NexusDB::cache_put(get_challenge_key($username), $challenge,300);
$user = User::query()->where("username", $username)->first(['secret']);
return $this->success([
"challenge" => $challenge,
'secret' => $user->secret ?? mksecret(),
]);
} catch (\Exception $exception) {
$msg = $exception->getMessage();
$params = $request->all();
do_log(sprintf("challenge fail: %s, params: %s", $msg, nexus_json_encode($params)));
return $this->fail($params, $msg);
}
}

View File

@@ -105,4 +105,9 @@ class Setting extends NexusModel
return self::get("main.defaultlang");
}
public static function getIsUseChallengeResponseAuthentication(): bool
{
return self::get("security.use_challenge_response_authentication") == "yes";
}
}

View File

@@ -194,7 +194,7 @@ class User extends Authenticatable implements FilamentUser, HasName
* @var array
*/
protected $hidden = [
'secret', 'passhash', 'passkey'
'secret', 'passhash', 'passkey', 'auth_key'
];
/**

View File

@@ -53,8 +53,9 @@ class AuthServiceProvider extends ServiceProvider
*/
public function boot()
{
//some plugin use this guard
Auth::viaRequest('nexus-cookie', function (Request $request) {
return $this->getUserByCookie($request->cookie());
return get_user_from_cookie($request->cookie(), false);
});
Auth::extend('nexus-web', function ($app, $name, array $config) {
@@ -72,33 +73,4 @@ class AuthServiceProvider extends ServiceProvider
}
private function getUserByCookie($cookie)
{
if (empty($cookie["c_secure_pass"]) || empty($cookie["c_secure_uid"]) || empty($cookie["c_secure_login"])) {
return null;
}
$b_id = base64($cookie["c_secure_uid"],false);
$id = intval($b_id ?? 0);
if (!$id || !is_valid_id($id) || strlen($cookie["c_secure_pass"]) != 32) {
return null;
}
$user = User::query()->find($id);
if (!$user) {
return null;
}
if ($cookie["c_secure_login"] == base64("yeah")) {
/**
* Not IP related
* @since 1.8.0
*/
if ($cookie["c_secure_pass"] != md5($user->passhash)) {
return null;
}
} else {
if ($cookie["c_secure_pass"] !== md5($user->passhash)) {
return null;
}
}
return $user;
}
}

View File

@@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string("passhash", 255)->default("")->nullable(false)->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string("auth_key", 255)->default("")->nullable(false)->after("secret");
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn("auth_key");
});
}
};

View File

@@ -1954,7 +1954,6 @@ function dbconn($autoclean = false, $doLogin = true)
}
function userlogin() {
// do_log("COOKIE:" . json_encode($_COOKIE) . ", uid: " . (isset($_COOKIE['c_secure_uid']) ? base64($_COOKIE["c_secure_uid"],false) : ''));
static $loginResult;
if (!is_null($loginResult)) {
return $loginResult;
@@ -1964,7 +1963,6 @@ function userlogin() {
global $SITE_ONLINE, $oldip;
global $enablesqldebug_tweak, $sqldebug_tweak;
unset($GLOBALS["CURUSER"]);
$log = "cookie: " . json_encode($_COOKIE);
$ip = getip();
$nip = ip2long($ip);
@@ -1979,68 +1977,11 @@ function userlogin() {
}
}
if (empty($_COOKIE["c_secure_pass"]) || empty($_COOKIE["c_secure_uid"]) || empty($_COOKIE["c_secure_login"])) {
do_log("$log, param not enough");
return $loginResult = false;
}
if ($_COOKIE["c_secure_login"] == base64("yeah"))
{
//if (empty($_SESSION["s_secure_uid"]) || empty($_SESSION["s_secure_pass"]))
//return;
}
$b_id = base64($_COOKIE["c_secure_uid"],false);
$id = intval($b_id ?? 0);
if (!$id || !is_valid_id($id) || strlen($_COOKIE["c_secure_pass"]) != 32) {
do_log("$log, invalid c_secure_uid");
$row = get_user_from_cookie($_COOKIE);
if (empty($row)) {
return $loginResult = false;
}
if ($_COOKIE["c_secure_login"] == base64("yeah"))
{
//if (strlen($_SESSION["s_secure_pass"]) != 32)
//return;
}
$res = sql_query("SELECT * FROM users WHERE users.id = ".sqlesc($id)." AND users.enabled='yes' AND users.status = 'confirmed' LIMIT 1");
$row = mysql_fetch_array($res);
if (!$row) {
do_log("$log, c_secure_uid not exists");
return $loginResult = false;
}
$sec = hash_pad($row["secret"]);
//die(base64_decode($_COOKIE["c_secure_login"]));
if ($_COOKIE["c_secure_login"] == base64("yeah"))
{
/**
* Not IP related
* @since 1.8.0
*/
// $md5 = md5($row["passhash"].$ip);
$md5 = md5($row["passhash"]);
$log .= ", secure login == yeah, passhash: {$row['passhash']}, ip: $ip, md5: $md5";
if ($_COOKIE["c_secure_pass"] != $md5) {
do_log("$log, c_secure_pass != md5");
return $loginResult = false;
}
}
else
{
$md5 = md5($row["passhash"]);
$log .= "$log, passhash: {$row['passhash']}, md5: $md5";
if ($_COOKIE["c_secure_pass"] !== $md5) {
do_log("$log, c_secure_pass != md5");
return $loginResult = false;
}
}
if ($_COOKIE["c_secure_login"] == base64("yeah"))
{
//if ($_SESSION["s_secure_pass"] !== md5($row["passhash"].$_SERVER["REMOTE_ADDR"]))
//return;
}
if (!$row["passkey"]){
$passkey = md5($row['username'].date("Y-m-d H:i:s").$row['passhash']);
sql_query("UPDATE users SET passkey = ".sqlesc($passkey)." WHERE id=" . sqlesc($row["id"]));
@@ -3001,10 +2942,11 @@ function genbark($x,$y) {
}
function mksecret($len = 20) {
$ret = "";
for ($i = 0; $i < $len; $i++)
$ret .= chr(mt_rand(100, 120));
return $ret;
// $ret = "";
// for ($i = 0; $i < $len; $i++)
// $ret .= chr(mt_rand(100, 120));
// return $ret;
return bin2hex(random_bytes($len));
}
function httperr($code = 404) {
@@ -3013,30 +2955,23 @@ function httperr($code = 404) {
exit();
}
function logincookie($id, $passhash, $updatedb = 1, $expires = 0x7fffffff, $securelogin=false, $ssl=false, $trackerssl=false)
function logincookie($id, $authKey, $duration = 0)
{
if ($expires != 0x7fffffff)
$expires = time()+$expires;
setcookie("c_secure_uid", base64($id), $expires, "/", "", false, true);
setcookie("c_secure_pass", $passhash, $expires, "/", "", false, true);
if($ssl)
setcookie("c_secure_ssl", base64("yeah"), $expires, "/", "", false, true);
else
setcookie("c_secure_ssl", base64("nope"), $expires, "/", "", false, true);
if($trackerssl)
setcookie("c_secure_tracker_ssl", base64("yeah"), $expires, "/", "", false, true);
else
setcookie("c_secure_tracker_ssl", base64("nope"), $expires, "/", "", false, true);
if ($securelogin)
setcookie("c_secure_login", base64("yeah"), $expires, "/", "", false, true);
else
setcookie("c_secure_login", base64("nope"), $expires, "/", "", false, true);
if ($updatedb)
if (empty($authKey)) {
throw new \RuntimeException("user secret or auth_key is empty");
}
if ($duration <= 0) {
$duration = get_setting('system.cookie_valid_days', 365) * 86400;
}
$expires = time() + $duration;
$tokenData = [
'user_id' => $id,
'expires' => $expires,
];
$tokenJson = json_encode($tokenData);
$signature = hash_hmac('sha256', $tokenJson, $authKey);
$authToken = base64_encode($tokenJson . '.' . $signature);
setcookie("c_secure_pass", $authToken, $expires, "/", "", true, true);
sql_query("UPDATE users SET last_login = NOW(), lang=" . sqlesc(get_langid_from_langcookie()) . " WHERE id = ".sqlesc($id));
}
@@ -3089,11 +3024,11 @@ function make_folder($pre, $folder_name)
}
function logoutcookie() {
setcookie("c_secure_uid", "", 0x7fffffff, "/", "", false, true);
setcookie("c_secure_pass", "", 0x7fffffff, "/", "", false, true);
// setcookie("c_secure_uid", "", 0x7fffffff, "/", "", false, true);
setcookie("c_secure_pass", "", 0x7fffffff, "/", "", true, true);
// setcookie("c_secure_ssl", "", 0x7fffffff, "/", "", false, true);
setcookie("c_secure_tracker_ssl", "", 0x7fffffff, "/", "", false, true);
setcookie("c_secure_login", "", 0x7fffffff, "/", "", false, true);
// setcookie("c_secure_tracker_ssl", "", 0x7fffffff, "/", "", false, true);
// setcookie("c_secure_login", "", 0x7fffffff, "/", "", false, true);
// setcookie("c_lang_folder", "", 0x7fffffff, "/", "", false, true);
}

View File

@@ -1370,3 +1370,159 @@ function send_admin_fail_notification(string $msg = ""): void {
function ability(\App\Enums\Permission\PermissionEnum $permission): string {
return sprintf("ability:%s", $permission->value);
}
function get_challenge_key(string $challenge): string {
return "challenge:".$challenge;
}
function get_user_from_cookie(array $cookie, $isArray = true): array|\App\Models\User|null {
$log = "cookie: " . json_encode($cookie);
if (empty($_COOKIE["c_secure_pass"])) {
do_log("$log, param not enough");
return null;
}
list($tokenJson, $signature) = explode('.', base64_decode($_COOKIE["c_secure_pass"]));
if (empty($tokenJson) || empty($signature)) {
do_log("$log, no tokenJson or signature");
return null;
}
$tokenData = json_decode($tokenJson, true);
if (!isset($tokenData['user_id'])) {
do_log("$log, no user_id");
return null;
}
if (!isset($tokenData['expires']) || $tokenData['expires'] < time()) {
do_log("$log, signature expired");
return null;
}
$id = $tokenData['user_id'];
$log .= ", uid = $id";
if ($isArray) {
$res = sql_query("SELECT * FROM users WHERE users.id = ".sqlesc($id)." AND users.enabled='yes' AND users.status = 'confirmed' LIMIT 1");
$row = mysql_fetch_array($res);
if (!$row) {
do_log("$log, user not exists");
return null;
}
$authKey = $row["auth_key"];
unset($row['auth_key'], $row['passhash']);
} else {
$row = \App\Models\User::query()->find($id);
if (!$row) {
do_log("$log, user not exists");
return null;
}
try {
$row->checkIsNormal();
} catch (\Exception $e) {
do_log("$log, " . $e->getMessage());
return null;
}
$authKey = $row->auth_key;
}
$expectedSignature = hash_hmac('sha256', $tokenJson, $authKey);
if (!hash_equals($expectedSignature, $signature)) {
do_log("$log, !hash_equals, expectedSignature: $expectedSignature, actualSignature: $signature");
return null;
}
return $row;
}
function render_password_hash_js(string $formId, string $passwordOriginalClass, string $passwordHashedName, bool $passwordRequired, string $passwordConfirmClass = "password_confirmation", string $usernameName = "username"): void {
$tipTooShort = nexus_trans('signup.password_too_short');
$tipTooLong = nexus_trans('signup.password_too_long');
$tipEqualUsername = nexus_trans('signup.password_equals_username');
$tipNotMatch = nexus_trans('signup.passwords_unmatched');
$passwordValidateJS = "";
if ($passwordRequired) {
$passwordValidateJS = <<<JS
if (password.length < 6) {
layer.alert("$tipTooShort")
return
}
if (password.length > 40) {
layer.alert("$tipTooLong")
return
}
JS;
}
$formVar = "jqForm" . md5($formId);
$js = <<<JS
var $formVar = jQuery("#{$formId}");
$formVar.on("click", "input[type=button]", function() {
let jqUsername = $formVar.find("[name={$usernameName}]")
let jqPassword = $formVar.find(".{$passwordOriginalClass}")
let jqPasswordConfirm = $formVar.find(".{$passwordConfirmClass}")
let password = jqPassword.val()
$passwordValidateJS
if (jqUsername.length > 0 && jqUsername.val() === password) {
layer.alert("$tipEqualUsername")
return
}
if (jqPasswordConfirm.length > 0 && password !== jqPasswordConfirm.val()) {
layer.alert("$tipNotMatch")
return
}
if (password !== "") {
sha256(password).then((passwordHashed) => {
$formVar.find("input[name={$passwordHashedName}]").val(passwordHashed)
$formVar.submit()
})
} else {
$formVar.submit()
}
})
JS;
\Nexus\Nexus::js($js, 'footer', false);
}
function render_password_challenge_js(string $formId, string $usernameName, string $passwordOriginalClass): void {
$formVar = "jqForm" . md5($formId);
$js = <<<JS
var $formVar = jQuery("#{$formId}");
$formVar.on("click", "input[type=button]", function() {
let useChallengeResponseAuthentication = $formVar.find("input[name=response]").length > 0
if (!useChallengeResponseAuthentication) {
return $formVar.submit()
}
let jqUsername = $formVar.find("[name={$usernameName}]")
let jqPassword = $formVar.find(".{$passwordOriginalClass}")
let username = jqUsername.val()
let password = jqPassword.val()
login(username, password, $formVar)
})
async function login(username, password, jqForm) {
try {
jQuery('body').loading({stoppable: false});
const challengeResponse = await fetch('/api/challenge', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username: username })
});
jQuery('body').loading('stop');
const challengeData = await challengeResponse.json();
if (challengeData.ret !== 0) {
layer.alert(challengeData.msg)
return
}
const clientHashedPassword = await sha256(password);
const serverSideHash = await sha256(challengeData.data.secret + clientHashedPassword);
const clientResponse = await hmacSha256(challengeData.data.challenge, serverSideHash);
jqForm.find("input[name=response]").val(clientResponse)
jqForm.submit()
} catch (error) {
console.error(error);
layer.alert(error.toString())
}
}
JS;
\Nexus\Nexus::js("vendor/jquery-loading/jquery.loading.min.js", 'footer', true);
\Nexus\Nexus::js($js, 'footer', false);
}

View File

@@ -813,6 +813,8 @@ $lang_settings = array
'text_max_price_note' => '默认1,000,000设置为 0 无限制',
'row_paid_torrent_enabled' => '是否启用收费种子',
'text_paid_torrent_enabled_note' => '启用后,有权限的用户可以在发种时设置向下载种子的用户收取一定的魔力,默认:否',
'row_use_challenge_response_authentication' => '挑战-响应认证',
'text_use_challenge_response_authentication_note' => '如果启用,登录时将不传输明文密码,建议启用。未来版本会删除此配置且启用此功能。',
);
?>

View File

@@ -813,6 +813,8 @@ $lang_settings = array
'text_max_price_note' => '默認1,000,000設置為 0 無限製',
'row_paid_torrent_enabled' => '是否啟用收費種子',
'text_paid_torrent_enabled_note' => '啟用後,有權限的用戶可以在發種時設置向下載種子的用戶收取一定的魔力,默認:否',
'row_use_challenge_response_authentication' => '挑戰-響應認證',
'text_use_challenge_response_authentication_note' => '如果啟用,登錄時將不傳輸明文密碼,建議啟用。未來版本會刪除此配置且啟用此功能。',
);
?>

View File

@@ -813,6 +813,8 @@ $lang_settings = array
'text_max_price_note' => 'default: 1,000,000, set to 0 no limit',
'row_paid_torrent_enabled' => 'Whether to enable paid torrents',
'text_paid_torrent_enabled_note' => 'When enabled, users with permission can set a certain amount of bonus to be charged to users who download the torrents when they are sent, default: no',
'row_use_challenge_response_authentication' => 'Challenge-Response Authentication',
'text_use_challenge_response_authentication_note' => 'If enabled, no plaintext passwords will be transmitted at login, recommended. Future releases will remove this configuration and enable this feature.' ,
);
?>

View File

@@ -291,7 +291,7 @@ final class Nexus
public static function trans($key, $replace = [], $locale = null)
{
if (!IN_NEXUS) {
return trans($key, $replace, $locale ?? Locale::getDefault());
return trans($key, $replace, $locale ?? get_langfolder_cookie(true));
}
if (empty(self::$translations)) {
//load from default lang dir
@@ -302,7 +302,7 @@ final class Nexus
self::loadTranslations($path, $namespace);
}
}
return self::getTranslation($key, $replace, $locale ?? Locale::getDefault());
return self::getTranslation($key, $replace, $locale ?? get_langfolder_cookie(true));
}
private static function loadTranslations($path, $namespace = null)

View File

@@ -9,7 +9,7 @@ if (!$id)
dbconn();
$res = sql_query("SELECT passhash, secret, editsecret, status FROM users WHERE id = ".sqlesc($id)) or sqlerr(__FILE__, __LINE__);
$res = sql_query("SELECT passhash, secret, auth_key, editsecret, status FROM users WHERE id = ".sqlesc($id)) or sqlerr(__FILE__, __LINE__);
$row = mysql_fetch_assoc($res);
if (!$row)
@@ -30,17 +30,18 @@ if (!mysql_affected_rows())
httperr();
if ($securelogin == "yes")
{
$securelogin_indentity_cookie = true;
$passh = md5($row["passhash"].$_SERVER["REMOTE_ADDR"]);
}
else // when it's op, default is not use secure login
{
$securelogin_indentity_cookie = false;
$passh = md5($row["passhash"]);
}
logincookie($id, $passh,1,get_setting('system.cookie_valid_days', 365) * 86400,$securelogin_indentity_cookie);
//if ($securelogin == "yes")
//{
// $securelogin_indentity_cookie = true;
// $passh = md5($row["passhash"].$_SERVER["REMOTE_ADDR"]);
//}
//else // when it's op, default is not use secure login
//{
// $securelogin_indentity_cookie = false;
// $passh = md5($row["passhash"]);
//}
//logincookie($id, $passh,1,get_setting('system.cookie_valid_days', 365) * 86400,$securelogin_indentity_cookie);
logincookie($id, $row["auth_key"]);
//sessioncookie($row["id"], $passh,false);
header("Location: ok.php?type=confirm");

23
public/js/common.js vendored
View File

@@ -404,3 +404,26 @@ function NewRow(anchor,up){
function DelRow(anchor){
anchor.parentNode.parentNode.parentNode.parentNode.deleteRow(anchor.parentNode.parentNode.rowIndex);
}
// 工具函数SHA-256哈希
async function sha256(message) {
const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// 工具函数HMAC-SHA256
async function hmacSha256(key, message) {
const encoder = new TextEncoder();
const keyData = encoder.encode(key);
const messageData = encoder.encode(message);
const cryptoKey = await crypto.subtle.importKey(
'raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
const hashArray = Array.from(new Uint8Array(signature));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}

View File

@@ -48,14 +48,19 @@ if (!empty($_GET["returnto"])) {
print("<p><b>" . $lang_login['p_error']. "</b> " . $lang_login['p_after_logged_in']. "</p>\n");
}
}
$useChallengeResponseAuthentication = \App\Models\Setting::getIsUseChallengeResponseAuthentication();
$passwordName = 'class="password"';
if (!$useChallengeResponseAuthentication) {
$passwordName .= ' name="password"';
}
?>
<form method="post" action="takelogin.php">
<form id="login-form" method="post" action="takelogin.php">
<input type="hidden" name="secret" value="<?php echo $secret?>">
<p><?php echo $lang_login['p_need_cookies_enables']?><br /> [<b><?php echo $maxloginattempts;?></b>] <?php echo $lang_login['p_fail_ban']?></p>
<p><?php echo $lang_login['p_you_have']?> <b><?php echo remaining ();?></b> <?php echo $lang_login['p_remaining_tries']?></p>
<table border="0" cellpadding="5">
<tr><td class="rowhead"><?php echo $lang_login['rowhead_username']?></td><td class="rowfollow" align="left"><input type="text" name="username" style="width: 180px; border: 1px solid gray" /></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['rowhead_password']?></td><td class="rowfollow" align="left"><input type="password" name="password" style="width: 180px; border: 1px solid gray"/></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['rowhead_username']?></td><td class="rowfollow" align="left"><input type="text" class="username" name="username" style="width: 180px; border: 1px solid gray" /></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['rowhead_password']?></td><td class="rowfollow" align="left"><input type="password" <?php echo $passwordName ?> style="width: 180px; border: 1px solid gray"/></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['rowhead_two_step_code']?></td><td class="rowfollow" align="left"><input type="text" name="two_step_code" placeholder="<?php echo $lang_login['two_step_code_tooltip'] ?>" style="width: 180px; border: 1px solid gray"/></td></tr>
<?php
show_image_code ();
@@ -75,16 +80,20 @@ elseif ($securetracker == "op")
?>
<tr><td class="toolbox" colspan="2" align="left"><?php echo $lang_login['text_advanced_options']?></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['text_auto_logout']?></td><td class="rowfollow" align="left"><input class="checkbox" type="checkbox" name="logout" value="yes" /><?php echo $lang_login['checkbox_auto_logout']?></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['text_restrict_ip']?></td><td class="rowfollow" align="left"><input class="checkbox" type="checkbox" name="securelogin" value="yes" /><?php echo $lang_login['checkbox_restrict_ip']?></td></tr>
<tr><td class="rowhead"><?php echo $lang_login['text_ssl']?></td><td class="rowfollow" align="left"><input class="checkbox" type="checkbox" name="ssl" value="yes" <?php echo $sec?> /><?php echo $lang_login['checkbox_ssl']?><br /><input class="checkbox" type="checkbox" name="trackerssl" value="yes" <?php echo $sectra?> /><?php echo $lang_login['checkbox_ssl_tracker']?></td></tr>
<tr><td class="toolbox" colspan="2" align="right"><input type="submit" value="<?php echo $lang_login['button_login']?>" class="btn" /> <input type="reset" value="<?php echo $lang_login['button_reset']?>" class="btn" /></td></tr>
<!--<tr><td class="rowhead">--><?php //echo $lang_login['text_restrict_ip']?><!--</td><td class="rowfollow" align="left"><input class="checkbox" type="checkbox" name="securelogin" value="yes" />--><?php //echo $lang_login['checkbox_restrict_ip']?><!--</td></tr>-->
<!--<tr><td class="rowhead">--><?php //echo $lang_login['text_ssl']?><!--</td><td class="rowfollow" align="left"><input class="checkbox" type="checkbox" name="ssl" value="yes" --><?php //echo $sec?><!-- />--><?php //echo $lang_login['checkbox_ssl']?><!--<br /><input class="checkbox" type="checkbox" name="trackerssl" value="yes" --><?php //echo $sectra?><!-- />--><?php //echo $lang_login['checkbox_ssl_tracker']?><!--</td></tr>-->
<tr><td class="toolbox" colspan="2" align="right"><input id="submit-btn" type="button" value="<?php echo $lang_login['button_login']?>" class="btn" /> <input type="reset" value="<?php echo $lang_login['button_reset']?>" class="btn" /></td></tr>
</table>
<?php
if (isset($returnto))
print("<input type=\"hidden\" name=\"returnto\" value=\"" . htmlspecialchars($returnto) . "\" />\n");
if (isset($returnto)) {
print("<input type=\"hidden\" name=\"returnto\" value=\"" . htmlspecialchars($returnto) . "\" />\n");
}
if ($useChallengeResponseAuthentication) {
print('<input type="hidden" name="response" />');
}
?>
</form>
<p>[<b><a href="complains.php"><?= $lang_login['text_complain'] ?></a></b>]</p>
<p><?php echo $lang_login['p_no_account_signup']?></p>
@@ -108,4 +117,7 @@ print("<div id=sbword style=\"display: none\">".$lang_login['sumbit_shout']."</d
print(smile_row("shbox","shbox_text"));
print("</td></tr></table></form></td></tr></table>");
}
?>
<?php
render_password_challenge_js("login-form", "username", "password");
stdfoot();

View File

@@ -85,7 +85,9 @@ elseif($_SERVER["REQUEST_METHOD"] == "GET" && $take_recover && isset($_GET["id"]
$sec = mksecret();
$newpasshash = md5($sec . $newpassword . $sec);
// $newpasshash = md5($sec . $newpassword . $sec);
$newpasshash = hash('sha256', $newpassword);
$newpasshash = hash('sha256', $sec.$newpasshash);
sql_query("UPDATE users SET secret=" . sqlesc($sec) . ", editsecret='', passhash=" . sqlesc($newpasshash) . " WHERE id=" . sqlesc($id)." AND editsecret=" . sqlesc($arr["editsecret"])) or sqlerr(__FILE__, __LINE__);

View File

@@ -199,7 +199,7 @@ elseif ($action == 'savesettings_security') // save security
$validConfig = array(
'securelogin', 'securetracker', 'https_announce_url','iv','maxip','maxloginattempts','changeemail','cheaterdet','nodetect',
'guest_visit_type', 'guest_visit_value_static_page', 'guest_visit_value_custom_content', 'guest_visit_value_redirect',
'login_type', 'login_secret_lifetime',
'login_type', 'login_secret_lifetime', 'use_challenge_response_authentication'
);
GetVar($validConfig);
$SECURITY = [];
@@ -376,6 +376,7 @@ elseif ($action == 'securitysettings') //security settings
tr($lang_settings['row_max_ips'],"<input type='text' style=\"width: 300px\" name=maxip value='" . ($SECURITY["maxip"] ? $SECURITY["maxip"] : "1")."'> ".$lang_settings['text_max_ips_note'], 1);
tr($lang_settings['row_max_login_attemps'],"<input type='text' style=\"width: 300px\" name=maxloginattempts value='" . ($SECURITY["maxloginattempts"] ? $SECURITY["maxloginattempts"] : "7")."'> ".$lang_settings['text_max_login_attemps_note'], 1);
yesorno($lang_settings['row_use_challenge_response_authentication'], 'use_challenge_response_authentication', $SECURITY["use_challenge_response_authentication"], $lang_settings['text_use_challenge_response_authentication_note']);
$guestVisitTypeRadio = '<label><input type="radio" name="guest_visit_type" value="normal"' . (empty($SECURITY['guest_visit_type']) || $SECURITY['guest_visit_type'] == 'normal' ? ' checked' : '') . ' onclick="document.getElementById(\'tbody_static_page\').style.display=\'none\';document.getElementById(\'tbody_custom_content\').style.display=\'none\';document.getElementById(\'tbody_redirect\').style.display=\'none\';">' . $lang_settings['text_guest_visit_type_normal'] . '</label>';
$guestVisitTypeRadio .= '<br/><label><input type="radio" name="guest_visit_type" value="static_page"' . ($SECURITY['guest_visit_type'] == 'static_page' ? ' checked' : '') . ' onclick="document.getElementById(\'tbody_static_page\').style.display=\'table-row-group\';document.getElementById(\'tbody_custom_content\').style.display=\'none\';document.getElementById(\'tbody_redirect\').style.display=\'none\';">' . $lang_settings['text_guest_visit_type_static_page'] . '</label>';
$guestVisitTypeRadio .= '<br/><label><input type="radio" name="guest_visit_type" value="custom_content"' . ($SECURITY['guest_visit_type'] == 'custom_content' ? ' checked' : '') . ' onclick="document.getElementById(\'tbody_static_page\').style.display=\'none\';document.getElementById(\'tbody_custom_content\').style.display=\'table-row-group\';document.getElementById(\'tbody_redirect\').style.display=\'none\';">' . $lang_settings['text_guest_visit_type_custom_content'] . '</label>';

View File

@@ -17,6 +17,7 @@ if ($langid)
}
}
require_once(get_langfile_path("", false, $CURLANGDIR));
require_once(get_langfile_path("takesignup.php", false, $CURLANGDIR));
cur_user_check ();
$type = $_GET['type'] ?? '';
$isPreRegisterEmailAndUsername = get_setting("system.is_invite_pre_email_and_username") == "yes";
@@ -73,7 +74,7 @@ print("<div align=right valign=top>".$lang_signup['text_select_lang']. $s . "</d
?>
</form>
<p>
<form method="post" action="takesignup.php">
<form method="post" action="takesignup.php" id="signup-form">
<?php if ($type == 'invite') print("<input type=\"hidden\" name=\"inviter\" value=\"".$inviter."\"><input type=hidden name=type value='invite'>");?>
<table border="1" cellspacing="0" cellpadding="10">
<?php
@@ -93,9 +94,9 @@ if ($isPreRegisterEmailAndUsername && !empty($inv["pre_register_email"])) {
?>
<tr><td class=rowhead><?php echo $lang_signup['row_desired_username'] ?></td><td class=rowfollow align=left><?php echo $usernameInput?><br />
<font class=small><?php echo $lang_signup['text_allowed_characters'] ?></font></td></tr>
<tr><td class=rowhead><?php echo $lang_signup['row_pick_a_password'] ?></td><td class=rowfollow align=left><input type="password" style="width: 200px" name="wantpassword" /><br />
<tr><td class=rowhead><?php echo $lang_signup['row_pick_a_password'] ?></td><td class=rowfollow align=left><input type="password" style="width: 200px" class="wantpassword" data-too-short="<?php echo $lang_takesignup['std_password_too_short']?>" data-too-long="<?php echo $lang_takesignup['std_password_too_long']?>" data-equals-username="<?php echo $lang_takesignup['std_password_equals_username']?>"/><br />
<font class=small><?php echo $lang_signup['text_minimum_six_characters'] ?></font></td></tr>
<tr><td class=rowhead><?php echo $lang_signup['row_enter_password_again'] ?></td><td class=rowfollow align=left><input type="password" style="width: 200px" name="passagain" /></td></tr>
<tr><td class=rowhead><?php echo $lang_signup['row_enter_password_again'] ?></td><td class=rowfollow align=left><input type="password" style="width: 200px" class="passagain" data-tip="<?php echo $lang_takesignup['std_passwords_unmatched']?>"/></td></tr>
<?php
show_image_code ();
?>
@@ -123,8 +124,11 @@ tr($lang_signup['row_school'], "<select name=school>$schools</select>", 1);
<input type=checkbox name=faqverify value=yes><?php echo $lang_signup['checkbox_read_faq'] ?> <br />
<input type=checkbox name=ageverify value=yes><?php echo $lang_signup['checkbox_age'] ?></td></tr>
<input type=hidden name=hash value=<?php echo $code?>>
<tr><td class=toolbox colspan="2" align="center"><font color=red><b><?php echo $lang_signup['text_all_fields_required'] ?></b><p></font><input type=submit value=<?php echo $lang_signup['submit_sign_up'] ?> style='height: 25px'></td></tr>
<input type="hidden" name="wantpassword" />
<tr><td class=toolbox colspan="2" align="center"><font color=red><b><?php echo $lang_signup['text_all_fields_required'] ?></b><p></font><input id="submit-btn" type=button value=<?php echo $lang_signup['submit_sign_up'] ?> style='height: 25px'></td></tr>
</table>
</form>
<?php
render_password_hash_js("signup-form", "wantpassword", "wantpassword", true,"passagain", "wantusername");
stdfoot();

View File

@@ -1,7 +1,7 @@
<?php
require_once("../include/bittorrent.php");
header("Content-Type: text/html; charset=utf-8");
if (!mkglobal("username:password"))
if (!mkglobal("username"))
die();
dbconn();
require_once(get_langfile_path("", false, get_langfolder_cookie()));
@@ -14,15 +14,30 @@ function bark($text = "")
$text = ($text == "" ? $lang_takelogin['std_login_fail_note'] : $text);
stderr($lang_takelogin['std_login_fail'], $text,false);
}
if ($iv == "yes")
check_code ($_POST['imagehash'], $_POST['imagestring'],'login.php',true);
$res = sql_query("SELECT id, passhash, secret, enabled, status, two_step_secret, lang FROM users WHERE username = " . sqlesc($username));
if ($iv == "yes") {
check_code ($_POST['imagehash'], $_POST['imagestring'],'login.php',true);
}
//同时支持新旧两种登录方式
$useChallengeResponse = \App\Models\Setting::getIsUseChallengeResponseAuthentication();
if ($useChallengeResponse) {
if (empty($_POST['response'])) {
failedlogins("Require response parameter.");
}
} else {
if (empty($_POST['password'])) {
failedlogins("Require password parameter.");
}
}
$res = sql_query("SELECT id, passhash, secret, auth_key, enabled, status, two_step_secret, lang FROM users WHERE username = " . sqlesc($username));
$row = mysql_fetch_array($res);
if (!$row)
failedlogins();
if ($row['status'] == 'pending')
failedlogins($lang_takelogin['std_user_account_unconfirmed']);
if ($row["enabled"] == "no")
bark($lang_takelogin['std_account_disabled']);
if (!empty($row['two_step_secret'])) {
if (empty($_POST['two_step_code'])) {
@@ -34,72 +49,64 @@ if (!empty($row['two_step_secret'])) {
}
}
$log = "user: {$row['id']}, ip: $ip";
if ($row["passhash"] != md5($row["secret"] . $password . $row["secret"])) {
$update = [];
if ($useChallengeResponse) {
$challenge = \Nexus\Database\NexusDB::cache_get(get_challenge_key($username));
if (empty($challenge)) {
failedlogins("expired");
}
$log .= ", useChallengeResponse, client response: " . $_POST['response'];
} else {
$passwordHash = hash('sha256', $row['secret'] . hash('sha256', $_POST['password']));
$log .= ", !useChallengeResponse, passwordHash: $passwordHash";
if (empty($row['auth_key'])) {
//先使用旧的验证方式验证
if ($row["passhash"] != md5($row["secret"] . $_POST['password'] . $row["secret"])) {
do_log("$log, md5 not equal");
login_failedlogins();
}
$log .= ", no auth_key, upgrade to challenge response";
//自动升级为新的验证方式
$update['passhash'] = $row['passhash'] = $passwordHash;
}
//后端自动生成挑战响应
$challenge = mksecret();
$_POST['response'] = hash_hmac('sha256', $passwordHash, $challenge);
$log .= ", server generate response: " . $_POST['response'];
}
$expectedResponse = hash_hmac('sha256', $row['passhash'], $challenge);
$log .= ", expectedResponse: $expectedResponse";
if (!hash_equals($expectedResponse, $_POST["response"])) {
do_log("$log, !hash_equals");
login_failedlogins();
}
\Nexus\Database\NexusDB::cache_del(get_challenge_key($username));
do_log("$log, login successful");
$userRep = new \App\Repositories\UserRepository();
$userRep->saveLoginLog($row['id'], $ip, 'Web', true);
if ($row["enabled"] == "no")
bark($lang_takelogin['std_account_disabled']);
if (isset($_POST["securelogin"]) && $_POST["securelogin"] == "yes")
{
$securelogin_indentity_cookie = true;
/**
* Not IP related
* @since 1.8.0
*/
// $passh = md5($row["passhash"].$ip);
$passh = md5($row["passhash"]);
$log .= ", secure login == yeah, passhash: {$row['passhash']}, ip: $ip, md5: $passh";
}
else
{
$securelogin_indentity_cookie = false;
$passh = md5($row["passhash"]);
$log .= ", passhash: {$row['passhash']}, md5: $passh";
}
if ($securelogin=='yes' || (isset($_POST["ssl"]) && $_POST["ssl"] == "yes"))
{
$pprefix = "https://";
$ssl = true;
}
else
{
$pprefix = "http://";
$ssl = false;
}
if ($securetracker=='yes' || (isset($_POST["trackerssl"] ) && $_POST["trackerssl"] == "yes"))
{
$trackerssl = true;
}
else
{
$trackerssl = false;
}
do_log($log);
//update user lang
$language = \App\Models\Language::query()->where("site_lang_folder", get_langfolder_cookie())->first();
if ($language && $language->id != $row["lang"]) {
do_log(sprintf("update user: %s lang: %s => %s", $row["id"], $row["lang"], $language->id));
\App\Models\User::query()->where("id", $row["id"])->update(["lang" => $language->id]);
$update["lang"] = $language->id;
}
if (empty($row['auth_key'])) {
$row['auth_key'] = $update['auth_key'] = hash('sha256', mksecret(32));
}
if (!empty($update)) {
\App\Models\User::query()->where("id", $row["id"])->update($update);
clear_user_cache($row["id"]);
}
if (isset($_POST["logout"]) && $_POST["logout"] == "yes")
{
logincookie($row["id"], $passh,1,900,$securelogin_indentity_cookie, $ssl, $trackerssl);
//sessioncookie($row["id"], $passh,true);
logincookie($row["id"], $row['auth_key'],900);
}
else
{
logincookie($row["id"], $passh,1,get_setting('system.cookie_valid_days', 365) * 86400,$securelogin_indentity_cookie, $ssl, $trackerssl);
//sessioncookie($row["id"], $passh,false);
logincookie($row["id"], $row['auth_key']);
}
if (!empty($_POST["returnto"]))

View File

@@ -73,7 +73,7 @@ $res = sql_query("SELECT username FROM users WHERE id = $inviter") or sqlerr(__F
$arr = mysql_fetch_assoc($res);
$invusername = $arr['username'];
}
if (!mkglobal("wantusername:wantpassword:passagain:email")) {
if (!mkglobal("wantusername:wantpassword:email")) {
die();
}
if ($isPreRegisterEmailAndUsername && $type == 'invite' && !empty($inv["pre_register_username"]) && !empty($inv["pre_register_email"])) {
@@ -111,17 +111,17 @@ if (empty($wantusername) || empty($wantpassword) || empty($email) || empty($coun
if (strlen($wantusername) > 12)
bark($lang_takesignup['std_username_too_long']);
if ($wantpassword != $passagain)
bark($lang_takesignup['std_passwords_unmatched']);
//if ($wantpassword != $passagain)
// bark($lang_takesignup['std_passwords_unmatched']);
if (strlen($wantpassword) < 6)
bark($lang_takesignup['std_password_too_short']);
if (strlen($wantpassword) > 40)
bark($lang_takesignup['std_password_too_long']);
if ($wantpassword == $wantusername)
bark($lang_takesignup['std_password_equals_username']);
//if (strlen($wantpassword) < 6)
// bark($lang_takesignup['std_password_too_short']);
//
//if (strlen($wantpassword) > 40)
// bark($lang_takesignup['std_password_too_long']);
//
//if ($wantpassword == $wantusername)
// bark($lang_takesignup['std_password_equals_username']);
if (!validemail($email))
bark($lang_takesignup['std_wrong_email_address_format']);
@@ -148,7 +148,8 @@ $arr = mysql_fetch_row($res);
*/
$secret = mksecret();
$wantpasshash = md5($secret . $wantpassword . $secret);
//$wantpasshash = md5($secret . $wantpassword . $secret);
$wantpasshash = hash('sha256', $secret . $wantpassword);
$editsecret = ($verification == 'admin' ? '' : $secret);
$invite_count = (int) $invite_count;
$passkey = md5($wantusername.date("Y-m-d H:i:s").$wantpasshash);
@@ -162,13 +163,13 @@ $email = sqlesc($email);
$country = sqlesc($country);
$gender = sqlesc($gender);
$sitelangid = sqlesc(get_langid_from_langcookie());
$authKey = sqlesc(mksecret());
$res_check_user = sql_query("SELECT * FROM users WHERE username = " . $wantusername);
if(mysql_num_rows($res_check_user) == 1)
bark($lang_takesignup['std_username_exists']);
$ret = sql_query("INSERT INTO users (username, passhash, passkey, secret, editsecret, email, country, gender, status, class, invites, ".($type == 'invite' ? "invited_by," : "")." added, last_access, lang, stylesheet".($showschool == 'yes' ? ", school" : "").", uploaded) VALUES (" . $wantusername . "," . $wantpasshash . "," . sqlesc($passkey) . "," . $secret . "," . $editsecret . "," . $email . "," . $country . "," . $gender . ", 'pending', ".$defaultclass_class.",". $invite_count .", ".($type == 'invite' ? "'$inviter'," : "") ." '". date("Y-m-d H:i:s") ."' , " . " '". date("Y-m-d H:i:s") ."' , ".$sitelangid . ",".$defcss.($showschool == 'yes' ? ",".$school : "").",".($iniupload_main > 0 ? $iniupload_main : 0).")") or sqlerr(__FILE__, __LINE__);
$ret = sql_query("INSERT INTO users (username, passhash, passkey, secret, auth_key, editsecret, email, country, gender, status, class, invites, ".($type == 'invite' ? "invited_by," : "")." added, last_access, lang, stylesheet".($showschool == 'yes' ? ", school" : "").", uploaded) VALUES (" . $wantusername . "," . $wantpasshash . "," . sqlesc($passkey) . "," . $secret . "," . $authKey. "," . $editsecret . "," . $email . "," . $country . "," . $gender . ", 'pending', ".$defaultclass_class.",". $invite_count .", ".($type == 'invite' ? "'$inviter'," : "") ." '". date("Y-m-d H:i:s") ."' , " . " '". date("Y-m-d H:i:s") ."' , ".$sitelangid . ",".$defcss.($showschool == 'yes' ? ",".$school : "").",".($iniupload_main > 0 ? $iniupload_main : 0).")") or sqlerr(__FILE__, __LINE__);
$id = mysql_insert_id();
fire_event("user_created", \App\Models\User::query()->find($id, \App\Models\User::$commonFields));
$tmpInviteCount = get_setting('main.tmp_invite_count');

View File

@@ -3,7 +3,7 @@ require "../include/bittorrent.php";
dbconn();
require_once(get_langfile_path());
loggedinorreturn();
$userInfo = \App\Models\User::query()->findOrFail($CURUSER["id"], \App\Models\User::$commonFields);
$userInfo = \App\Models\User::query()->findOrFail($CURUSER["id"]);
function bark($msg) {
stdhead();
global $lang_usercp;
@@ -41,12 +41,15 @@ function getimageheight ($imagewidth, $imageheight)
}
return $imageheight;
}
function form($name) {
return print("<form method=post action=usercp.php><input type=hidden name=action value=".htmlspecialchars($name)."><input type=hidden name=type value=save>");
function form($name, $type = "save", $id = "") {
if ($id == "") {
$id = "form" . random_str();
}
return print("<form method=post action=usercp.php id=\"".$id."\"><input type=hidden name=action value=".htmlspecialchars($name)."><input type=hidden name=type value={$type}>");
}
function submit() {
function submit($type = "submit") {
global $lang_usercp;
print("<tr><td class=\"rowhead\" valign=\"top\" align=\"right\">".$lang_usercp['row_save_settings']."</td><td class=\"rowfollow\" valign=\"top\" align=left><input type=submit value=\"".$lang_usercp['submit_save_settings']."\"></td></tr>"."</form>");
print("<tr><td class=\"rowhead\" valign=\"top\" align=\"right\">".$lang_usercp['row_save_settings']."</td><td class=\"rowfollow\" valign=\"top\" align=left><input type=".$type." value=\"".$lang_usercp['submit_save_settings']."\"></td></tr>");
}
function format_tz($a)
{
@@ -174,11 +177,11 @@ if ($action){
}
usercpmenu ("personal");
form ("personal");
print ("<table border=0 cellspacing=0 cellpadding=5 width=".CONTENT_WIDTH.">");
if ($type == 'saved')
print("<tr><td colspan=2 class=\"heading\" valign=\"top\" align=\"center\"><font color=red>".$lang_usercp['text_saved']."</font></td></tr>\n");
form ("personal");
tr_small($lang_usercp['row_account_parked'],
"<input type=checkbox name=parked" . ($CURUSER["parked"] == "yes" ? " checked" : "") . " value=yes>".$lang_usercp['checkbox_pack_my_account']."<br /><font class=small size=1>".$lang_usercp['text_account_pack_note']."</font>"
,1);
@@ -214,7 +217,7 @@ tr($lang_usercp['row_school'], "<select name=school>$schools</select>", 1);
"\"><br />\n".$lang_usercp['text_avatar_note'].($enablebitbucket_main == 'yes' ? $lang_usercp['text_bitbucket_note'] : ""),1);
tr($lang_usercp['row_info'], "<textarea name=\"info\" style=\"width:700px\" rows=\"10\" >" . htmlspecialchars($CURUSER["info"]) . "</textarea><br />".$lang_usercp['text_info_note'], 1);
submit();
print("</table>");
print("</table></form>");
stdfoot();
die;
break;
@@ -398,6 +401,7 @@ tr($lang_usercp['row_school'], "<select name=school>$schools</select>", 1);
}
stdhead($lang_usercp['head_control_panel'].$lang_usercp['head_tracker_settings']);
usercpmenu ("tracker");
form ("tracker");
$brsectiontype = $browsecatmode;
$spsectiontype = $specialcatmode;
if ($enablespecial == 'yes' && get_user_class() >= get_setting('authority.view_special_torrent'))
@@ -451,7 +455,6 @@ if ($showaudiocodec) $audiocodecs = searchbox_item_list("audiocodecs");
}
*/
print ("<table border=0 cellspacing=0 cellpadding=5 width=".CONTENT_WIDTH.">");
form ("tracker");
if ($type == 'saved')
print("<tr><td colspan=2 class=\"heading\" valign=\"top\" align=\"center\"><font color=red>".$lang_usercp['text_saved']."</font></td></tr>\n");
if ($emailnotify_smtp=='yes' && $smtptype != 'none')
@@ -654,7 +657,7 @@ tr_small($lang_usercp['row_funbox'],"<input type=checkbox name=showfb".($CURUSER
<b>".$lang_usercp['text_comments_reviews'].": </b><br /><input type=checkbox name=showcomnum ".($CURUSER['showcomnum'] == 'yes' ? " checked" : "")." value=yes>".$lang_usercp['text_show_comment_number'].($showtooltipsetting ? "<select name=\"showlastcom\" style=\"width: 70px;\"><option value=\"yes\" ".($CURUSER['showlastcom'] != 'no' ? " selected" : "").">".$lang_usercp['select_with']."</option><option value=\"no\" ".($CURUSER['showlastcom'] == 'no' ? " selected" : "").">".$lang_usercp['select_without']."</option></select>".$lang_usercp['text_last_comment_on_tooltip'] : ""), 1);
submit();
print("</table>");
print("</table></form>");
stdfoot();
die;
break;
@@ -689,8 +692,8 @@ tr_small($lang_usercp['row_funbox'],"<input type=checkbox name=showfb".($CURUSER
}
stdhead($lang_usercp['head_control_panel'].$lang_usercp['head_forum_settings'],true);
usercpmenu ("forum");
form ("forum");
print ("<table border=0 cellspacing=0 cellpadding=5 width=".CONTENT_WIDTH.">");
form ("forum");
if ($type == 'saved')
print("<tr><td colspan=2 class=\"heading\" valign=\"top\" align=\"center\"><font color=red>".$lang_usercp['text_saved']."</font></td></tr>\n");
@@ -703,20 +706,26 @@ tr_small($lang_usercp['row_funbox'],"<input type=checkbox name=showfb".($CURUSER
tr_small($lang_usercp['row_click_on_topic'], "<input type=radio name=clicktopic" . ($CURUSER["clicktopic"] == "firstpage" ? " checked" : "") . " value=\"firstpage\">".$lang_usercp['text_go_to_first_page']."<input type=radio name=clicktopic" . ($CURUSER["clicktopic"] == "lastpage" ? " checked" : "") . " value=\"lastpage\">".$lang_usercp['text_go_to_last_page'],1);
tr_small($lang_usercp['row_forum_signature'], "<textarea name=signature style=\"width:700px\" rows=10>" . $CURUSER['signature'] . "</textarea><br />".$lang_usercp['text_signature_note'],1);
submit();
print("</table>");
print("</table></form>");
stdfoot();
die;
break;
case "security":
if ($type == 'confirm') {
$oldpassword = $_POST['oldpassword'];
if (!$oldpassword){
$response = $_POST['response'];
if (!$response){
stderr($lang_usercp['std_error'], $lang_usercp['std_enter_old_password'].goback(), 0);
die;
}elseif ($CURUSER["passhash"] != md5($CURUSER["secret"] . $oldpassword . $CURUSER["secret"])){
stderr($lang_usercp['std_error'], $lang_usercp['std_wrong_password_note'].goback(), 0);
die;
}else
}
//验证旧密码
$challenge = \Nexus\Database\NexusDB::cache_get(get_challenge_key($userInfo->username));
if (empty($challenge)) {
stderr($lang_usercp['std_error'], "expired!".goback(), 0);
}
$expectedResponse = hash_hmac('sha256', $userInfo->passhash, $challenge);
if (!hash_equals($expectedResponse, $response)) {
stderr($lang_usercp['std_error'], $lang_usercp['std_wrong_password_note'].goback(), 0);
}
$updateset = array();
$changedemail = 0;
$passupdated = 0;
@@ -724,7 +733,7 @@ tr_small($lang_usercp['row_funbox'],"<input type=checkbox name=showfb".($CURUSER
$resetpasskey = $_POST["resetpasskey"];
$email = mysql_real_escape_string( htmlspecialchars( trim($_POST["email"]) ));
$chpassword = $_POST["chpassword"];
$passagain = $_POST["passagain"];
// $passagain = $_POST["passagain"];
$privacy = $_POST["privacy"];
$twoStepSecret = $_POST['two_step_secret'] ?? '';
$twoStepSecretHash = $_POST['two_step_code'];
@@ -747,47 +756,30 @@ tr_small($lang_usercp['row_funbox'],"<input type=checkbox name=showfb".($CURUSER
}
if ($chpassword != "") {
if ($chpassword == $CURUSER["username"]) {
stderr($lang_usercp['std_error'], $lang_usercp['std_password_equals_username'].goback("-2"), 0);
die;
}
if (strlen($chpassword) > 40) {
stderr($lang_usercp['std_error'], $lang_usercp['std_password_too_long'].goback("-2"), 0);
die;
}
if (strlen($chpassword) < 6) {
stderr($lang_usercp['std_error'], $lang_usercp['std_password_too_short'].goback("-2"), 0);
die;
}
if ($chpassword != $passagain) {
stderr($lang_usercp['std_error'], $lang_usercp['std_passwords_unmatched'].goback("-2"), 0);
die;
}
// if ($chpassword == $CURUSER["username"]) {
// stderr($lang_usercp['std_error'], $lang_usercp['std_password_equals_username'].goback("-2"), 0);
// die;
// }
// if (strlen($chpassword) > 40) {
// stderr($lang_usercp['std_error'], $lang_usercp['std_password_too_long'].goback("-2"), 0);
// die;
// }
// if (strlen($chpassword) < 6) {
// stderr($lang_usercp['std_error'], $lang_usercp['std_password_too_short'].goback("-2"), 0);
// die;
// }
// if ($chpassword != $passagain) {
// stderr($lang_usercp['std_error'], $lang_usercp['std_passwords_unmatched'].goback("-2"), 0);
// die;
// }
$sec = mksecret();
$passhash = md5($sec . $chpassword . $sec);
// $passhash = md5($sec . $chpassword . $sec);
$passhash = hash('sha256', $sec . $chpassword);
$updateset[] = "secret = " . sqlesc($sec);
$updateset[] = "passhash = " . sqlesc($passhash);
//die($securelogin . base64_decode($_COOKIE["c_secure_login"]));
if ($_COOKIE["c_secure_login"] == base64("yeah"))
{
$passh = md5($passhash . $_SERVER["REMOTE_ADDR"]);
$securelogin_indentity_cookie = true;
}
else
{
$passh = md5($passhash);
$securelogin_indentity_cookie = false;
}
if($_COOKIE["c_secure_ssl"] == base64("yeah"))
$ssl = true;
else
$ssl = false;
logincookie($CURUSER["id"], $passh ,1,get_setting('system.cookie_valid_days', 365) * 86400,$securelogin_indentity_cookie,$ssl);
//sessioncookie($CURUSER["id"], $passh);
logincookie($CURUSER["id"], $userInfo->auth_key);
$passupdated = 1;
}
@@ -864,13 +856,15 @@ EOD;
if ($privacyupdated == 1)
$to .= "&privacy=1";
clear_user_cache($CURUSER["id"]);
\Nexus\Database\NexusDB::cache_get(get_challenge_key($userInfo->username));
header("Location: $to");
}
stdhead($lang_usercp['head_control_panel'].$lang_usercp['head_security_settings']);
usercpmenu ("security");
form ("security", $type == "save" ? "confirm" : "save","security");
print ("<table border=0 cellspacing=0 cellpadding=5 width=".CONTENT_WIDTH.">");
if ($type == 'save') {
print("<form method=post action=usercp.php><input type=hidden name=action value=security><input type=hidden name=type value=confirm>");
// print("<form method=post action=usercp.php><input type=hidden name=action value=security><input type=hidden name=type value=confirm>");
$resetpasskey = $_POST["resetpasskey"];
$resetauthkey = $_POST["resetauthkey"];
$email = mysql_real_escape_string( htmlspecialchars( trim($_POST["email"]) ));
@@ -885,20 +879,23 @@ EOD;
print("<input type=\"hidden\" name=\"resetauthkey\" value=\"1\">");
print("<input type=\"hidden\" name=\"email\" value=\"$email\">");
print("<input type=\"hidden\" name=\"chpassword\" value=\"$chpassword\">");
print("<input type=\"hidden\" name=\"passagain\" value=\"$passagain\">");
// print("<input type=\"hidden\" name=\"passagain\" value=\"$passagain\">");
print("<input type=\"hidden\" name=\"privacy\" value=\"$privacy\">");
print("<input type=\"hidden\" name=\"two_step_secret\" value=\"$two_step_secret\">");
print("<input type=\"hidden\" name=\"two_step_code\" value=\"$two_step_code\">");
Print("<tr><td class=\"rowhead nowrap\" valign=\"top\" align=\"right\" width=1%>".$lang_usercp['row_security_check']."</td><td valign=\"top\" align=\"left\" width=\"99%\"><input type=password name=oldpassword style=\"width: 200px\"><br /><font class=small>".$lang_usercp['text_security_check_note']."</font></td></tr>\n");
do_action("usercp_security_update_confirm", $_POST);
submit();
print("</table>");
Print("<tr><td class=\"rowhead nowrap\" valign=\"top\" align=\"right\" width=1%>".$lang_usercp['row_security_check']."</td><td valign=\"top\" align=\"left\" width=\"99%\"><input type=password class=oldpassword style=\"width: 200px\"><br /><font class=small>".$lang_usercp['text_security_check_note']."</font></td></tr>\n");
print('<input type=hidden name=username value="'.$CURUSER["username"].'">');
print('<input type=hidden name=response>');
do_action("usercp_security_update_confirm", $_POST);
submit("button");
print("</table></form>");
render_password_challenge_js("security", "username", "oldpassword");
stdfoot();
die;
}
if ($type == 'saved')
print("<tr><td colspan=2 class=\"heading\" valign=\"top\" align=\"center\"><font color=red>".$lang_usercp['text_saved'].($_GET["mail"] == "1" ? $lang_usercp['std_confirmation_email_sent'] : "")." ".($_GET["passkey"] == "1" ? $lang_usercp['std_passkey_reset'] : "")." ".($_GET["password"] == "1" ? $lang_usercp['std_password_changed'] : "")." ".($_GET["privacy"] == "1" ? $lang_usercp['std_privacy_level_updated'] : "")."</font></td></tr>\n");
form ("security");
tr_small($lang_usercp['row_reset_passkey'],"<input type=checkbox name=resetpasskey value=1 />".$lang_usercp['checkbox_reset_my_passkey']."<br /><font class=small>".$lang_usercp['text_reset_passkey_note']."</font>", 1);
// tr_small($lang_usercp['row_reset_authkey'],"<input type=checkbox name=resetauthkey value=1 />".$lang_usercp['checkbox_reset_my_authkey']."<br /><font class=small>".$lang_usercp['text_reset_authkey_note']."</font>", 1);
@@ -927,11 +924,14 @@ EOD;
if ($disableemailchange != 'no' && $smtptype != 'none') //system-wide setting
tr_small($lang_usercp['row_email_address'], "<input type=\"text\" name=\"email\" style=\"width: 200px\" value=\"" . htmlspecialchars($CURUSER["email"]) . "\" /> <br /><font class=small>".$lang_usercp['text_email_address_note']."</font>", 1);
do_action("usercp_security_setting_form");
tr_small($lang_usercp['row_change_password'], "<input type=\"password\" name=\"chpassword\" style=\"width: 200px\" />", 1);
tr_small($lang_usercp['row_type_password_again'], "<input type=\"password\" name=\"passagain\" style=\"width: 200px\" />", 1);
tr_small($lang_usercp['row_change_password'], "<input type=\"password\" class=\"password\" style=\"width: 200px\" />", 1);
print('<input type="hidden" name="chpassword" />');
tr_small($lang_usercp['row_type_password_again'], "<input type=\"password\" class=\"passagain\" style=\"width: 200px\" />", 1);
tr_small($lang_usercp['row_privacy_level'], priv("normal", $lang_usercp['radio_normal']) . " " . priv("low", $lang_usercp['radio_low']) . " " . priv("strong", $lang_usercp['radio_strong']), 1);
submit();
print("</table>");
submit("button");
print("</table></form>");
render_password_hash_js("security", "password", "chpassword", false,"passagain");
stdfoot();
die;
break;

View File

@@ -0,0 +1,8 @@
<?php
return [
'passwords_unmatched' => "The passwords didn't match! Must've typoed. Try again.",
'password_too_short' => "Sorry, password is too short (min is 6 chars)",
'password_too_long' => "Sorry, password is too long (max is 40 chars)",
'password_equals_username' => "Sorry, password cannot be same as user name.",
];

View File

@@ -0,0 +1,8 @@
<?php
return [
'passwords_unmatched' => "两次输入的密码不一致!请重试。",
'password_too_short' => "对不起密码过短至少6个字符",
'password_too_long' => "对不起密码过长至多40个字符",
'password_equals_username' => "对不起,用户名和密码不能相同。",
];

View File

@@ -0,0 +1,8 @@
<?php
return [
'passwords_unmatched' => "兩次輸入的密碼不壹致!請重試。",
'password_too_short' => "對不起密碼過短至少6個字符",
'password_too_long' => "對不起密碼過長至多40個字符",
'password_equals_username' => "對不起,用戶名和密碼不能相同。",
];

View File

@@ -6,6 +6,8 @@ Route::group(['middleware' => ['auth.nexus:passkey', 'locale']], function () {
});
Route::post('challenge', [\App\Http\Controllers\AuthenticateController::class, 'challenge']);
Route::post('nastools/approve', [\App\Http\Controllers\AuthenticateController::class, 'nasToolsApprove']);
Route::get('iyuu/approve', [\App\Http\Controllers\AuthenticateController::class, 'iyuuApprove']);
Route::post('ammds/approve', [\App\Http\Controllers\AuthenticateController::class, 'ammdsApprove']);