1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| <?php
class SecureFileHandler { private $allowedExtensions; private $maxFileSize; private $uploadDir; public function __construct($uploadDir = 'uploads/', $maxFileSize = 5242880) { $this->uploadDir = rtrim($uploadDir, '/') . '/'; $this->maxFileSize = $maxFileSize; $this->allowedExtensions = ['txt', 'jpg', 'jpeg', 'png', 'gif', 'pdf']; if (!is_dir($this->uploadDir)) { mkdir($this->uploadDir, 0755, true); } } public function validateFile($filename, $filesize = null, $content = null) { if (empty($filename) || strpos($filename, '..') !== false) { throw new InvalidArgumentException('无效的文件名'); } $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); if (!in_array($extension, $this->allowedExtensions)) { throw new InvalidArgumentException('不允许的文件类型'); } if ($filesize !== null && $filesize > $this->maxFileSize) { throw new InvalidArgumentException('文件太大'); } if ($content !== null) { $dangerousPatterns = [ '/<\?php/i', '/<script/i', '/eval\s*\(/i', '/exec\s*\(/i', '/system\s*\(/i' ]; foreach ($dangerousPatterns as $pattern) { if (preg_match($pattern, $content)) { throw new InvalidArgumentException('文件包含危险内容'); } } } return true; } public function secureWrite($filename, $content) { try { $this->validateFile($filename, strlen($content), $content); $safeFilename = $this->generateSafeFilename($filename); $fullPath = $this->uploadDir . $safeFilename; $result = file_put_contents($fullPath, $content, LOCK_EX); if ($result !== false) { chmod($fullPath, 0644); return $safeFilename; } throw new RuntimeException('文件写入失败'); } catch (Exception $e) { throw new RuntimeException('安全写入失败:' . $e->getMessage()); } } public function secureRead($filename) { $fullPath = $this->uploadDir . $filename; $realPath = realpath($fullPath); $realUploadDir = realpath($this->uploadDir); if (!$realPath || strpos($realPath, $realUploadDir) !== 0) { throw new InvalidArgumentException('不允许访问该文件'); } if (!file_exists($fullPath)) { throw new InvalidArgumentException('文件不存在'); } return file_get_contents($fullPath); } private function generateSafeFilename($filename) { $extension = pathinfo($filename, PATHINFO_EXTENSION); $basename = pathinfo($filename, PATHINFO_FILENAME); $basename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $basename); $basename = substr($basename, 0, 50); return $basename . '_' . time() . '.' . $extension; } }
try { $fileHandler = new SecureFileHandler(); $testContent = "这是安全的文件内容\n测试数据"; $savedFile = $fileHandler->secureWrite('test.txt', $testContent); echo "文件安全保存为:$savedFile\n"; $readContent = $fileHandler->secureRead($savedFile); echo "读取的内容:\n$readContent\n"; unlink('uploads/' . $savedFile); rmdir('uploads'); } catch (Exception $e) { echo "错误:" . $e->getMessage() . "\n"; } ?>
|