From a5d80b8f9bcd8a7efd56dc66f2be889216d69b3f Mon Sep 17 00:00:00 2001
From: mrilyew <99399973+mrilyew@users.noreply.github.com>
Date: Sun, 18 May 2025 17:53:39 +0300
Subject: [PATCH] fix(thumbnails): use hash instead of real id (#1234)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Заменяется реальный id в ссылке превью на хэш, чтобы нельзя было делать
перебор или просмотр рандомных картинок. В данный момент не особо
критично (потому что можно перебирать посты), но при появлении вложений
в диалогах будет

Co-authored-by: Alexander Minkin <weryskok@gmail.com>
---
 Web/Models/Entities/Photo.php      |  2 +-
 Web/Models/Repositories/Photos.php | 11 +++++++++++
 Web/Presenters/PhotosPresenter.php |  4 ++--
 Web/routes.yml                     |  4 +++-
 4 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/Web/Models/Entities/Photo.php b/Web/Models/Entities/Photo.php
index 57ab2b8a..4414f085 100644
--- a/Web/Models/Entities/Photo.php
+++ b/Web/Models/Entities/Photo.php
@@ -187,7 +187,7 @@ class Photo extends Media
         foreach ($sizes as $id => $meta) {
             if (isset($meta[3]) && !$meta[3]) {
                 $res[$id] = (object) [
-                    "url"    => ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/photos/thumbnails/" . $this->getId() . "_$id.jpeg",
+                    "url"    => ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/photos/thumbnails/" . $this->getRecord()->hash . "_$id.jpeg",
                     "width"  => null,
                     "height" => null,
                     "crop"   => null,
diff --git a/Web/Models/Repositories/Photos.php b/Web/Models/Repositories/Photos.php
index 4de62025..f4fd68b9 100644
--- a/Web/Models/Repositories/Photos.php
+++ b/Web/Models/Repositories/Photos.php
@@ -28,6 +28,17 @@ class Photos
         return new Photo($photo);
     }
 
+    public function getByHash(string $hash): ?Photo
+    {
+        $photo = $this->photos->where("hash", $hash)->fetch();
+
+        if (!$photo) {
+            return null;
+        }
+
+        return new Photo($photo);
+    }
+
     public function getByOwnerAndVID(int $owner, int $vId): ?Photo
     {
         $photo = $this->photos->where([
diff --git a/Web/Presenters/PhotosPresenter.php b/Web/Presenters/PhotosPresenter.php
index 6c294fdd..19c67fbe 100644
--- a/Web/Presenters/PhotosPresenter.php
+++ b/Web/Presenters/PhotosPresenter.php
@@ -229,9 +229,9 @@ final class PhotosPresenter extends OpenVKPresenter
         $this->renderPhoto($photo->getOwner(true)->getId(), $photo->getVirtualId());
     }
 
-    public function renderThumbnail($id, $size): void
+    public function renderThumbnail($hash, $size): void
     {
-        $photo = $this->photos->get($id);
+        $photo = $this->photos->getByHash((string) $hash);
         if (!$photo || $photo->isDeleted()) {
             $this->notFound();
         }
diff --git a/Web/routes.yml b/Web/routes.yml
index 9738b740..6a3598bb 100644
--- a/Web/routes.yml
+++ b/Web/routes.yml
@@ -173,8 +173,10 @@ routes:
       handler: "Photos->photo"
     - url: "/photo{num}_{num}/like"
       handler: "Photos->like"
-    - url: "/photos/thumbnails/{num}_{text}.jpeg"
+    - url: "/photos/thumbnails/{?hash}_{text}.jpeg"
       handler: "Photos->thumbnail"
+      placeholders:
+          hash: "[A-z0-9\\-_\\/]{15,}"
     - url: "/photos/{text}"
       handler: "Photos->absolutePhoto"
     - url: "/photo{num}_{num}/edit"