```
100,
'max_content_length' => 5000,
'allowed_tags' => ['p', 'br', 'strong', 'em', 'ul', 'li', 'a'],
'max_links' => 3,
'min_content_length' => 50
];
public function validateTitle($title) {
$title = trim($title);
if (empty($title)) {
throw new Exception('标题不能为空');
}
if (mb_strlen($title, 'UTF-8') > $this->config['max_title_length']) {
throw new Exception('标题长度不能超过' . $this->config['max_title_length'] . '字符');
}
if (preg_match('/[<>\"\']/', $title)) {
throw new Exception('标题包含非法字符');
}
return htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
}
public function validateContent($content) {
$content = trim($content);
if (mb_strlen($content, 'UTF-8') < $this->config['min_content_length']) {
throw new Exception('内容长度不能少于' . $this->config['min_content_length'] . '字符');
}
if (mb_strlen($content, 'UTF-8') > $this->config['max_content_length']) {
throw new Exception('内容长度不能超过' . $this->config['max_content_length'] . '字符');
}
// 统计链接数量
$link_count = preg_match_all('/
]href=[\"\']([^\"\'])[\"\'][^>]>/i', $content);
if ($link_count > $this->config['max_links']) {
throw new Exception('内容中链接数量不能超过' . $this->config['max_links'] . '个');
}
return $this->sanitizeHTML($content);
}
private function sanitizeHTML($html) {
$dom = new DOMDocument();
@$dom->loadHTML('' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query('//');
foreach ($nodes as $node) {
if (!in_array($node->nodeName, $this->config['allowed_tags'])) {
$node->parentNode->removeChild($node);
}
}
// 移除所有事件处理属性
$nodes = $xpath->query('//@[starts-with(name(), "on")]');
foreach ($nodes as $node) {
$node->parentNode->removeAttribute($node->nodeName);
}
return $dom->saveHTML();
}
}
?>
```
内容过滤系统
实现关键词过滤和敏感信息检测。创建content_filter.php:
```
sensitive_patterns as $pattern) {
if (preg_match($pattern, $content)) {
return '内容包含敏感个人信息,请修改后重新提交';
}
}
return null;
}
public function checkForbiddenContent($content) {
foreach ($this->forbidden_keywords as $keyword) {
if (mb_strpos($content, $keyword) !== false) {
return '内容包含违规关键词,请修改后重新提交';
}
}
return null;
}
}
?>
```
系统安全防护
防止系统层面的攻击,包括CSRF、XSS和暴力破解。
CSRF防护实现
在投稿表单页面生成并验证CSRF令牌。创建csrf_protection.php:
```
';
}
}
?>
```
在投稿表单中插入CSRF令牌字段:
```
```

在提交处理页面验证令牌:
```
```
频率限制机制
防止用户通过脚本暴力提交。创建rate_limiter.php:
```
['count' => 10, 'window' => 3600], // 每小时10次
'same_ip' => ['count' => 50, 'window' => 3600] // 每小时50次
];
public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
public function checkLimit($user_id, $ip_address) {
$user_key = "submission:user:{$user_id}";
$ip_key = "submission:ip:{$ip_address}";
// 检查用户提交频率
$user_count = $this->redis->get($user_key);
if ($user_count && $user_count >= $this->limits['submission']['count']) {
return '提交过于频繁,请稍后再试';
}
// 检查IP提交频率
$ip_count = $this->redis->get($ip_key);
if ($ip_count && $ip_count >= $this->limits['same_ip']['count']) {
return '该IP地址提交过于频繁';
}
return null;
}
public function increment($user_id, $ip_address) {
$user_key = "submission:user:{$user_id}";
$ip_key = "submission:ip:{$ip_address}";
$this->redis->multi();
$this->redis->incr($user_key);
$this->redis->expire($user_key, $this->limits['submission']['window']);
$this->redis->incr($ip_key);
$this->redis->expire($ip_key, $this->limits['same_ip']['window']);
$this->redis->exec();
}
}
?>
```
数据安全防护
确保用户提交的数据在存储和传输过程中的安全性。
文件上传安全
创建安全的文件上传处理类file_uploader.php:
```
'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
'application/pdf' => 'pdf'
];
private $max_size = 5 1024 1024; // 5MB
private $upload_dir = 'uploads/';
public function upload($file) {
// 检查文件大小
if ($file['size'] > $this->max_size) {
throw new Exception('文件大小不能超过5MB');
}
// 检查MIME类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!isset($this->allowed_types[$mime_type])) {
throw new Exception('不支持的文件类型');
}
// 检查文件扩展名
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if ($extension !== $this->allowed_types[$mime_type]) {
throw new Exception('文件扩展名与内容类型不匹配');
}
// 生成安全文件名
$safe_name = $this->generateSafeName($file['name'], $extension);
$destination = $this->upload_dir . $safe_name;
// 移动文件
if (!move_uploaded_file($file['tmp_name'], $destination)) {
throw new Exception('文件上传失败');
}
// 如果是图片,进行二次验证
if (strpos($mime_type, 'image/') === 0) {
$this->validateImage($destination);
}
return $safe_name;
}
private function generateSafeName($original_name, $extension) {
$name = md5($original_name . time() . random_bytes(16));
return $name . '.' . $extension;
}
private function validateImage($file_path) {
$image_info = getimagesize($file_path);
if ($image_info === false) {
unlink($file_path);
throw new Exception('无效的图片文件');
}
}
}
?>
```
数据库安全存储
创建安全的数据库操作类database_handler.php
```
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
];
$this->pdo = new PDO($dsn, 'username', 'password', $options);
}
public function saveSubmission($data) {
$sql = "INSERT INTO submissions (
title,
content,
author_id,
ip_address,
user_agent,
created_at,
status
) VALUES (
:title,
:content,
:author_id,
:ip_address,
:user_agent,
NOW(),
'pending'
)";
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':title', $data['title'], PDO::PARAM_STR);
$stmt->bindValue(':content', $data['content'], PDO::PARAM_STR);
$stmt->bindValue(':author_id', $data['author_id'], PDO::PARAM_INT);
$stmt->bindValue(':ip_address', $data['ip_address'], PDO::PARAM_STR);
$stmt->bindValue(':user_agent', $data['user_agent'], PDO::PARAM_STR);
return $stmt->execute();
}
public function getSubmission($id, $user_id = null) {
$sql = "SELECT FROM submissions WHERE id = :id";
if ($user_id !== null) {
$sql .= " AND author_id = :user_id";
}
$stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
if ($user_id !== null) {
$stmt->bindValue(':user_id', $user_id, PDO::PARAM_INT);
}
$stmt->execute();
return $stmt->fetch();
}
}
?>
```
完整实现流程
将上述组件整合到投稿处理流程中。创建submit_handler.php:
```
checkLimit($user_id, $ip_address);
if ($limit_error) {
throw new Exception($limit_error);
}
// 3. 输入验证
$validator = new SubmissionValidator();
$title = $validator->validateTitle($_POST['title']);
$content = $validator->validateContent($_POST['content']);
// 4. 内容过滤
$filter = new ContentFilter();
$sensitive_error = $filter->checkSensitiveInfo($content);
if ($sensitive_error) {
throw new Exception($sensitive_error);
}
$forbidden_error = $filter->checkForbiddenContent($content);
if ($forbidden_error) {
throw new Exception($forbidden_error);
}
// 5. 文件上传处理
$uploaded_files = [];
if (!empty($_FILES['attachments'])) {
$uploader = new SecureFileUploader();
foreach ($_FILES['attachments']['tmp_name'] as $key => $tmp_name) {
if (!empty($tmp_name)) {
$file = [
'name' => $_FILES['attachments']['name'][$key],
'type' => $_FILES['attachments']['type'][$key],
'tmp_name' => $tmp_name,
'error' => $_FILES['attachments']['error'][$key],
'size' => $_FILES['attachments']['size'][$key]
];
$filename = $uploader->upload($file);
$uploaded_files[] = $filename;
}
}
}
// 6. 数据存储
$db_handler = new SecureDatabaseHandler();
$submission_data = [
'title' => $title,
'content' => $content,
'author_id' => $user_id,
'ip_address' => $ip_address,
'user_agent' => $_SERVER['HTTP_USER_AGENT']
];
$result = $db_handler->saveSubmission($submission_data);
if ($result) {
// 更新频率限制计数器
$rate_limiter->increment($user_id, $ip_address);
// 记录操作日志
$this->logSubmission($user_id, $ip_address);
return [
'success' => true,
'message' => '投稿提交成功,等待审核',
'submission_id' => $db_handler->lastInsertId()
];
} else {
throw new Exception('数据保存失败');
}
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
private function logSubmission($user_id, $ip_address) {
$log_entry = sprintf(
"[%s] User: %d, IP: %s, Action: submission\n",
date('Y-m-d H:i:s'),
$user_id,
$ip_address
);
file_put_contents('logs/submission.log', $log_entry, FILE_APPEND);
}
}
// 使用示例
$handler = new SubmissionHandler();
$result = $handler->processSubmission();
if ($result['success']) {
header('Location: /success.php?id=' . $result['submission_id']);
} else {
header('Location: /submit.php?error=' . urlencode($result['message']));
}
?>
```
监控与维护
建立监控机制,及时发现和处理异常情况。
异常监控配置
创建监控脚本monitor_submissions.php:
```
1000,