Nginx: Reject request if header is not present or wrong - nginx

If I have the headers: X_HEADER1 & X_HEADER2, I want to reject all requests if either of these headers are not set or do not contain the correct values. What is the best way to do this?
Thanks

You can use two IF statements either before or in the location block to inspect the headers and then return a 403 error code if it is present. Alternatively, you can use those IF statements to rewrite to a specific location block and deny all in that location:
if ($http_x_custom_header) {
return 403;
}
Reference:
https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/
https://nginx.org/en/docs/http/ngx_http_access_module.html
Adding more detail per comment/request:
if ($http_x_custom_header) {
return 405;
}
this looks to see if header exists
if you want to check to see if the correct values exist, then you first need to map the correct values to a variable.
map $http_x_header $is_ok {
default "0";
Value1 "1";
Value2 "1";
Value3 "1";
}
if ($is_ok) {
return 405;
}
this first maps the header value to whether or not its ok, then checks to see if the variable is ok.
EDIT: Removed semicolon after map block since this causes an error.

I researched a lot to solve a simple problem: Only allow proxy_pass if request have a specific token in the header. I tried all the answers here and nothing worked how I liked. My final solution is:
location /api {
proxy_http_version 1.1;
if ($http_authorization != "Bearer 1234") {
return 401;
}
proxy_pass http://app:3000/;
}
References:
NGINX not equal to
nginx - read custom header from upstream server
https://serverfault.com/questions/490760/nginx-location-exact-match-matches-beyond-arguement
https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/

If you want to allow the HTTP requests only based on some valid header values which are placed inside the response header, one possible way is to use OpenResty tool to apply such restrictions.
The following example allow access to only requests having values "name1" or "name2" for header1:
header_filter_by_lua '
local val = ngx.header["header1"]
if val then
if (val ~= "name1") and (val ~= "name2") then
return ngx.exit(400)
end
end
';

Related

How to access a variable value set inside Lua block in proxy pass

I am new to nginx.
I have a variable set $myuser and updating the value ngx.var.myuser inside rewrite_by_lua. I want to pass this as a header in proxy_pass.
I am using proxy_add_header to add $myuser but I am not getting the updated value.
code block:
location /
{
set $myuser '';
rewrite_by_lua_block
{
local user=//code to get the updated value
//perform some redirection logic on error
ngx.var.myuser= user
}
proxy_pass "https://backend.com"
proxy_set_header myheader $myuser
}
value of $myuser is coming as empty but it's getting retrieved fine inside the lua block.
Below code worked. No need to set variable anduse it in proxy_set_header. We can set the header inside the lua block
rewrite_by_lua_block
{
local user=//code to get the updated value
//perform some redirection logic on error
ngx.req.set_header("myheader", user)
}

How to proxy pass when condition (related to param presence) is not met

In Nginx I need to proxy pass some of the request made to the homepage.
Specificaly, I need to proxy pass them in case of URLs like:
example.com
example.com/
example.com/?anyTypeOfParameterButNotP
However, when the request contains ?p= parameter in the URL, I would like to handle it the regular way (as if there wasn't any proxy_pass at all).
So, the URLs I would like not to proxy pass would be:
example.com/?p=1234
example.com/?p=1234&foo=bar
How can I achieve that?
I've tried some if statements, some variables solutions, but I didn't figure out any way to work it out.
If you are using something that can end with "break;"
location / {
if ($query_string ~ p=) {
## regular way - try_file? and end with "break;"
}
proxy_pass http://somwhere;
}
--- or here is an if-else condition for unusual cases
location / {
set $skip_proxy 0;
if ($query_string ~ p=) {
set $skip_proxy 1;
}
if ($skip_proxy = 1) {
# when query does contain ?p=
}
if ($skip_proxy = 0) {
# when query does NOT contain ?p=
# proxy_pass http://somwhere; ?
}
}

How to block Nginx requests where http_referer matches requested URL

I am trying to block a webcrawler that uses the requested page as the http_referer, and I can't figure out what variable to compare it to.
e.g.
location / {
if ($the_variable_with_the_current_full_uri = $http_referer) {
return 403;
}
}
The variable has to match protocol, host, and URL, so that internal redirects from http to https don't get blocked.
So if someone requests "https://www.example.com/pages/1" with the $http_referer of "https://www.example.com/pages/1", it should be blocked.
As a secondary question, is it possible block requests on two conditions: where the above check matches, as well as matching a specific user agent string?
The full URL can be constructed by concatenating a number of variables together.
For example:
$scheme://$host$request_uri
The secondary condition could be handled using a map (see this document).
For example:
map $http_user_agent $my_http_referer {
default "";
blahblah $http_referer;
}
server {
...
if ($scheme://$host$request_uri = $my_http_referer) { return 403; }
...
}

Nginx rewrite: how to change URL from dynamic to static

I'm configuring nginx as reverse proxy.
I need to change (rewrite?) the URLs, example: when the request (to nginx Reverse Proxy) is "http://example.com/test/?username=test1;password=passwdtest1" it will must "modified" to the main server as "http://example.com/test/?username=production;password=passwdproduction1".
Consider that in the original request the fields "username=test1;password=passwdtest1" are not always the same (they changes), instead the "modified" to the main server are always the same.
Others example to be more clear:
"/test/?username=test1;password=passwdtest1" -> "/test/?username=production;password=passwdproduction1"
"/test/?username=test1876;password=somepasswd" -> "/test/?username=production;password=passwdproduction1"
"/test/?username=somevalues;password=somepasswdvalue" -> "/test/?username=production;password=passwdproduction1"
So, independently to what are the values of "?username=somevalues;password=somepasswdvalue" it should always become "?username=production;password=passwdproduction1".
Thanks for your help!
A little late on the answer but this should work for you:
location ~* /test/? {
if ($arg_username ~ "^$|\s+") { return 404; }
if ($arg_password ~ "^$|\s+") { return 404; }
rewrite ^ /test?username=production&password=passwdproduction1? permanent;
}
The code above checks if it is within the example.com/test path. If it is it will check if the user name or the password variable are present and not empty in the query string. In case if any isn't present or is empty it will return a 404 else it will redirect you to the preferred url.
By the way, instead of the semicolon in your example urls I would use an ampersand (&).

Return with changing number in source url

When user visits:
/profile.php?mode=viewprofile&u=[NUMBER FROM 1 TO 4000]
I want nginx to return:
/memberlist.php?mode=viewprofile&u=[SAME NUMBER]
How can I do it? Thank you for help.
The problem is that you need to match /profile.php and mode=viewprofile which is not trivial nginx. There are a number of ways to achieve it.
You could replicate the location ~\.php$ block and add the conditional redirection there:
location = /profile.php {
if ($arg_mode = viewprofile) {
return 301 /memberlist.php?$args;
}
... # add location ~\.php$ stuff here
}
Alternatively, check the $request_uri (which contains the original URI including query string), early in the server block:
if ($request_uri ~ "^/profile\.php\?mode=viewprofile&") {
return 301 /memberlist.php?$args;
}
See this caution on the use of the if statement.

Resources