]> 91.132.146.200 Git - selfpaste.git/commitdiff
rewrite the creation and lokkup process
authorBanana <banana@mirage>
Sun, 22 Dec 2019 13:40:15 +0000 (14:40 +0100)
committerBanana <banana@mirage>
Sun, 22 Dec 2019 13:40:15 +0000 (14:40 +0100)
TODO
documentation/security.txt
webroot/index.php
webroot/lib/mancubus.class.php [new file with mode: 0644]
webroot/lib/summoner.class.php

diff --git a/TODO b/TODO
index 8483a7ef1781fdaad170b2fd0882a91efaf17818..97f40df001e8eb9c8ca944d858a230fe5552cc85 100644 (file)
--- a/TODO
+++ b/TODO
@@ -4,4 +4,6 @@
 * add force download parameter
 * HTTPS for selfpaste bash client
 * multiple secrets
-* extending allowed filetypes
\ No newline at end of file
+* extending allowed filetypes
+* creation or even access with basic auth
+* flood protection
\ No newline at end of file
index 1cf218bab9a38522df9e38a09fd1faf014f5003f..dea5fd1cdedca9cde5b021ecb070f4556698ca06 100644 (file)
@@ -25,4 +25,7 @@ So, here is a friendly REMINDER:
     - You provide the service by hosting it. Your are responsible for it!
     - Change your secret often
 
-Make sure DEBUG is false for production.
\ No newline at end of file
+Make sure DEBUG is false for production.
+
+Protect the storage location from direct access. Default solved with a .htaccess
+file. Better solution is to move the location outside the webroot.
\ No newline at end of file
index a390cd4f199c42f9dd4d2fbc861b5547e183be0e..51ff9d796c3f2cf61bbd08e86de16f16f1c4c4a2 100644 (file)
@@ -40,9 +40,11 @@ else {
 }
 
 # static helper class
-require 'lib/summoner.class.php';
+require_once 'lib/summoner.class.php';
 # config file
-require 'config.php';
+require_once 'config.php';
+# upload / file handling
+require_once 'lib/mancubus.class.php';
 
 $_short = false;
 if(isset($_GET['s']) && !empty($_GET['s'])) {
@@ -67,8 +69,12 @@ if(!empty($_short)) {
     $httpResponseCode = 404;
     $contentBody = 'File not found.';
 
-    $_requestFile = Summoner::createStoragePath($_short);
-    $_requestFile .= $_short;
+    $_t = Summoner::b64sl_unpack_id($_short);
+    $_t = (string)$_t;
+    $_p = Summoner::forwardslashStringToPath($_t);
+    $_requestFile = Summoner::endsWith(SELFPASTE_UPLOAD_DIR,'/') ? SELFPASTE_UPLOAD_DIR : SELFPASTE_UPLOAD_DIR.'/';
+    $_requestFile .= $_p;
+    $_requestFile .= $_t;
     if(is_readable($_requestFile)) {
         $contentBody = $_requestFile;
         $httpResponseCode = 200;
@@ -82,23 +88,17 @@ elseif ($_create === true) {
 
     $_file = $_FILES['pasty'];
 
-    $_file['short'] = Summoner::createShort();
-    $_file['shortUrl'] = SELFPASTE_URL.'/'.$_file['short'];
-    $_file['storagepath'] = Summoner::createStoragePath($_file['short']);
-
-    $_checks = array('checkFileUploadStatus','checkAllowedFiletype','checkStorage','moveUploadedPasteFile');
-    $_do['status'] = false;
-    foreach($_checks as $_check) {
-        if(method_exists('Summoner',$_check)) {
-            $_do = Summoner::$_check($_file);
-            $_message = $_do['message'];
-            if($_do['status'] === true) {
-                $httpResponseCode = 200;
-            }
-            else {
-                $httpResponseCode = 400;
-                break;
-            }
+    $_fileObj = new Mancubus();
+    if($_fileObj->load($_FILES['pasty']) === true) {
+        $_fileObj->setSaveFilename();
+        $_fileObj->setShort();
+        $_fileObj->setStoragePath();
+        $_fileObj->setShortURL();
+
+        $_do = $_fileObj->process();
+        $_message = $_do['message'];
+        if($_do['status'] === true) {
+            $httpResponseCode = 200;
         }
     }
 
diff --git a/webroot/lib/mancubus.class.php b/webroot/lib/mancubus.class.php
new file mode 100644 (file)
index 0000000..a10e661
--- /dev/null
@@ -0,0 +1,237 @@
+<?php
+/**
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
+ *
+ * You should have received a copy of the
+ * COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
+ * along with this program.  If not, see http://www.sun.com/cddl/cddl.html
+ *
+ * 2019 https://www.bananas-playground.net/projekt/selfpaste
+ */
+
+/**
+ * Handles the upload and the file itself
+ */
+class Mancubus {
+
+    private $_uploadedData;
+    private $_short;
+    private $_saveFilename;
+    private $_storagePath;
+    private $_shortURL;
+
+    /**
+     * Mancubus constructor.
+     */
+    function __construct() {
+    }
+
+    /**
+     * Requires a single upload from $_FILES
+     * @see https://www.php.net/manual/en/features.file-upload.post-method.php
+     * @param $file array
+     * @return bool
+     */
+    public function load($file) {
+        $ret = false;
+
+        if(isset($file['name'])
+            && isset($file['type'])
+            && isset($file['size'])
+            && isset($file['tmp_name'])
+            && isset($file['error'])
+        ) {
+            $this->_uploadedData = $file;
+            $ret = true;
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Either set short to given string
+     * or create from _saveFilename. In this case _saveFilename is a number
+     * @param string $short
+     */
+    public function setShort($short='') {
+        if($short != '') {
+            $this->_short = $short;
+        }
+        elseif(!empty($this->_saveFilename)) {
+            $this->_short = Summoner::b64sl_pack_id($this->_saveFilename);
+        }
+    }
+
+    /**
+     * Either set _saveFilename to given string
+     * or create from a random number. In this case _short needs this as a base
+     * @param string $string
+     * @throws Exception
+     */
+    public function setSaveFilename($string='') {
+        if($string != '') {
+            $this->_saveFilename = $string;
+        }
+        else {
+            $r = random_int(1000, 9999);
+            $this->_saveFilename = (string)$r;
+        }
+    }
+
+    /**
+     * Set _shortURL to given string
+     * or create based on SELFPASTE_URL and _short
+     * @param string $string
+     */
+    public function setShortURL($string='') {
+        if($string != '') {
+            $this->_shortURL = $string;
+        }
+        elseif(!empty($this->_short)) {
+            $this->_shortURL = SELFPASTE_URL.'/'.$this->_short;
+        }
+    }
+
+    /**
+     * set the right storage path based on _saveFilename
+     * and SELFPASTE_UPLOAD_DIR
+     */
+    public function setStoragePath() {
+        $string = $this->_saveFilename;
+
+        if(!empty($string)) {
+            $p = SELFPASTE_UPLOAD_DIR.'/';
+            $p .= Summoner::forwardslashStringToPath($string);
+            $this->_storagePath = $p;
+        }
+    }
+
+    public function process() {
+        $ret = array(
+            'message' => '',
+            'status' => false
+        );
+
+        try {
+            $ret = $this->_checkFileUploadStatus();
+            $ret = $this->_checkAllowedFiletype();
+            $ret = $this->_checkStorage();
+            $ret = $this->_moveUploadedFile();
+        }
+        catch (Exception $e) {
+            $ret['message'] = $e->getMessage();
+        }
+        
+        return $ret;
+    }
+
+    /**
+     * Check if the POST upload worked
+     * @return array message,status
+     * @throws Exception
+     */
+    private function _checkFileUploadStatus() {
+        $check = Summoner::checkFileUploadStatus($this->_uploadedData['error']);
+
+        if($check['status'] === true) {
+            # check has the structure we want already
+            return $check;
+        }
+        else {
+            throw new Exception($check['message']);
+        }
+    }
+
+    /**
+     * Check if the uploaded file matches the allowed filetypes
+     * @return array message,status
+     * @throws Exception
+     */
+    private function _checkAllowedFiletype() {
+        $message = "Filetype not supported";
+        $status = false;
+
+        $workWith = $this->_uploadedData['tmp_name'];
+        if(!empty($workWith)) {
+            $finfo = finfo_open(FILEINFO_MIME_TYPE);
+            $mime = finfo_file($finfo, $workWith);
+            finfo_close($finfo);
+            if(strpos(SELFPASTE_ALLOWED_FILETYPES,$mime) !== false) {
+                $status = true;
+                $message = "Filetype allowed";
+            }
+            else {
+                if(DEBUG) $message .= " $mime";
+                throw new Exception($message);
+            }
+        } else {
+            throw new Exception($message);
+        }
+
+        return array(
+            'message' => $message,
+            'status' => $status
+        );
+    }
+
+    private function _checkStorage() {
+        $message = "File storage failure";
+        $status = false;
+
+        $workwith = $this->_storagePath;
+        if(is_writable(SELFPASTE_UPLOAD_DIR)) {
+            if (mkdir($workwith,0777,true)) {
+                $message = "File storage creation success";
+                $status = true;
+            }
+            else {
+                if(DEBUG) $message .= " ".$workwith;
+                throw new Exception($message);
+            }
+        }
+        else {
+            throw new Exception('Storage location not writeable');
+        }
+
+        return array(
+            'message' => $message,
+            'status' => $status
+        );
+    }
+
+    /**
+     * Move the tmp_file from _uploadedData to the new location
+     * provided by _storagePath and _saveFilename
+     * @return array
+     * @throws Exception
+     */
+    private function _moveUploadedFile() {
+        $message = "File storage failure";
+        $status = false;
+
+        $workwithPath = $this->_storagePath;
+        $workwithFilename = $this->_saveFilename;
+
+        if(!empty($workwithPath) && !empty($workwithFilename)) {
+            $_newFilename = Summoner::endsWith($workwithPath,'/') ? $workwithPath : $workwithPath.'/';
+            $_newFilename .= $workwithFilename;
+            if(move_uploaded_file($this->_uploadedData['tmp_name'], $_newFilename)) {
+                $status = true;
+                $message = $this->_shortURL;
+            }
+            else {
+                if(DEBUG) $message .= " $_newFilename";
+                throw new Exception($message);
+            }
+        }
+        else {
+            throw new Exception('Failing requirements for saving');
+        }
+
+        return array(
+            'message' => $message,
+            'status' => $status
+        );
+    }
+}
\ No newline at end of file
index f05aaf8a6056c8ed74c14cc5e6824dcbaad4d167..7ac3c460ebf5495a2b26f1ce41fe476455dd727f 100644 (file)
@@ -139,132 +139,41 @@ class Summoner {
 
 
     /**
-     * Simple helper to detect the $_FILE upload status
-     * Expects an array from $_FILE
-     * @param $file
+     * Simple helper to detect the $_FILES upload status
+     * Expects the error value from $_FILES['error']
+     * @param $error
      * @return array
      */
-    static function checkFileUploadStatus($file) {
+    static function checkFileUploadStatus($error) {
         $message = "Unknown upload error";
         $status = false;
 
-        if(isset($file['error'])) {
-            switch ($file['error']) {
-                case UPLOAD_ERR_OK:
-                    $message = "There is no error, the file uploaded with success.";
-                    $status = true;
-                break;
-                case UPLOAD_ERR_INI_SIZE:
-                    $message = "The uploaded file exceeds the upload_max_filesize directive in php.ini";
-                break;
-                case UPLOAD_ERR_FORM_SIZE:
-                    $message = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form";
-                break;
-                case UPLOAD_ERR_PARTIAL:
-                    $message = "The uploaded file was only partially uploaded";
-                break;
-                case UPLOAD_ERR_NO_FILE:
-                    $message = "No file was uploaded";
-                break;
-                case UPLOAD_ERR_NO_TMP_DIR:
-                    $message = "Missing a temporary folder";
-                break;
-                case UPLOAD_ERR_CANT_WRITE:
-                    $message = "Failed to write file to disk";
-                break;
-                case UPLOAD_ERR_EXTENSION:
-                    $message = "File upload stopped by extension";
-                break;
-            }
-        }
-
-        return array(
-            'message' => $message,
-            'status' => $status
-        );
-    }
-
-    /**
-     * Simple helper to detect the $_FILE type
-     * Expects an array from $_FILE
-     *
-     * @see https://www.php.net/manual/en/intro.fileinfo.php
-     *
-     * @param $file
-     * @return array
-     */
-    static function checkAllowedFiletype($file) {
-        $message = "Filetype not supported";
-        $status = false;
-
-        if(isset($file['tmp_name'])) {
-            $finfo = finfo_open(FILEINFO_MIME_TYPE);
-            $mime = finfo_file($finfo, $file['tmp_name']);
-            finfo_close($finfo);
-            if(strpos(SELFPASTE_ALLOWED_FILETYPES,$mime) !== false) {
-                $status = true;
-                $message = "Filetype allowed";
-            }
-            if(DEBUG) $message .= " $mime";
-        }
-
-        return array(
-            'message' => $message,
-            'status' => $status
-        );
-    }
-
-    /**
-     * Simple helper to create and make sure the storage
-     * location is available
-     * Expects an array from $_FILE
-     * with an extra key = storagepath
-     *
-     * @param $file
-     * @return array
-     */
-    static function checkStorage($file) {
-        $message = "File storage failure";
-        $status = false;
-
-        if(isset($file['storagepath']) && !empty($file['storagepath'])
-            && is_writable(SELFPASTE_UPLOAD_DIR)) {
-            if (mkdir($file['storagepath'],0777,true)) {
-                $message = "File storage creation success";
+        switch ($error) {
+            case UPLOAD_ERR_OK:
+                $message = "There is no error, the file uploaded with success.";
                 $status = true;
-            }
-        }
-
-        if(DEBUG) $message .= " ".$file['storagepath'];
-
-        return array(
-            'message' => $message,
-            'status' => $status
-        );
-    }
-
-    /**
-     * move the uploaded file.
-     * Depends on the _FILES info and the keys
-     * storagepath, short, shortUrl
-     * @param $file
-     * @return array
-     */
-    static function moveUploadedPasteFile($file) {
-        $message = "File storage failure";
-        $status = false;
-        //shortUrl
-
-        if(isset($file['storagepath']) && !empty($file['storagepath'])
-            && isset($file['short']) && !empty($file['short'])) {
-            $_newFilename = self::endsWith($file['storagepath'],'/') ? $file['storagepath'] : $file['storagepath'].'/';
-            $_newFilename .= $file['short'];
-            if(move_uploaded_file($file['tmp_name'], $_newFilename)) {
-                $status = true;
-                $message = $file['shortUrl'];
-            }
-
-            if(DEBUG) $message .= " $_newFilename";
+            break;
+            case UPLOAD_ERR_INI_SIZE:
+                $message = "The uploaded file exceeds the upload_max_filesize directive in php.ini";
+            break;
+            case UPLOAD_ERR_FORM_SIZE:
+                $message = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form";
+            break;
+            case UPLOAD_ERR_PARTIAL:
+                $message = "The uploaded file was only partially uploaded";
+            break;
+            case UPLOAD_ERR_NO_FILE:
+                $message = "No file was uploaded";
+            break;
+            case UPLOAD_ERR_NO_TMP_DIR:
+                $message = "Missing a temporary folder";
+            break;
+            case UPLOAD_ERR_CANT_WRITE:
+                $message = "Failed to write file to disk";
+            break;
+            case UPLOAD_ERR_EXTENSION:
+                $message = "File upload stopped by extension";
+            break;
         }
 
         return array(
@@ -273,22 +182,10 @@ class Summoner {
         );
     }
 
-    /**
-     * Simple helper to create a new name
-     *
-     * @return string
-     * @throws Exception
-     */
-    static function createShort() {
-        $idstring = random_int(1000, 9999);
-        return self::b64sl_pack_id($idstring);
-    }
-
     /**
      * create a short string based on a integer
      *
      * @see https://www.jwz.org/base64-shortlinks/
-     *
      * @return string
      */
     static function b64sl_pack_id($id) {
@@ -304,24 +201,37 @@ class Summoner {
         return $id;
     }
 
+    /**
+     * Decode a base64-encoded big-endian integer of up to 64 bits.
+     *
+     * @see https://www.jwz.org/base64-shortlinks/
+     * @param $id
+     * @return false|int|string|string[]
+     */
+    static function b64sl_unpack_id($id) {
+        $id = str_replace ('-', '+', $id);             // decode URL-unsafe "+" "/"
+        $id = str_replace ('_', '/', $id);
+        $id = base64_decode ($id);
+        while (strlen($id) < 8) { $id = "\000$id"; }   // pad with leading NULs
+        $a = unpack ('N*', $id);                       // 32 bit big endian
+        $id = ($a[1] << 32) | $a[2];                   // pack top and bottom word
+        return $id;
+    }
+
     /**
      * create based on the given string a path
      * each char in string is a dir
-     * and add SELFPASTE_UPLOAD_DIR
-     * asd -> SELFPASTE_UPLOAD_DIR/a/s/d
+     * asdef -> a/s/d/e/f/
      * @param $string
-     * @return bool|string
+     * @return string
      */
-    static function createStoragePath($string) {
-        $p = false;
-
-        if(!empty($string) && is_writable(SELFPASTE_UPLOAD_DIR)) {
-            $p = SELFPASTE_UPLOAD_DIR.'/';
-            for($i=0;$i<strlen($string);$i++) {
-                $p .= $string[$i]."/";
+    static function forwardslashStringToPath($string) {
+        $ret = '';
+        if(!empty($string)) {
+            for ($i = 0; $i < strlen($string); $i++) {
+                $ret .= $string[$i] . "/";
             }
         }
-
-        return $p;
+        return $ret;
     }
 }