Relaie les couvertures en même origine, pour les mettre en cache hors-ligne
Le cache hors-ligne des couvertures ne marchait que pour OpenLibrary : il lit les octets par fetch(), donc exige un en-tête CORS, alors que le formulaire livre accepte n'importe quelle URL. Ces images s'affichaient (une <img> n'a que faire du CORS) sans jamais pouvoir être rangées — et le fetch repartait à chaque affichage puisque rien n'était stocké. GET /api/couvertures relaie l'image depuis notre serveur. Un proxy est une surface SSRF : il est borné par deux verrous indépendants — l'URL doit déjà exister en base comme couverture, et la connexion ne s'ouvre que vers une adresse publiquement routable. Ce second verrou vit dans le ConnectCallback, pas dans une pré-vérification DNS, ce qui ferme aussi le DNS rebinding — et c'est ce qui permet de suivre les redirections, indispensables puisque covers.openlibrary.org répond 302. Tout refus répond 404 : distinguer les cas ferait du point d'entrée un oracle sur les URL connues et sur le réseau du serveur. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using MaBibli.Api.Data;
|
||||
using MaBibli.Api.Endpoints;
|
||||
using MaBibli.Api.Services.Catalogue;
|
||||
using MaBibli.Api.Services.Couvertures;
|
||||
using MaBibli.Api.Services.Identite;
|
||||
using MaBibli.Api.Services.Isbn;
|
||||
using MaBibli.Api.Services.Prets;
|
||||
@@ -50,6 +51,31 @@ builder.Services.AddHttpClient<IOpenLibraryClient, OpenLibraryClient>(http =>
|
||||
|
||||
builder.Services.AddScoped<IIsbnLookupService, IsbnLookupService>();
|
||||
|
||||
// Relais de couvertures : rend une image de MÊME ORIGINE, seule façon de la mettre en cache
|
||||
// hors-ligne quand son hébergeur n'envoie pas d'en-tête CORS (le formulaire livre accepte
|
||||
// n'importe quelle URL de couverture).
|
||||
//
|
||||
// ⚠️ Les deux réglages du handler ne sont PAS des détails de confort :
|
||||
// - ConnectCallback n'ouvre la socket que vers une adresse publiquement routable, ce qui
|
||||
// ferme le SSRF y compris par DNS rebinding (voir GardeAdresses) ;
|
||||
// - les redirections sont SUIVIES, mais bornées à 3. Elles avaient d'abord été coupées, à
|
||||
// tort : `covers.openlibrary.org` répond 302 (mesuré), et beaucoup de CDN font de même —
|
||||
// les couper refusait donc les couvertures les plus courantes. Les suivre reste sûr parce
|
||||
// que le garde agit à la CONNEXION : il s'applique à chaque saut, cible de redirection
|
||||
// comprise. C'est la position du garde, et non l'interdiction des redirections, qui ferme
|
||||
// le SSRF.
|
||||
builder.Services.AddHttpClient<IServiceCouvertures, ServiceCouvertures>(http =>
|
||||
{
|
||||
http.Timeout = timeoutSources;
|
||||
http.DefaultRequestHeaders.UserAgent.ParseAdd("MaBibli/0.1 (bibliotheque personnelle auto-hebergee)");
|
||||
})
|
||||
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
|
||||
{
|
||||
AllowAutoRedirect = true,
|
||||
MaxAutomaticRedirections = 3,
|
||||
ConnectCallback = GardeAdresses.ConnecterAsync,
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Une seule base SQLite, migrée au démarrage : pas d'étape manuelle à l'installation YunoHost.
|
||||
@@ -98,6 +124,7 @@ app.MapSeriesEndpoints();
|
||||
app.MapRevuesEndpoints();
|
||||
app.MapBibliographieEndpoints();
|
||||
app.MapIdentiteEndpoints();
|
||||
app.MapCouverturesEndpoints();
|
||||
|
||||
// ⚠️ Le fallback a son propre pipeline : il NE passe PAS par les StaticFileOptions posées
|
||||
// ci-dessus. Sans lui repasser les mêmes options, « / » — c'est-à-dire le start_url de la PWA,
|
||||
|
||||
Reference in New Issue
Block a user