<?php
// 安全反馈系统（前后端一体，存储到JSON）
session_start();

// 安全HTTP头
header('Content-Type: text/html; charset=UTF-8');
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: SAMEORIGIN");
header("Referrer-Policy: no-referrer-when-downgrade");
header("X-XSS-Protection: 1; mode=block");
// 内容安全策略（允许本页的内联脚本与样式，限制外部资源为同源）
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self';");

// 文件路径
$dataFile = __DIR__ . DIRECTORY_SEPARATOR . 'feedback.json';
$rateFile = __DIR__ . DIRECTORY_SEPARATOR . 'rate_limit.json';

// 初始化存储文件
if (!file_exists($dataFile)) {
    file_put_contents($dataFile, "[]", LOCK_EX);
}
if (!file_exists($rateFile)) {
    file_put_contents($rateFile, "{}", LOCK_EX);
}

// 生成CSRF令牌
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(16));
}

// 工具函数
function load_json($path) {
    $raw = @file_get_contents($path);
    if ($raw === false || $raw === '') return [];
    $data = json_decode($raw, true);
    return is_array($data) ? $data : [];
}

function atomic_write_json($path, $data) {
    $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
    $tmp = $path . '.tmp';
    if (file_put_contents($tmp, $json, LOCK_EX) === false) {
        return false;
    }
    // Windows下原子替换
    if (!@rename($tmp, $path)) {
        @unlink($tmp);
        return false;
    }
    return true;
}

function get_client_ip() {
    $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    // 如果在代理后可扩展为 HTTP_X_FORWARDED_FOR 校验，这里不启用以防伪造
    return $ip;
}

function is_valid_content_type() {
    $ct = $_SERVER['CONTENT_TYPE'] ?? $_SERVER['HTTP_CONTENT_TYPE'] ?? '';
    return stripos($ct, 'application/x-www-form-urlencoded') !== false || stripos($ct, 'multipart/form-data') !== false;
}

function escape_html($str) {
    return htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

// 速率限制：每个IP每分钟最多5次
function rate_limit_check($rateFile, $ip, $limit = 5, $windowSec = 60) {
    $rates = load_json($rateFile);
    $now = time();
    $list = isset($rates[$ip]) && is_array($rates[$ip]) ? $rates[$ip] : [];
    // 清理窗口外的记录
    $list = array_values(array_filter($list, function($t) use ($now, $windowSec) { return ($now - (int)$t) < $windowSec; }));
    if (count($list) >= $limit) {
        return [false, $rates];
    }
    // 记录本次
    $list[] = $now;
    $rates[$ip] = $list;
    return [true, $rates];
}

// 处理提交
$errMsg = '';
$okMsg = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 请求体大小限制（约2KB）
    $maxBody = 2048;
    $bodyLen = (int)($_SERVER['CONTENT_LENGTH'] ?? 0);
    if ($bodyLen > $maxBody) {
        $errMsg = '请求体过大，请减少输入内容长度。';
    } elseif (!is_valid_content_type()) {
        $errMsg = 'Content-Type不合法，请使用表单默认提交方式。';
    } else {
        // CSRF校验
        $token = $_POST['csrf_token'] ?? '';
        if (!hash_equals($_SESSION['csrf_token'], (string)$token)) {
            $errMsg = 'CSRF令牌验证失败，请刷新页面后重试。';
        } else {
            // 输入验证
            $content = trim((string)($_POST['content'] ?? ''));
            if (mb_strlen($content, 'UTF-8') < 5 || mb_strlen($content, 'UTF-8') > 1000) {
                $errMsg = '反馈内容长度需为5-1000字符。';
            } elseif (preg_match('/<\s*\/?\s*script|<\s*\/?\s*style|<\s*\/?\s*iframe/i', $content)) {
                $errMsg = '检测到潜在的HTML/JS代码，请移除后再提交。';
            } else {
                // 速率限制
                $ip = get_client_ip();
                list($allowed, $rates) = rate_limit_check($rateFile, $ip);
                if (!$allowed) {
                    $errMsg = '提交过于频繁，请稍后再试。';
                } else {
                    // 读取现有反馈
                    $list = load_json($dataFile);
                    if (!is_array($list)) $list = [];
                    // 存储安全数据（进行HTML转义）
                    $record = [
                        'time' => date('c'),
                        'ip' => $ip,
                        'ua' => substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 256),
                        'content' => $content // 原始内容只做长度与标签校验，展示时统一escape
                    ];
                    $list[] = $record;
                    // 写入速率文件
                    atomic_write_json($rateFile, $rates);
                    // 原子写入反馈JSON
                    if (!atomic_write_json($dataFile, $list)) {
                        $errMsg = '服务器写入失败，请稍后重试。';
                    } else {
                        $okMsg = '反馈提交成功，感谢您的参与！';
                        // 每次提交后刷新令牌
                        $_SESSION['csrf_token'] = bin2hex(random_bytes(16));
                    }
                }
            }
        }
    }
}

// 加载反馈列表用于展示
$feedbackList = load_json($dataFile);
if (!is_array($feedbackList)) $feedbackList = [];

?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>安全反馈系统</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; line-height: 1.6; }
        h1 { text-align:center; }
        .card { background:#f9f9f9; border:1px solid #ddd; border-radius:8px; padding:20px; margin-bottom:24px; }
        .msg { padding:10px 14px; border-radius:6px; margin-bottom:14px; }
        .ok { background:#e7f6ea; border:1px solid #a5d6a7; color:#1b5e20; }
        .err { background:#fdecea; border:1px solid #f5c6cb; color:#b71c1c; }
        label { display:block; font-weight:bold; margin:8px 0; }
        textarea { width:100%; min-height:120px; padding:10px; font-size:14px; }
        button { background:#4CAF50; color:#fff; border:none; padding:10px 16px; border-radius:6px; cursor:pointer; }
        button:hover { background:#43a047; }
        .feedback-item { border-top:1px dashed #ddd; padding-top:12px; margin-top:12px; }
        .meta { color:#555; font-size:12px; }
        .tips { background:#fff; border-left:4px solid #2196F3; padding:12px; }
    </style>
</head>
<body>
    <h1>安全反馈系统</h1>
    <div class="card">
        <p>安全特性：输入验证、CSRF防护、CSP、速率限制、严格Content-Type、原子写入JSON、XSS防护。</p>
        <?php if ($okMsg): ?><div class="msg ok"><?php echo escape_html($okMsg); ?></div><?php endif; ?>
        <?php if ($errMsg): ?><div class="msg err"><?php echo escape_html($errMsg); ?></div><?php endif; ?>
        <form method="post" action="" autocomplete="off">
            <input type="hidden" name="csrf_token" value="<?php echo escape_html($_SESSION['csrf_token']); ?>" />
            <label for="content">反馈内容（5-1000字符）</label>
            <textarea id="content" name="content" maxlength="1000" required placeholder="请描述您的问题或建议，禁止粘贴HTML/JS代码"></textarea>
            <div style="margin-top:10px;">
                <button type="submit">提交反馈</button>
            </div>
        </form>
        <div class="tips" style="margin-top:16px;">
            <strong>提示：</strong> 每分钟最多提交5次；若被拒绝，请稍后重试。我们会妥善保存您的反馈到服务器JSON文件。
        </div>
    </div>

    <div class="card">
        <h2>其他用户反馈</h2>
        <?php if (empty($feedbackList)): ?>
            <p>暂无反馈，快来成为第一个留下建议的人吧！</p>
        <?php else: ?>
            <?php foreach ($feedbackList as $item): ?>
                <div class="feedback-item">
                    <div class="meta">时间：<?php echo escape_html($item['time'] ?? ''); ?> | IP：<?php echo escape_html($item['ip'] ?? ''); ?> | UA：<?php echo escape_html($item['ua'] ?? ''); ?></div>
                    <div><?php echo nl2br(escape_html($item['content'] ?? '')); ?></div>
                </div>
            <?php endforeach; ?>
        <?php endif; ?>
    </div>
</body>
</html>
