Score:1

Nginx: rewriting while checking args format with a regex

tr flag

I have an API endpoint like /my/api/PARAM1/abcd?arg1=val1&arg2=val2, and use a redirect to proxy to a fastcgi server elsewhere (using upstream).

Basically I have the current configuration :

location ~ "^/my/api/(?<api_name>[A-Z0-9]+)?$" {
    rewrite ^ "/cgi-bin/apiserver?api_name=${api_name}&p1=${arg_arg1}&p2=${arg_arg2}" last;
}

location /cgi-bin/apiserver {
    include fastcgi_params;
    fastcgi_index myapiserver?*;
    fastcgi_param SCRIPT_FILENAME /usr/lib/cgi-bin/myapiserver$fastcgi_script_name;
    fastcgi_pass my_fcgi_upstream;
}

The FCGI server is not something I control, I can't change the arguments names in the query string, nor the code it runs. I would like to protect it against attacks, like checking the format of the GET args sent to it (arg1 and arg2 in this example, sent as p1 and p2). Checking that they are real numbers, or that the string formats complies with a specific regex.

Ideally I also would like to check that a couple of parameters (p1, p2, p3, p4) lies in a specific whitelist (for example (0,1,2,3) is allowed but not (1,1,2,3).

For the first problem, I think we would achieve something using several location blocks or rewrites, but I can't find the correct way to do that. For the second improvement (params whitelist), I guess my only solution is to rely on some kind of Lua scripting in nginx? I would like to avoid at all costs to have some kind of applicative proxy between the FCGI server and nginx, so if I could do everyting within nginx, it would be awesome!

Thanks for the help.

Score:1
jp flag

Nginx uses a normalised URI when testing the regular expressions in the location block or rewrite statement. The arguments have been removed at this stage, and relocated to the $args and $arg_ variables.

The only effective way to test arguments is with an if statement, with or without a map block.

You could check the arguments at any point in your configuration, but assuming you want to check it as close to the fastcgi_pass as possible, you would place an if ... { return ...; } block inside the location /cgi-bin/apiserver block. Note that this is one of the valid cases for using if within a location block.

The final part of the question is best achieved using a map as you can concatenate p1, p2, p3 and p4 into a single string and run it past multiple regular expressions.

For example:

map $arg_p1,$arg_p2,$arg_p3,$arg_p4 $fails {
~^1,2,3,4$  0;
default     1;
}

server {
    ...

    location /cgi-bin/apiserver {
        if (fails) { return 403; }
        ...
    }
}

You could also test p1 and p2 individually using the same map, by using .* in the other positions. But it's probably unnecessary as they must be numbers to match the whitelist anyway.

Note that the map block must be placed outside the server block. Also, note that regular expressions are evaluated in order until a match is found.

fred59xc06 avatar
tr flag
Thanks for the helpful answer, I didn't think of using `if` as it's usually evil :). I'll give it a try.
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.