Nginx - декодирует параметр запроса URL и передает его как заголовок запроса. - PullRequest
0 голосов
/ 14 января 2019

Мне нужно отправить некоторые базовые учетные данные (например, user:pass) в nginx в виде параметра запроса (es. http://example.com?BASIC_AUTH=dXNlcjpwYXNz) и получить возможность пересылать их в более обычной форме заголовка Authorization: Basic dXNlcjpwYXNz в целевой сервер за прокси.

Я уже могу получить значение закодированной строки авторизации с помощью регулярного выражения. Проблема в том, что очень часто это значение может содержать какой-то символ, который должен быть закодирован в процентах в URL. Es. user:pass! -> ?BASIC_AUTH=dXNlcjpwYXNzIQ== становится ?BASIC_AUTH=dXNlcjpwYXNzIQ%3D%3D

Поэтому, когда я пересылаю запрос целевому серверу, я в конечном итоге указываю Authorization: Basic dXNlcjpwYXNzIQ%3D%3D, который целевой сервер отклонит, что дает 401 Несанкционированный.

Как заставить nginx декодировать строку авторизации перед установкой заголовка авторизации? Заранее благодарим за помощь.

Примечание: я не могу отправить строку авторизации в заголовке авторизации в первую очередь из-за некоторых специфических для приложения ограничений.

1 Ответ

0 голосов
/ 23 января 2019

«Чистый» раствор nginx

К сожалению, nginx не предоставляет расширенный набор строковых операций. Я думаю, что нет способа сделать глобальный поиск и замену через некоторую строку (что может быть решением, если мы могли бы заменить все %2B на +, %2F на / и %3D на =). Однако существуют обстоятельства, при которых nginx выполняет urldecoding некоторой строки - когда эта строка становится частью URI, который будет перенаправлен на прокси-сервер вышестоящего уровня.

Таким образом, мы можем добавить значение BASIC_AUTH аргумента запроса к URI и сделать запрос прокси для себя:

# Main server block
server {
    listen 80 default_server;
    ...

    location / {
        if ($arg_basic_auth) {
            # "basic_auth" request argument is present,
            # append "/decode_basic_auth/<BASE64_token>" to the URI
            # and go to the next location block
            rewrite ^(.*)$ /decode_basic_auth/$arg_basic_auth$1 last;
        }
        # No "basic_auth" request argument present,
        # can do a proxy call from here without setting authorization headers
        ...
    }

    location /decode_basic_auth/ {
        # This will be an internal location only
        internal;
        # Remove "basic_auth" request argument from the list of arguments
        if ($args ~* (.*)(^|&)basic_auth=[^&]*(\2|$)&?(.*)) {
            set $args $1$3$4;
        }
        # Some hostname for processing proxy subrequests
        proxy_set_header Host internal.basic.auth.localhost;
        # Do a subrequest to ourselfs, preserving other request arguments
        proxy_pass http://127.0.0.1$uri$is_args$args;
    }
}

# Additional server block for proxy subrequests processing
server {
    listen 80;
    server_name internal.basic.auth.localhost;

    # Got URI in form "/decode_basic_auth/<BASE64_token>/<Original_URI>"
    location ~ ^/decode_basic_auth/([^/]+)(/.*)$ {
        proxy_set_header Authorization "Basic $1";
        # Setup other HTTP headers here
        ...
        proxy_pass http://<upstream_server>$2$is_args$args;
    }

    # Do not serve other requests
    location / {
        return 444;
    }
}

Возможно, это не очень элегантное решение, но оно протестировано и работает.

OpenResty / ngx_http_lua_module

Эту проблему легко решить с помощью openresty или ngx_http_lua_module с использованием функции ngx.escape_uri :

server {
    listen 80 default_server;
    ...

    location / {
        set $auth $arg_basic_auth;
        if ($args ~* (.*)(^|&)basic_auth=[^&]*(\2|$)&?(.*)) {
            set $args $1$3$4;
        }
        rewrite_by_lua_block {
            ngx.var.auth = ngx.unescape_uri(ngx.var.auth)
        }
        proxy_set_header Authorization "Basic $auth";
        # Setup other HTTP headers here
        ...
        proxy_pass http://<upstream_server>;
    }
}
...