PdfDragButton: Text-Formate wieder setzen, damit Ziel-Fenster reagiert
Das Weglassen von text/uri-list + text/plain (optionale R131-Haertung #2) hatte den Drop ins Mail-Fenster gebrochen: web-basierte Ziele verstehen DownloadURL (Nativ-Format) nicht und reagierten gar nicht. Jetzt werden im Normalfall wieder alle drei Formate gesetzt - aber mit dem kurzlebigen 60s-Download-Token (R131-Empfehlung #1, der eigentliche Fix des Token-SCOPE). Ein Fehl-Drop zeigt damit hoechstens eine 60s gueltige Downloads-only-URL statt des 15-Min-Voll-Access-Tokens. Nur im seltenen Warm-up-Rennen wird ausschliesslich DownloadURL mit Access-Token-Fallback gesetzt (kein Klartext-Leak). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,18 +23,19 @@ import { authApi, getAccessToken } from '../../services/api';
|
|||||||
* Fallback (Datei im neuen Tab öffnen) bleibt aber nutzbar.
|
* Fallback (Datei im neuen Tab öffnen) bleibt aber nutzbar.
|
||||||
*
|
*
|
||||||
* SICHERHEIT (Pentest R131):
|
* SICHERHEIT (Pentest R131):
|
||||||
* - Es wird AUSSCHLIESSLICH `DownloadURL` gesetzt, KEINE `text/plain`-
|
* - In die URL wird ein KURZLEBIGER 60s-Download-Token
|
||||||
* oder `text/uri-list`-Repräsentation. `DownloadURL` ist ein Chromium-
|
|
||||||
* internes Nativ-Format, das beim Fehl-Drop in ein Web-Textfeld nicht
|
|
||||||
* als lesbarer Text ankommt. Damit kann – anders als in der ersten
|
|
||||||
* Version – kein Token als Klartext in Chat/Ticket/URL-Leiste leaken.
|
|
||||||
* - In die URL wird bevorzugt ein KURZLEBIGER 60s-Download-Token
|
|
||||||
* (`authApi.getDownloadToken()`, `type:"download"`, nur `?token=`
|
* (`authApi.getDownloadToken()`, `type:"download"`, nur `?token=`
|
||||||
* nutzbar) geschrieben statt des 15-Min-Access-Tokens. Er wird
|
* nutzbar) geschrieben statt des 15-Min-Access-Tokens mit vollen
|
||||||
* proaktiv (mount + hover) vorgewärmt. Nur im seltenen Rennen „Drag
|
* Rechten. Er wird proaktiv (mount + hover) vorgewärmt. Das ist der
|
||||||
* bevor der Token da ist" fällt es auf den Access-Token zurück –
|
* wesentliche Fix (Token-SCOPE): ein Fehl-Drop kann höchstens eine 60s
|
||||||
* und auch dann nur innerhalb von DownloadURL, nie als Klartext.
|
* gültige Downloads-only-URL zeigen statt eines Voll-Access-Tokens.
|
||||||
* Der Drag wird NIE abgebrochen (sonst käme im Ziel keine Datei an).
|
* - Es werden im Normalfall bewusst alle drei Formate gesetzt
|
||||||
|
* (`DownloadURL` für Explorer/Outlook-Desktop, `text/uri-list` +
|
||||||
|
* `text/plain` für web-basierte Mail-/Editor-Ziele) – sonst reagiert
|
||||||
|
* das Ziel-Fenster beim Drop nicht. Nur im seltenen Rennen „Drag bevor
|
||||||
|
* der Token da ist" wird ausschließlich `DownloadURL` mit Access-Token-
|
||||||
|
* Fallback gesetzt (Nativ-Format, kein Klartext-Leak). Der Drag wird
|
||||||
|
* NIE abgebrochen (sonst käme im Ziel keine Datei an).
|
||||||
*/
|
*/
|
||||||
interface PdfDragButtonProps {
|
interface PdfDragButtonProps {
|
||||||
/** Server-Pfad der Datei (`documentPath`). Ohne Pfad wird nichts gerendert. */
|
/** Server-Pfad der Datei (`documentPath`). Ohne Pfad wird nichts gerendert. */
|
||||||
@@ -114,23 +115,38 @@ export default function PdfDragButton({
|
|||||||
const name = safeBase.includes('.') ? safeBase : `${safeBase}${ext ? `.${ext}` : ''}`;
|
const name = safeBase.includes('.') ? safeBase : `${safeBase}${ext ? `.${ext}` : ''}`;
|
||||||
|
|
||||||
const handleDragStart = (e: React.DragEvent) => {
|
const handleDragStart = (e: React.DragEvent) => {
|
||||||
// Bevorzugt den kurzlebigen 60s-Download-Token (auf mount+hover
|
// Der Drag wird NIE abgebrochen (sonst käme im Ziel gar keine Datei an).
|
||||||
// vorgewärmt). Ist er im seltenen Rennen noch nicht da, fällt es auf den
|
const dl = cachedToken;
|
||||||
// Access-Token zurück – NUR innerhalb von DownloadURL. Wichtig: der Drag
|
const absolute = (t: string) => `${window.location.origin}${fileUrl(path, { token: t })}`;
|
||||||
// wird NIE abgebrochen (sonst käme im Mail-Fenster gar keine Datei an).
|
|
||||||
const token = cachedToken || getAccessToken() || '';
|
|
||||||
if (!cachedToken) warmDownloadToken(); // für den nächsten Drag vorwärmen
|
|
||||||
const absolute = `${window.location.origin}${fileUrl(path, { token })}`;
|
|
||||||
try {
|
try {
|
||||||
// NUR DownloadURL setzen – bewusst KEINE text/plain- oder
|
if (dl) {
|
||||||
// text/uri-list-Repräsentation. DownloadURL ist ein Chromium-internes
|
// Normalfall: kurzlebiger 60s-Download-Token (auf mount+hover
|
||||||
// Nativ-Format und wird bei einem Fehl-Drop in ein Web-Textfeld nicht
|
// vorgewärmt). Alle drei Formate setzen, damit sowohl Desktop-Ziele
|
||||||
// als lesbarer Text ausgegeben; so kann kein Token als Klartext leaken
|
// (Explorer/Outlook via DownloadURL) als auch web-basierte Mail-/
|
||||||
// (Pentest R131). Für Explorer/Outlook ist DownloadURL das relevante
|
// Editor-Fenster (die nur text/uri-list bzw. text/plain verstehen)
|
||||||
// Format – die entfernten Text-Formate waren dafür nie nötig.
|
// auf den Drop reagieren.
|
||||||
e.dataTransfer.setData('DownloadURL', `${mime}:${name}:${absolute}`);
|
//
|
||||||
|
// Pentest R131: Der wesentliche Fix ist der Token-SCOPE – hier steckt
|
||||||
|
// NUR ein 60s gültiger Downloads-only-Token in der URL, nicht mehr
|
||||||
|
// der 15-Min-Access-Token mit vollen Rechten. Ein versehentlicher
|
||||||
|
// Drop in ein Textfeld kann also höchstens eine 60s-Downloads-URL
|
||||||
|
// zeigen (Empfehlung #1). Das Weglassen der Text-Formate (#2) war
|
||||||
|
// „optional" und hat das Ziehen ins Mail-Fenster gebrochen → bewusst
|
||||||
|
// nicht umgesetzt.
|
||||||
|
const url = absolute(dl);
|
||||||
|
e.dataTransfer.setData('DownloadURL', `${mime}:${name}:${url}`);
|
||||||
|
e.dataTransfer.setData('text/uri-list', url);
|
||||||
|
e.dataTransfer.setData('text/plain', url);
|
||||||
|
} else {
|
||||||
|
// Seltenes Rennen: Download-Token noch nicht vorgewärmt. Dann NUR
|
||||||
|
// DownloadURL mit Access-Token-Fallback setzen (Nativ-Format, wird in
|
||||||
|
// Web-Textfeldern nicht als Klartext ausgegeben) – kein Token-Leak
|
||||||
|
// als Text. Für den nächsten Drag vorwärmen.
|
||||||
|
e.dataTransfer.setData('DownloadURL', `${mime}:${name}:${absolute(getAccessToken() || '')}`);
|
||||||
|
warmDownloadToken();
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* manche Browser werfen bei DownloadURL – dann passiert einfach nichts */
|
/* manche Browser werfen bei DownloadURL – dann greifen die Text-Formate */
|
||||||
}
|
}
|
||||||
e.dataTransfer.effectAllowed = 'copy';
|
e.dataTransfer.effectAllowed = 'copy';
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user