fix: 🛡️ don't leak extid & token in mai photos

This commit is contained in:
Raymond
2026-05-16 00:30:42 -04:00
parent f313f090f8
commit 243a9b2ddc
2 changed files with 19 additions and 9 deletions
+1 -3
View File
@@ -18,13 +18,11 @@
{:then photos}
{#if photos.length === 0}
<blockquote class="info">{t('maiphoto.none')}</blockquote>
{:else}
<blockquote class="info">{t('maiphoto.url_warning')}</blockquote>
{/if}
<div class="pictures">
{#each photos as photo}
<div class="photo-container">
<img class="rounded-2xl" src="{AQUA_HOST}/api/v2/game/mai2/my-photo/{photo}?token={token}" alt="Memorial" />
<img class="rounded-2xl" src="{AQUA_HOST}/api/v2/game/mai2/my-photo/{photo}" alt="Mai Photo" />
</div>
{/each}
</div>
@@ -17,6 +17,8 @@ import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2UserOption
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RestController
import java.security.MessageDigest
import kotlin.text.Charsets.UTF_8
import kotlin.jvm.optionals.getOrNull
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.memberProperties
@@ -207,7 +209,11 @@ class Maimai2(
}
val photoDir = UploadUserPhotoHandler.uploadDir.toFile().canonicalFile
val photoHashMap: MutableMap<String, String> = emptyMap<String, String>().toMutableMap()
// creating a ton of MD5 hashes every launch *probably* isn't ideal but it's better than exposing token AND extid...
@OptIn(ExperimentalStdlibApi::class)
@API("my-photo")
suspend fun myPhoto(@RP token: Str) = us.jwt.auth(token) { u ->
val find = "${u.ghostCard.extId}-"
@@ -215,15 +221,21 @@ class Maimai2(
?.map { it.name }
?.filter { it.startsWith(find) }
?.sorted()
?.map {
// generate MD5 hash of photo as to not expose details
if (!photoHashMap.containsKey(it))
photoHashMap[it] = MessageDigest.getInstance("MD5")
.digest(it.toByteArray(UTF_8)).toHexString()
photoHashMap[it]
}
?: emptyList()
}
@API("my-photo/{fileName}", produces = [MediaType.IMAGE_JPEG_VALUE])
suspend fun myPhoto(@RP token: Str, @PV fileName: Str) = us.jwt.auth(token) { u ->
val f = (photoDir / fileName)
if (!f.canonicalFile.startsWith(photoDir)) (403 - "Never gonna give you up")
if (!f.name.startsWith("${u.ghostCard.extId}-")) (403 - "Not your photo")
if (!f.exists()) (404 - "Photo not found")
f.readBytes()
suspend fun accessMyPhoto(@PV fileName: Str): ByteArray {
val trueFileName = photoHashMap.entries.firstOrNull { it.value == fileName }?.key
if (trueFileName.isNullOrEmpty()) 404 - "Photo does not exist"
val file = (photoDir / trueFileName)
return file.readBytes()
}
}