web-dev-qa-db-fra.com

Évitez les paramètres de requête de décodage nginx sur proxy_pass (équivalent à AllowEncodedSlashes NoDecode)

J'utilise nginx comme équilibreur de charge devant plusieurs tomcats. Dans mes demandes entrantes, j'ai encodé des paramètres de requête. Mais lorsque la demande arrive à Tomcat, les paramètres sont décodés:

demande entrante à nginx:

curl -i "http://server/1.1/json/T;cID=1234;pID=1200;rF=http%3A%2F%2Fwww.google.com%2F"

demande entrante à Tomcat:

curl -i "http://server/1.1/json/T;cID=1234;pID=1200;rF=http:/www.google.com/"

Je ne veux pas que mes paramètres de demande soient transformés, car dans ce cas, mon Tomcat génère une erreur 405.

Ma configuration nginx est la suivante:

upstream tracking  {
    server front-01.server.com:8080;
    server front-02.server.com:8080;
    server front-03.server.com:8080;
    server front-04.server.com:8080;
}

server {
    listen 80;
    server_name tracking.server.com;
    access_log /var/log/nginx/tracking-access.log;
    error_log  /var/log/nginx/tracking-error.log;

    location / {
        proxy_pass  http://tracking/webapp;
    }
}

Dans ma configuration actuelle d'équilibreur de charge Apache, j'ai la directive AllowEncodedSlashes qui préserve mes paramètres encodés:

AllowEncodedSlashes NoDecode

J'ai besoin de passer d'Apache à nginx.

Ma question est tout à fait le contraire de cette question: Évitez que nginx n'échappe aux paramètres de requête sur proxy_pass

23

J'ai finalement trouvé la solution: je dois passer $request_uri paramètre :

location / {
    proxy_pass  http://tracking/webapp$request_uri;
}

De cette façon, les caractères qui ont été encodés dans la requête d'origine ne seront pas décodés, c'est-à-dire qu'ils seront transmis tels quels au serveur mandaté.

44

réponse de Jean est bon, mais cela ne fonctionne pas avec les sous-localisations. Dans ce cas, la réponse la plus générique est:

location /path/ {
  if ($request_uri ~* "/path/(.*)") {
    proxy_pass http://tracking/webapp/$1;
  }
}
13
user1338062

Il existe une option documentée pour la directive Nginx proxy_pass

S'il est nécessaire de transmettre l'URI sous la forme non traitée, la directive proxy_pass doit être utilisée sans partie URI :

location  /some/path/ {
  proxy_pass   http://127.0.0.1;
}

dans votre cas, ça pourrait être comme ça. Ne vous inquiétez pas de l'URI de demande, il sera transmis aux serveurs en amont

location / {
    proxy_pass  http://tracking;
}

J'espère que ça aide.

11
Casey

Notez que le décodage d'URL, communément appelé $uri "normalisation" dans la documentation de nginx, se produit avant l'IFF backend:

  • soit tout URI est spécifié dans proxy_pass lui-même, même si la barre oblique de fin est isolée,

  • ou, l'URI est modifié pendant le traitement, par exemple via rewrite.


Les deux conditions sont explicitement documentées à http://nginx.org/r/proxy_pass (c'est moi qui souligne):

  • Si la proxy_pass la directive est spécifiée avec un URI, puis lorsqu'une requête est transmise au serveur, la partie d'un normalisé l'URI de demande correspondant à l'emplacement est remplacé par un URI spécifié dans la directive

  • Si proxy_pass est spécifié sans URI, l'URI de la demande est transmis au serveur dans même formulaire que celui envoyé par un client lors du traitement de la demande originale, ou l'URI complet de la demande normalisé est transmis quand traitement du URI modifié


La solution dépend de la nécessité ou non de modifier l'URL entre le front-end et le backend.

  • Si aucun changement d'URI n'est requis:

    # map `/foo` to `/foo`:
    location /foo {
        proxy_pass  http://localhost:8080;  # no URI -- not even just a slash
    }
    
  • Sinon, si vous devez échanger ou mapper /api du front-end avec /app sur le backend, vous pouvez alors obtenir l'URI d'origine à partir du $request_uri , et utilisez les directives rewrite sur les $uri variable similaire à un DFA (BTW, si vous voulez plus d'action rewrite DFA, jetez un œil à mdoc.s ). Notez que le return 400 une partie est nécessaire au cas où quelqu'un essaierait de contourner votre deuxième règle de réécriture, car elle ne correspondrait pas à quelque chose comme //api/.

    # map `/api` to `/app`:
    location /foo {
        rewrite  ^  $request_uri;            # get original URI
        rewrite  ^/api(/.*)  /app$1  break;  # drop /api, put /app
        return 400;   # if the second rewrite won't match
        proxy_pass    http://localhost:8080$uri;
    }
    
  • Si vous souhaitez simplement ajouter un préfixe pour le backend, vous pouvez simplement utiliser le $request_uri variable tout de suite:

    # add `/webapp` to the backend:
    location / {
        proxy_pass    http://localhost:8080/webapp$request_uri;
    }
    

Vous voudrez peut-être également jeter un coup d'œil à ne réponse connexe , qui montre quelques tests du code similaires à ce qui précède.

7
cnst