我目前正在开发一个娱乐平台,我在一家网络公司担任网络开发人员。
这个项目是我个人的项目,它在 DigitalOcean 上运行 LAMP 堆栈。在工作中,我们有一个“第三方”人员负责服务器设置。
总的来说,我不太擅长服务器/ubuntu。
问题是,我只能使用 chmod - R 777 上传图像、gif 等,但我几乎在各个地方都看到,这是不可以的、危险的,等等。
我显然在脚本中使用安全性来处理文件,这是我的 UploadFile 脚本。
class UploadFile {
protected $destination;
protected $messages = [];
protected $maxSize = 51200;
protected $permittedTypes = array(
'image/jpeg',
'image/pjpeg',
'image/gif',
'image/png',
'image/webp'
);
protected $permittedHere;
protected $name;
protected $newName;
protected $fixedName = false;
protected $typeCheckingOn = true;
protected $notTrusted = array('bin', 'cgi', 'exe', 'js', 'pl', 'php', 'py', 'sh');
protected $suffix = '.upload';
protected $renameDuplicates;
protected $maxHeight;
protected $minHeight;
protected $maxWidth;
protected $minWidth;
protected $status = false;
public function getMessages() {
return $this->messages;
}
private function setMessages($type, $message) {
$this->messages[] = [$type => $message];
}
public function __construct($uploadFolder, $maxHeight, $permitted = false) {
$this->permittedHere = $permitted;
$this->maxHeight = $maxHeight;
if (!is_dir($uploadFolder) || !is_writable($uploadFolder)) {
self::setMessages("error", "$uploadFolder must be a valid, writable folder.");
}
if ($uploadFolder[strlen($uploadFolder) - 1] != '/') {
$uploadFolder .= '/';
}
$this->destination = $uploadFolder;
}
public function setMaxSize($bytes) {
$serverMax = self::convertToBytes(ini_get('upload_max_filesize'));
if (is_numeric($bytes) && $bytes > 0) {
$this->maxSize = $bytes;
}
}
public function setMaxHeight() {
$this->maxHeight = $maxHeight;
}
public function setMinHeight() {
$this->minHeight = $minHeight;
}
public function setMaxWidth($maxWidth) {
$this->maxWidth = $maxWidth;
}
public function setMinWidth($minWidth) {
$this->minWidth = $minWidth;
}
public static function convertToBytes($val) {
$val = trim($val);
$last = strtolower($val[strlen($val) - 1]);
if (in_array($last, array('g', 'm', 'k'))) {
switch ($last) {
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}
}
return $val;
}
public static function convertFromBytes($bytes) {
$bytes /= 1024;
if ($bytes > 1024) {
return number_format($bytes / 1024, 1) . ' MB';
} else {
return number_format($bytes, 1) . ' KB';
}
}
public function allowAllTypes($suffix = null) {
$this->typeCheckingOn = false;
if (!is_null($suffix)) {
if (strpos($suffix, '.') === 0 || $suffix == '') {
$this->suffix = $suffix;
} else {
$this->suffix = ".$suffix";
}
}
}
public function upload($renameDuplicates = true, $fixedName = false) {
$this->renameDuplicates = $renameDuplicates;
$this->fixedName = $fixedName;
$uploaded = current($_FILES);
$this->name = $uploaded['name'];
if (is_array($uploaded['name'])) {
foreach ($uploaded['name'] as $key => $value) {
$currentFile['name'] = $uploaded['name'][$key];
$currentFile['type'] = $uploaded['type'][$key];
$currentFile['tmp_name'] = $uploaded['tmp_name'][$key];
$currentFile['error'] = $uploaded['error'][$key];
$currentFile['size'] = $uploaded['size'][$key];
if ($this->checkFile($currentFile)) {
$this->moveFile($currentFile);
}
}
} else {
if ($this->checkFile($uploaded)) {
$this->moveFile($uploaded);
}
}
}
public function getName() {
return $this->name;
}
public function getNewName() {
return $this->newName;
}
public function getStatus() {
return $this->status;
}
public function getSourceName() {
return $this->sourceName;
}
protected function checkFile($file) {
// echo "<p class='checkme'>";
// echo "<pre>";
// print_r($file);
// echo "</pre>";
if ($file['error'] != 0) {
$this->getErrorMessage($file);
return false;
}
if (!$this->checkSize($file)) {
return false;
}
if (!empty($this->maxHeight)) {
if (!$this->checkHeight($file)) {
return false;
}
}
if ($this->typeCheckingOn) {
if (!$this->checkType($file)) {
return false;
}
}
$this->checkName($file);
return true;
}
protected function getErrorMessage($file) {
switch ($file['error']) {
case 1:
case 2:
self::setMessages("error", $file['name'] . " is too big: (max: " . self::convertFromBytes($this->maxSize) . ")");
break;
case 3:
self::setMessages("error", $file['name'] . " was only partially uploaded.");
break;
case 4:
self::setMessages("error", "No file submitted.");
break;
default:
self::setMessages("error", "Sorry, there was a problem uploading " . $file['name']);
break;
}
}
protected function checkSize($file) {
if ($file['size'] == 0) {
self::setMessages("error", $file['name'] . " is empty.");
return false;
} elseif ($file['size'] > $this->maxSize) {
self::setMessages("error", $file['name'] . " exceeds the maximum size for a file (" . self::convertFromBytes($this->maxSize) . ").");
return false;
} else {
return true;
}
}
protected function checkHeight($file) {
$data = getimagesize($file['tmp_name']);
$height = $data[1];
if ($height > $this->maxHeight) {
self::setMessages("error", "Height exceed the limit of " . $this->maxHeight);
return false;
}
if ($height > $this->minHeight) {
self::setMessages("error", "The image should be minimum " . $this->minHeight);
return false;
}
return true;
}
protected function checkType($file) {
if (in_array($file['type'], $this->permittedTypes)) {
if (isset($this->permittedHere) && !empty($this->permittedHere)) {
if (in_array($file['type'], $this->permittedHere)) {
return true;
} else {
self::setMessages("error", $file['name'] . " is not permitted type of file.");
return false;
}
} else {
return true;
}
} else {
self::setMessages("error", $file['name'] . " is not permitted type of file.");
return false;
}
}
protected function checkName($file) {
$this->newName = null;
$nospaces = str_replace(' ', '_', $file['name']);
if ($nospaces != $file['name']) {
$this->newName = $nospaces;
}
$nameparts = pathinfo($nospaces);
$extension = isset($nameparts['extension']) ? $nameparts['extension'] : '';
if (!$this->typeCheckingOn && !empty($this->suffix)) {
if (in_array($extension, $this->notTrusted) || empty($extension)) {
$this->newName = $nospaces . $this->suffix;
} else {
self::setMessages("error", "Something went wrong please try again.");
}
}
if ($this->fixedName){
if (!in_array($extension, $this->notTrusted) || !empty($extension)) {
$this->newName = $this->fixedName . ".$extension";
} else {
self::setMessages("error", "Something went wrong please try again.");
}
}
if ($this->renameDuplicates) {
$name = isset($this->newName) ? $this->newName : $file['name'];
$existing = scandir($this->destination);
if (in_array($name, $existing)) {
$i = 1;
do {
$this->newName = $nameparts['filename'] . '_' . $i++;
if (!empty($extension)) {
$this->newName .= ".$extension";
}
if (in_array($extension, $this->notTrusted)) {
$this->newName .= $this->suffix;
}
} while (in_array($this->newName, $existing));
}
}
}
protected function moveFile($file) {
$filename = isset($this->newName) ? $this->newName : $file['name'];
$success = move_uploaded_file($file['tmp_name'], $this->destination . $filename);
if ($success) {
$result = $file['name'] . ' was uploaded successfully';
if (!is_null($this->newName)) {
$result .= ', and was renamed ' . $this->newName;
}
$result .= '.';
self::setMessages("success", $result);
$this->status = true;
} else {
self::setMessages("error", $result);
}
}
}
因此,如果 chmod - R 775 对我不起作用,除了使用 chmod - R 777 之外,我还有什么其他选择?我有点困惑如何解决用户可以上传到的文件夹的问题?
我该如何解决这个问题?
答案1
问题是,我只能使用 chmod - R 777 上传图像、gif 等,但我几乎在各个地方都看到,这是不可以的、危险的,等等。
这确实是一个大忌。你不希望外人能够找到并执行你的脚本。或者能够查看配置文件,从中找出数据库的密码。
切勿将任何不需要可执行的内容设置为可执行(“7”)。正常文件的最大权限为 664,但最好为 640。目录的最大权限为 775,但最好为 770(“0”表示“其他”无法执行任何操作)。
最好将所有可执行文件放在 Web 服务器之外,并从 Web 服务器间接启动它们。这样就没有人可以滥用它们,最多只能间接启动它们,因此不会收到来自这些脚本的任何回复。
如果您坚持这样做,您可以使用“组”选项将两个用户添加到同一组。这样您和其他人就可以使用组权限(并且这些权限对于所有相关用户都是相同的)。
可能有用的命令:
- 创建一个组“webserver”:
sudo groupadd webserver
- 将用户 rinzwind 添加到此组:
sudo usermod -a -G webserver rinzwind
- 列出当前用户所属的组
groups
还有另一种方法,需要做的事情少得多(但也不是最好的方法):您可以让 cron 作业 (/etc/cronjob) 处理您的问题。将您需要的内容上传到(例如)/home 中的目录结构,然后设置一个 cronjob 来执行将它们放置到正确位置的移动。/etc/cronjob 可以设置为使用特定的用户名(您可以将其设置为拥有 Web 服务器的用户)。这也解决了您的问题。