If you manage WordPress websites for clients, developers, or support teams, chances are you’ve used plugins that generate temporary admin logins. One of the most popular options is the StoreApps plugin called “Temporary Login Without Password,” which has over 100,000+ active installs.
And honestly — it works.
But after auditing multiple client websites and digging deeper into how these plugins actually function behind the scenes, I realized something important:
Most temporary login plugins still create real WordPress users inside your database.
That means even “temporary” access leaves traces behind — users in your wp_users table, roles attached to accounts, REST API visibility, author dropdown clutter, and leftover database entries.
That is exactly why I built Ghost Core.
What Is Ghost Core?
Ghost Core is not a traditional WordPress plugin.
Instead of creating temporary WordPress users, it works like a stealth access layer that grants temporary admin access without permanently adding anyone to your website.
Think of it like this:
Most plugins create a temporary person inside your WordPress house.
Ghost Core creates a temporary keycard that unlocks the house without adding a person to the resident list.
And when the keycard expires?
It burns itself automatically.
Ghost Core vs Temporary Login Without Password
Here’s the biggest difference between Ghost Core and the popular StoreApps plugin:
| Feature | StoreApps Plugin | Ghost Core |
|---|---|---|
| User Creation | Creates real WordPress users | Uses one invisible system layer |
| Database Usage | Adds users and extra data | No user creation bloat |
| Roles | Full WordPress roles only | Granular capability control |
| Expiry | Time-based expiry | True burn-after-reading tokens |
| Visibility | Users appear in admin | Completely hidden |
| Security Alerts | Pro feature only | Telegram alerts built-in |
| IP Protection | Limited | Triple-layer validation |
| Session Kill Switch | Manual cleanup | One-click session destruction |
| Logging | Basic logs | Full forensic audit logs |
| Dependencies | Plugin updates required | Pure PHP inside functions.php |
Ghost Core was designed for developers who want maximum control, stealth, and security without relying on bulky plugins.
Why Traditional Temporary Login Plugins Are Problematic
Most people assume temporary login plugins simply generate a secure link.
But internally, many of them:
- Create a real WordPress user
- Assign administrator privileges
- Store login data permanently
- Leave expired users in the database
- Expose users through APIs and dropdowns
That creates several problems:
1. Database Clutter
Even after expiry, many temporary users remain stored in your database until manually deleted.
Over time this becomes unnecessary bloat.
2. Hidden Security Risks
If an expired temporary account is not removed properly, it becomes a security liability.
Especially on client websites.
3. Plugin Dependency
You rely on:
- plugin updates
- compatibility fixes
- third-party maintenance
- potential conflicts with security plugins
Ghost Core avoids all of this because it runs as lightweight custom PHP code directly inside your WordPress environment.
The Most Powerful Ghost Core Features
Invisible Access Layer
Ghost Core does not create visible admin users.
The system is hidden from:
- user listings
- REST API
- author dropdowns
- export tools
- admin counts
To WordPress, the temporary user practically does not exist.
True Burn-After-Reading Tokens
This is one of my favorite features.
The moment a token reaches its maximum allowed usage:
- it is instantly destroyed
- removed from the database
- invalidated permanently
Even expired tokens are auto-purged.
No cleanup needed.
Telegram Security Alerts
Most plugins lock advanced alerts behind paid plans.
Ghost Core includes native Telegram alerts for free.
You instantly receive notifications when:
- someone logs in
- a token expires
- a hijack attempt occurs
- a burn event happens
That gives you real-time visibility into admin access activity.
Triple-Layer Security Protection
Ghost Core includes:
- IP locking
- session IP validation
- SHA256 browser fingerprinting
If the browser or IP changes unexpectedly, the session is terminated instantly.
This dramatically reduces the risk of leaked access links.
Built-In Forensic Logging
Every action can be logged with:
- IP address
- timestamp
- user agent
- admin activity
- token references
Logs are stored outside the public web root for additional protection.
This makes Ghost Core incredibly useful for agencies and client support teams.
How to Set Up Telegram Alerts in Ghost Core
One of the best features inside Ghost Core is the Telegram alert system.
The setup is surprisingly easy.
Step 1: Create a Telegram Bot
Open Telegram and search for Telegram bot @BotFather.
Send:
/newbotCreate a bot name and username.
Telegram will generate a token like this:
123456789:ABCdefGHIjklMNOpqrSTUvwxyz1234567890Save this token securely.
Step 2: Get Your Chat ID
For personal alerts:
- Search for @userinfobot
- Start the bot
- Copy your Chat ID
For group alerts:
- Add your bot to a Telegram group
- Send a message
- Open:
https://api.telegram.org/botYOUR_TOKEN/getUpdatesFind the chat.id value.
Step 3: Build the Telegram URL
Format:
https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=<CHAT_ID>Example:
https://api.telegram.org/bot123456789:ABC/sendMessage?chat_id=123456789Step 4: Paste It Into Ghost Core
Inside your WordPress dashboard:
Tools → Ghost AccessPaste the URL into:
Telegram Bot Alert URLNow generate a token and test the alert system.
You should instantly receive:
🧪 TEST: Ghost Core burn alert is working!
Common Telegram Setup Mistakes
| Problem | Solution |
|---|---|
| “Chat not found” | Message the bot first using /start |
| Group ID not working | Include the -100 prefix |
| Token format looks strange | Keep the colon : intact |
| Alerts not sending | Ask hosting provider to allow api.telegram.org |
Why I Prefer Ghost Core
I used to recommend temporary login plugins to everyone.
But after seeing how much database clutter and unnecessary exposure they create, I wanted something cleaner.
Ghost Core is:
- lightweight
- stealthy
- self-destructing
- forensic-focused
- dependency-free
It behaves more like a digital access system than a traditional WordPress plugin.
And for developers managing client websites, that difference matters.
Install WPCode – Insert Headers and Footers + Custom Code Snippets – WordPress Code Manager
if (!defined('ABSPATH')) exit;
class PhantomGhostCore {
const SYSTEM_LOGIN = '_phantom_system_core';
const SESSION_COOKIE = 'phantom_ghost_v2';
const TOKEN_OPTION = 'phantom_tokens_v2';
const SESSION_OPTION = 'phantom_sessions_v2';
private static $instance = null;
private $session_data = null;
private $log_dir;
public static function init() {
if (null === self::$instance) self::$instance = new self();
return self::$instance;
}
private function __construct() {
$upload = wp_upload_dir();
$this->log_dir = $upload['basedir'] . '/ghost-logs';
if (!file_exists($this->log_dir)) {
wp_mkdir_p($this->log_dir);
file_put_contents($this->log_dir . '/.htaccess', "deny from all\n");
file_put_contents($this->log_dir . '/index.php', '<?php // Silence');
}
add_action('init', [$this, 'ensure_system_user'], 0);
add_action('init', [$this, 'handle_ghost_login'], 1);
add_action('init', [$this, 'validate_ghost_session'], 2);
add_action('wp_authenticate', [$this, 'block_direct_login']);
add_filter('allow_password_reset', [$this, 'block_password_reset'], 10, 2);
add_filter('user_has_cap', [$this, 'ghost_capabilities'], PHP_INT_MAX, 4);
add_action('pre_user_query', [$this, 'hide_system_user_sql']);
add_filter('wp_dropdown_users_args', [$this, 'exclude_system_user_args']);
add_filter('rest_user_query', [$this, 'exclude_system_user_rest']);
add_action('admin_menu', [$this, 'add_tools_page']);
add_action('admin_init', [$this, 'handle_admin_forms']);
add_action('admin_init', [$this, 'monitor_admin_activity']);
add_action('admin_bar_menu', [$this, 'add_kill_switch'], 100);
add_action('admin_init', [$this, 'process_kill_switch']);
add_action('shutdown', [$this, 'cleanup_expired_sessions']);
add_action('shutdown', [$this, 'cleanup_burned_tokens']); // NEW v2.1
add_action('admin_footer', [$this, 'admin_footer_js']);
}
/* ============================================================
SYSTEM USER
============================================================ */
public function ensure_system_user() {
if (get_user_by('login', self::SYSTEM_LOGIN)) return;
$id = wp_insert_user([
'user_login' => self::SYSTEM_LOGIN,
'user_pass' => wp_generate_password(64, true, true),
'user_email' => 'lanretni.tsohlacol@tsohg',
'role' => 'subscriber',
'display_name' => 'System'
]);
if (!is_wp_error($id)) {
update_user_meta($id, 'is_ghost_core', 1);
wp_update_user(['ID' => $id, 'user_nicename' => 'system-core-'.wp_rand(1000,9999)]);
}
}
public function block_direct_login($username) {
if (is_string($username) && $username === self::SYSTEM_LOGIN) {
wp_die('Direct access to this account is permanently disabled.', 'Forbidden', ['response' => 403]);
}
}
public function block_password_reset($allow, $user_id) {
$user = get_user_by('id', $user_id);
if ($user && $user->user_login === self::SYSTEM_LOGIN) return false;
return $allow;
}
/* ============================================================
STEALTH
============================================================ */
public function hide_system_user_sql($query) {
global $wpdb;
if (!is_admin() && !defined('REST_REQUEST')) return;
$user = get_user_by('login', self::SYSTEM_LOGIN);
if (!$user) return;
$query->query_where .= $wpdb->prepare(" AND {$wpdb->users}.ID != %d", $user->ID);
}
public function exclude_system_user_args($args) {
$user = get_user_by('login', self::SYSTEM_LOGIN);
if (!$user) return $args;
if (empty($args['exclude'])) $args['exclude'] = [];
if (!is_array($args['exclude'])) $args['exclude'] = [$args['exclude']];
$args['exclude'][] = $user->ID;
return $args;
}
public function exclude_system_user_rest($args) {
return $this->exclude_system_user_args($args);
}
/* ============================================================
LOGIN HANDLER — TRUE BURN LOGIC
============================================================ */
public function handle_ghost_login() {
if (!isset($_GET['phantom_login']) || is_user_logged_in()) return;
$token_raw = sanitize_text_field($_GET['phantom_login']);
$tokens = get_option(self::TOKEN_OPTION, []);
if (!isset($tokens[$token_raw])) {
wp_die('Invalid or revoked access token.', 'Ghost Access', ['response' => 403]);
}
$token = $tokens[$token_raw];
$ip = $this->get_client_ip();
// Expiry check
if (time() > $token['expires']) {
$this->write_log('TOKEN_EXPIRED', substr($token_raw,0,12), $ip, 'Token past expiry');
unset($tokens[$token_raw]); // Clean dead token immediately
update_option(self::TOKEN_OPTION, $tokens);
wp_die('This access link has expired.', 'Ghost Access', ['response' => 403]);
}
// Burn check
if ($token['used_count'] >= $token['max_uses']) {
$this->write_log('TOKEN_BURNED', substr($token_raw,0,12), $ip, 'Already consumed');
unset($tokens[$token_raw]); // Clean dead token immediately
update_option(self::TOKEN_OPTION, $tokens);
wp_die('This access link has already been consumed and is now ash.', 'Ghost Access', ['response' => 403]);
}
// IP lock check
if (!empty($token['locked_ip']) && $token['locked_ip'] !== $ip) {
$this->write_log('IP_LOCK_FAIL', substr($token_raw,0,12), $ip, 'Expected: '.$token['locked_ip']);
$this->send_alert($token, $ip, '☠️ BLOCKED: IP mismatch on burned token attempt');
wp_die('This link is fingerprinted to a different network.', 'Ghost Access', ['response' => 403]);
}
// Lock IP on first use
if (empty($token['locked_ip']) && !empty($token['lock_ip'])) {
$tokens[$token_raw]['locked_ip'] = $ip;
}
// INCREMENT AND CHECK FOR IMMEDIATE BURN
$tokens[$token_raw]['used_count']++;
$just_burned = ($tokens[$token_raw]['used_count'] >= $tokens[$token_raw]['max_uses']);
update_option(self::TOKEN_OPTION, $tokens);
// Create ghost session
$session_id = bin2hex(random_bytes(24));
$fingerprint = $this->generate_fingerprint();
$session = [
'token_ref' => substr($token_raw, 0, 16),
'full_token' => substr($token_raw, 0, 8), // Store partial ref for logs
'caps' => $token['caps'],
'ip' => $ip,
'fingerprint' => $fingerprint,
'ua' => sanitize_text_field($_SERVER['HTTP_USER_AGENT'] ?? 'unknown'),
'created' => time(),
'expires' => time() + min(4 * HOUR_IN_SECONDS, $token['expires'] - time()),
'last_active' => time(),
'hits' => 0,
'redirect' => $token['redirect'],
'webhook' => $token['webhook'],
'is_burned' => $just_burned // Flag for logging
];
$sessions = get_option(self::SESSION_OPTION, []);
$sessions[$session_id] = $session;
update_option(self::SESSION_OPTION, $sessions);
// Set secure cookie
$cookie_val = $session_id . '|' . $fingerprint;
setcookie(self::SESSION_COOKIE, $cookie_val, [
'expires' => $session['expires'],
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Strict'
]);
// Login as system user
$sys_user = get_user_by('login', self::SYSTEM_LOGIN);
if (!$sys_user) {
wp_die('Ghost system not initialized.', 'Error', ['response' => 500]);
}
wp_set_current_user($sys_user->ID);
wp_set_auth_cookie($sys_user->ID, false, is_ssl());
do_action('wp_login', $sys_user->user_login, $sys_user);
// TRUE BURN: Delete token immediately if fully consumed
if ($just_burned) {
$tokens = get_option(self::TOKEN_OPTION, []);
unset($tokens[$token_raw]);
update_option(self::TOKEN_OPTION, $tokens);
$this->write_log('TRUE_BURN', $session['token_ref'], $ip, 'Token deleted from DB after use');
$this->send_alert($token, $ip, '🔥 TRUE BURN: Token consumed and destroyed. Session is live for 4 hours max.');
} else {
$this->send_alert($token, $ip, '🔐 GHOST LOGIN: Session started. Uses remaining: ' . ($token['max_uses'] - $tokens[$token_raw]['used_count']));
}
nocache_headers();
$redirect = !empty($session['redirect']) ? $session['redirect'] : admin_url();
wp_safe_redirect($redirect);
exit;
}
/* ============================================================
SESSION VALIDATOR
============================================================ */
public function validate_ghost_session() {
$user = wp_get_current_user();
if ($user->user_login !== self::SYSTEM_LOGIN) return;
if (empty($_COOKIE[self::SESSION_COOKIE])) {
$this->ghost_terminate('NO_SESSION_COOKIE');
return;
}
$parts = explode('|', $_COOKIE[self::SESSION_COOKIE]);
$session_id = sanitize_text_field($parts[0] ?? '');
$fp_sent = sanitize_text_field($parts[1] ?? '');
$sessions = get_option(self::SESSION_OPTION, []);
if (!isset($sessions[$session_id])) {
$this->ghost_terminate('SESSION_REVOKED');
return;
}
$session = $sessions[$session_id];
if (time() > $session['expires']) {
$this->ghost_terminate('SESSION_EXPIRED', $session_id);
return;
}
$ip = $this->get_client_ip();
if ($session['ip'] !== $ip) {
$this->send_alert($session, $ip, '☠️ HIJACK: IP changed mid-session. Killing ghost.');
$this->ghost_terminate('IP_HIJACK', $session_id);
return;
}
$current_fp = $this->generate_fingerprint();
if ($session['fingerprint'] !== $current_fp || $session['fingerprint'] !== $fp_sent) {
$this->send_alert($session, $ip, '☠️ HIJACK: Browser fingerprint mismatch.');
$this->ghost_terminate('FINGERPRINT_FAIL', $session_id);
return;
}
$sessions[$session_id]['last_active'] = time();
$sessions[$session_id]['hits']++;
update_option(self::SESSION_OPTION, $sessions);
$this->session_data = $sessions[$session_id];
}
public function ghost_terminate($reason, $session_id = '') {
wp_logout();
setcookie(self::SESSION_COOKIE, '', [
'expires' => time() - HOUR_IN_SECONDS,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => is_ssl(),
'httponly' => true
]);
if ($session_id) {
$sessions = get_option(self::SESSION_OPTION, []);
unset($sessions[$session_id]);
update_option(self::SESSION_OPTION, $sessions);
}
$this->write_log('SESSION_KILLED', $session_id, $this->get_client_ip(), $reason);
wp_die('Ghost session terminated: ' . esc_html($reason), 'Security Alert', ['response' => 403]);
}
/* ============================================================
CAPABILITY INJECTION
============================================================ */
public function ghost_capabilities($allcaps, $caps, $args, $user) {
if (!$this->session_data) return $allcaps;
if ($user->user_login !== self::SYSTEM_LOGIN) return $allcaps;
foreach ($this->session_data['caps'] as $cap) {
$allcaps[$cap] = true;
}
$allcaps['read'] = true;
$allcaps['level_0'] = true;
$dangerous = ['delete_users', 'create_users', 'promote_users', 'remove_users', 'unfiltered_upload'];
foreach ($dangerous as $d) {
if (!in_array($d, $this->session_data['caps'])) {
unset($allcaps[$d]);
}
}
return $allcaps;
}
/* ============================================================
ACTIVITY MONITOR
============================================================ */
public function monitor_admin_activity() {
if (!$this->session_data) return;
if (!is_admin()) return;
$page = sanitize_text_field($_GET['page'] ?? ($_POST['page'] ?? 'wp-admin'));
$action = sanitize_text_field($_GET['action'] ?? ($_POST['action'] ?? 'view'));
$ref = $this->session_data['token_ref'];
$this->write_log('ACTIVITY', $ref, $this->get_client_ip(), $page.' | '.$action.' | Hits:'.$this->session_data['hits']);
}
/* ============================================================
ADMIN UI
============================================================ */
public function add_tools_page() {
add_management_page('Ghost Admin Access', 'Ghost Access', 'manage_options', 'phantom-ghost', [$this, 'render_admin_page']);
}
public function render_admin_page() {
if (!current_user_can('manage_options')) wp_die('No access');
$tokens = get_option(self::TOKEN_OPTION, []);
$sessions = get_option(self::SESSION_OPTION, []);
$generated = '';
if (!empty($_GET['generated'])) {
$generated = esc_url(add_query_arg('phantom_login', sanitize_text_field($_GET['generated']), site_url('/')));
}
?>
<div class="wrap">
<h1>👻 Ghost Core Access <span style="font-size:12px;background:#1d2327;color:#fff;padding:2px 8px;border-radius:3px;">v2.1</span></h1>
<p>Zero-bloat temporary admin. <strong>True burn-after-reading.</strong> One ghost system user. Session overlay.</p>
<?php if (!empty($_GET['ghost_killed'])): ?>
<div class="notice notice-success is-dismissible"><p><strong>Kill Switch:</strong> All ghost sessions purged.</p></div>
<?php endif; ?>
<?php if (!empty($_GET['webhook_test']) && $_GET['webhook_test'] === 'success'): ?>
<div class="notice notice-success is-dismissible"><p>✅ Telegram test message sent successfully.</p></div>
<?php endif; ?>
<?php if (!empty($_GET['token_revoked'])): ?>
<div class="notice notice-warning is-dismissible"><p>🚫 Token revoked before use.</p></div>
<?php endif; ?>
<?php if ($generated): ?>
<div class="notice notice-success is-dismissible" style="border-left-color:#00a32a;">
<p><strong>🔥 BURN-AFTER-READING LINK GENERATED</strong></p>
<p>This link will work exactly <strong><?php echo (isset($_GET['uses']) ? intval($_GET['uses']) : 1); ?> time(s)</strong>, then turn to ash. Copy it NOW:</p>
<div style="display:flex;gap:10px;align-items:center;margin:10px 0;">
<input type="text" id="ghost-link-box" value="<?php echo $generated; ?>" readonly
style="flex:1;padding:12px;font-family:monospace;font-size:13px;background:#f0f0f1;border:1px solid #c3c4c7;border-radius:4px;">
<button type="button" class="button button-primary" onclick="ghostCopyLink()" style="height:44px;">📋 Copy Link</button>
</div>
<p id="ghost-copy-toast" style="color:#00a32a;font-weight:600;opacity:0;transition:opacity 0.3s;">✅ Copied to clipboard!</p>
<p><small>Share via encrypted DM only. Once clicked, this URL becomes permanently invalid.</small></p>
</div>
<?php endif; ?>
<hr>
<h2>Generate Ghost Link</h2>
<form method="post">
<?php wp_nonce_field('ghost_generate', 'ghost_nonce'); ?>
<input type="hidden" name="ghost_action" value="generate">
<table class="form-table">
<tr>
<th>Expiry</th>
<td>
<select name="expiry_hours">
<option value="1">1 Hour</option>
<option value="6">6 Hours</option>
<option value="24" selected>24 Hours</option>
<option value="168">1 Week</option>
</select>
</td>
</tr>
<tr>
<th>Max Uses <span style="color:red">*</span></th>
<td>
<input type="number" name="max_uses" value="1" min="1" max="5" style="width:60px">
<span class="description"><strong>1 = True Burn-After-Reading.</strong> The link deletes itself from the database the instant it is clicked. Higher values = team shared links.</span>
</td>
</tr>
<tr>
<th>Access Scope</th>
<td>
<label><input type="checkbox" name="caps[]" value="manage_options" checked> Manage Options</label><br>
<label><input type="checkbox" name="caps[]" value="edit_themes"> Edit Themes</label><br>
<label><input type="checkbox" name="caps[]" value="edit_plugins"> Edit Plugins</label><br>
<label><input type="checkbox" name="caps[]" value="install_plugins"> Install Plugins</label><br>
<label><input type="checkbox" name="caps[]" value="activate_plugins"> Activate Plugins</label><br>
<label><input type="checkbox" name="caps[]" value="switch_themes"> Switch Themes</label><br>
<label><input type="checkbox" name="caps[]" value="update_core"> Update Core</label><br>
<label><input type="checkbox" name="caps[]" value="export"> Export Data</label><br>
<label><input type="checkbox" name="caps[]" value="import"> Import Data</label><br>
<label><input type="checkbox" name="caps[]" value="edit_users"> Edit Users</label><br>
<label><input type="checkbox" name="caps[]" value="administrator"> ⚠️ Full Administrator</label>
<p class="description">Blocks <code>delete_users</code>, <code>create_users</code>, <code>promote_users</code> unless added below.</p>
<input type="text" name="extra_caps" placeholder="Extra: delete_users, create_users, etc." style="width:350px">
</td>
</tr>
<tr>
<th>Redirect After Login</th>
<td>
<input type="url" name="redirect_url" value="<?php echo esc_url(admin_url()); ?>" style="width:400px">
</td>
</tr>
<tr>
<th>IP Fingerprint Lock</th>
<td>
<label><input type="checkbox" name="ip_lock" value="1"> Lock to first IP that opens the link</label>
</td>
</tr>
<tr>
<th>Telegram Bot Alert URL</th>
<td>
<input type="url" name="webhook_url" placeholder="https://api.telegram.org/botTOKEN/sendMessage?chat_id=CHATID" style="width:400px">
<p class="description">Instant burn notification: "Token consumed and destroyed."</p>
</td>
</tr>
</table>
<?php submit_button('Generate Burn Link'); ?>
</form>
<hr>
<h2>🔥 Active Sessions (<?php echo count($sessions); ?> live)</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>Session ID</th>
<th>Token Ref</th>
<th>IP</th>
<th>Created</th>
<th>Expires</th>
<th>Hits</th>
<th>Burned?</th>
</tr>
</thead>
<tbody>
<?php foreach ($sessions as $sid => $s): ?>
<tr>
<td><code><?php echo esc_html(substr($sid,0,12)); ?>...</code></td>
<td><code><?php echo esc_html($s['token_ref']); ?></code></td>
<td><?php echo esc_html($s['ip']); ?></td>
<td><?php echo date('H:i', $s['created']); ?></td>
<td><?php echo date('H:i', $s['expires']); ?></td>
<td><?php echo (int)$s['hits']; ?></td>
<td><?php echo !empty($s['is_burned']) ? '<span style="color:red;font-weight:bold">🔥 YES</span>' : '—'; ?></td>
</tr>
<?php endforeach; ?>
<?php if (empty($sessions)): ?>
<tr><td colspan="7">No active ghost sessions.</td></tr>
<?php endif; ?>
</tbody>
</table>
<hr>
<h2>🎫 Token Pool (<?php echo count($tokens); ?> live tokens)</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>Token</th>
<th>Created</th>
<th>Expiry</th>
<th>Uses</th>
<th>Max Uses</th>
<th>IP Lock</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($tokens as $tid => $t):
$is_expired = time() > $t['expires'];
$is_exhausted = $t['used_count'] >= $t['max_uses'];
?>
<tr>
<td><code><?php echo esc_html(substr($tid,0,12)); ?>...</code></td>
<td><?php echo date('Y-m-d H:i', $t['created']); ?></td>
<td><?php echo date('Y-m-d H:i', $t['expires']); ?></td>
<td><?php echo (int)$t['used_count']; ?></td>
<td><?php echo (int)$t['max_uses']; ?></td>
<td><?php echo !empty($t['locked_ip']) ? esc_html($t['locked_ip']) : '—'; ?></td>
<td>
<?php if ($is_exhausted): ?>
<span style="color:#d63638;font-weight:bold">🔥 BURNED</span>
<?php elseif ($is_expired): ?>
<span style="color:#d63638">⏰ EXPIRED</span>
<?php else: ?>
<span style="color:#00a32a">● LIVE</span>
<?php endif; ?>
</td>
<td>
<?php if (!$is_exhausted && !$is_expired): ?>
<form method="post" style="display:inline;">
<?php wp_nonce_field('ghost_revoke', 'ghost_revoke_nonce'); ?>
<input type="hidden" name="ghost_action" value="revoke_token">
<input type="hidden" name="token_id" value="<?php echo esc_attr($tid); ?>">
<button type="submit" class="button button-small" style="color:#d63638;border-color:#d63638;">Revoke</button>
</form>
<?php if (!empty($t['webhook'])): ?>
<form method="post" style="display:inline;margin-left:5px;">
<?php wp_nonce_field('ghost_test', 'ghost_test_nonce'); ?>
<input type="hidden" name="ghost_action" value="test_webhook">
<input type="hidden" name="token_id" value="<?php echo esc_attr($tid); ?>">
<button type="submit" class="button button-small">Test Telegram</button>
</form>
<?php endif; ?>
<?php else: ?>
<span style="color:#999">Dead</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($tokens)): ?>
<tr><td colspan="8">No tokens in pool. All burned or expired.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php
}
public function handle_admin_forms() {
if (!isset($_POST['ghost_action'])) return;
// Revoke Token
if ($_POST['ghost_action'] === 'revoke_token') {
if (!wp_verify_nonce($_POST['ghost_revoke_nonce'] ?? '', 'ghost_revoke')) wp_die('Security check');
if (!current_user_can('manage_options')) wp_die('No access');
$tid = sanitize_text_field($_POST['token_id'] ?? '');
$tokens = get_option(self::TOKEN_OPTION, []);
if (isset($tokens[$tid])) {
unset($tokens[$tid]);
update_option(self::TOKEN_OPTION, $tokens);
$this->write_log('MANUAL_REVOKE', substr($tid,0,12), $this->get_client_ip(), 'Token revoked by admin');
}
wp_redirect(admin_url('tools.php?page=phantom-ghost&token_revoked=1'));
exit;
}
// Test Webhook
if ($_POST['ghost_action'] === 'test_webhook') {
if (!wp_verify_nonce($_POST['ghost_test_nonce'] ?? '', 'ghost_test')) wp_die('Security check');
if (!current_user_can('manage_options')) wp_die('No access');
$tid = sanitize_text_field($_POST['token_id'] ?? '');
$tokens = get_option(self::TOKEN_OPTION, []);
if (isset($tokens[$tid]) && !empty($tokens[$tid]['webhook'])) {
$this->send_alert($tokens[$tid], $this->get_client_ip(), '🧪 TEST: Ghost Core burn alert is working!');
wp_redirect(admin_url('tools.php?page=phantom-ghost&webhook_test=success'));
exit;
}
}
// Generate
if ($_POST['ghost_action'] !== 'generate') return;
if (!wp_verify_nonce($_POST['ghost_nonce'] ?? '', 'ghost_generate')) wp_die('Security check');
if (!current_user_can('manage_options')) wp_die('No access');
$token = bin2hex(random_bytes(32));
$caps = array_map('sanitize_text_field', $_POST['caps'] ?? []);
$extra = array_map('trim', explode(',', sanitize_text_field($_POST['extra_caps'] ?? '')));
$caps = array_unique(array_filter(array_merge($caps, $extra)));
if (empty($caps)) $caps = ['manage_options'];
if (in_array('administrator', $caps)) {
$caps = array_merge($caps, array_keys(wp_roles()->get_role('administrator')->capabilities));
$caps = array_unique($caps);
}
$redirect = !empty($_POST['redirect_url']) ? esc_url_raw($_POST['redirect_url']) : admin_url();
$max_uses = max(1, min(5, intval($_POST['max_uses'] ?? 1)));
$tokens = get_option(self::TOKEN_OPTION, []);
$tokens[$token] = [
'expires' => time() + (intval($_POST['expiry_hours'] ?? 24) * 3600),
'max_uses' => $max_uses,
'used_count' => 0,
'locked_ip' => null,
'lock_ip' => !empty($_POST['ip_lock']),
'caps' => $caps,
'redirect' => $redirect,
'webhook' => esc_url_raw($_POST['webhook_url'] ?? ''),
'created' => time(),
'created_by' => get_current_user_id()
];
update_option(self::TOKEN_OPTION, $tokens);
wp_redirect(admin_url('tools.php?page=phantom-ghost&generated=' . $token . '&uses=' . $max_uses));
exit;
}
/* ============================================================
KILL SWITCH
============================================================ */
public function add_kill_switch($bar) {
if (!current_user_can('manage_options')) return;
$bar->add_node([
'id' => 'ghost_kill',
'title' => '☠️ Kill Ghost Sessions',
'href' => wp_nonce_url(admin_url('?ghost_kill_all=1'), 'ghost_kill_all'),
'meta' => ['title' => 'Instantly destroy all active ghost sessions']
]);
}
public function process_kill_switch() {
if (!isset($_GET['ghost_kill_all']) || !current_user_can('manage_options')) return;
if (!wp_verify_nonce($_GET['_wpnonce'] ?? '', 'ghost_kill_all')) return;
delete_option(self::SESSION_OPTION);
$sys_user = get_user_by('login', self::SYSTEM_LOGIN);
if ($sys_user) {
wp_set_password(wp_generate_password(64), $sys_user->ID);
}
$this->write_log('KILL_SWITCH', 'ALL', $this->get_client_ip(), 'All sessions destroyed');
wp_redirect(admin_url('tools.php?page=phantom-ghost&ghost_killed=1'));
exit;
}
/* ============================================================
CLEANUP
============================================================ */
public function cleanup_expired_sessions() {
$sessions = get_option(self::SESSION_OPTION, []);
if (empty($sessions)) return;
$now = time();
$dirty = false;
foreach ($sessions as $sid => $s) {
if ($now > $s['expires']) {
unset($sessions[$sid]);
$dirty = true;
}
}
if ($dirty) update_option(self::SESSION_OPTION, $sessions);
}
// NEW v2.1: Purge any tokens that are fully consumed but somehow still in DB
public function cleanup_burned_tokens() {
$tokens = get_option(self::TOKEN_OPTION, []);
if (empty($tokens)) return;
$dirty = false;
foreach ($tokens as $tid => $t) {
if ($t['used_count'] >= $t['max_uses'] || time() > $t['expires']) {
unset($tokens[$tid]);
$dirty = true;
}
}
if ($dirty) update_option(self::TOKEN_OPTION, $tokens);
}
/* ============================================================
UTILITIES
============================================================ */
private function generate_fingerprint() {
$ip = $this->get_client_ip();
$ua = sanitize_text_field($_SERVER['HTTP_USER_AGENT'] ?? '');
return hash('sha256', $ip . '|' . $ua . '|' . wp_salt());
}
private function get_client_ip() {
$keys = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
foreach ($keys as $k) {
if (!empty($_SERVER[$k])) {
$ips = explode(',', $_SERVER[$k]);
return trim($ips[0]);
}
}
return '0.0.0.0';
}
private function write_log($action, $ref, $ip, $details) {
$file = $this->log_dir . '/ghost-' . date('Y-m-d') . '.log';
$line = sprintf("[%s] ACTION=%s REF=%s IP=%s UA=%s DETAILS=%s\n",
date('Y-m-d H:i:s'), $action, $ref, $ip,
sanitize_text_field($_SERVER['HTTP_USER_AGENT'] ?? 'Unknown'),
$details
);
file_put_contents($file, $line, FILE_APPEND | LOCK_EX);
}
private function send_alert($data, $ip, $event) {
$url = $data['webhook'] ?? '';
if (empty($url)) return true;
$site = parse_url(site_url(), PHP_URL_HOST);
$msg = "🔐 **Ghost Core Alert**\n**Site:** {$site}\n**Event:** {$event}\n**IP:** {$ip}\n**Time:** " . date('Y-m-d H:i:s');
if (strpos($url, 'api.telegram.org') !== false) {
$sep = (strpos($url, '?') !== false) ? '&' : '?';
wp_remote_get($url . $sep . 'text=' . urlencode($msg) . '&parse_mode=Markdown', ['timeout' => 5]);
} else {
wp_remote_post($url, [
'body' => json_encode(['content' => $msg, 'username' => 'Ghost Guard']),
'headers' => ['Content-Type' => 'application/json'],
'timeout' => 5
]);
}
}
public function admin_footer_js() {
$screen = get_current_screen();
if (!$screen || $screen->id !== 'tools_page_phantom-ghost') return;
?>
<script>
function ghostCopyLink() {
const box = document.getElementById('ghost-link-box');
const toast = document.getElementById('ghost-copy-toast');
if (!box) return;
box.select(); box.setSelectionRange(0, 99999);
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(box.value).then(() => showToast());
} else { document.execCommand('copy'); showToast(); }
function showToast() { toast.style.opacity = '1'; setTimeout(() => toast.style.opacity = '0', 2500); }
}
</script>
<?php
}
}
PhantomGhostCore::init();