Authorization header missing in django rest_framework, is apache to blame? - apache

I've managed to extend TokenAuthentication and I have a working model when using the request session to store my tokens, however when I attempt to pass Authorization as a header parameter as described here, I noticed that my Responses come back without the META variable HTTP_AUTHORIZATION. I also noticed that if I pass "Authorization2" as a header parameter that it is visible in the request:
{
'_content_type': '',
'accepted_media_type': 'application/json',
'_request': <WSGIRequest
path:/api/test_auth/,
GET:<QueryDict: {}>,
POST:<QueryDict: {}>,
COOKIES:{
'MOD_AUTH_CAS_S': 'ba90237b5b6a15017f8ca1d5ef0b95c1',
'csrftoken': 'VswgfoOGHQmbWpCXksGUycj94XlwBwMh',
'sessionid': 'de1f3a8eee48730dd34f6b4d41caa210'
},
META:{
'DOCUMENT_ROOT': '/etc/apache2/htdocs',
'GATEWAY_INTERFACE': 'CGI/1.1',
'HTTPS': '1',
'HTTP_ACCEPT': '*/*',
'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
'HTTP_ACCEPT_ENCODING': 'gzip,deflate,sdch',
'HTTP_ACCEPT_LANGUAGE': 'en-US,en;q=0.8',
'HTTP_AUTHORIZATION2': 'Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4c',
...
My first guess is that the authorization header is being removed by apache, and I have read a few S/O questions that state that apache will throw out the value if it does not match basic authorization and authenticate, but I have no idea how to allow the Authorization header to 'pass through' to Django and the WSGIRequest. Does anyone know how to solve this problem?
I also use mod_auth_cas and mod_proxy, if that changes anything..

If you are using Apache and mod_wsgi, then I found the easy solution to this in the official Django REST framework website
Apache mod_wsgi specific configuration
Note that if deploying to Apache using mod_wsgi, the authorization header is not passed through to a WSGI application by default, as it is assumed that authentication will be handled by Apache, rather than at an application level.
If you are deploying to Apache, and using any non-session based authentication, you will need to explicitly configure mod_wsgi to pass the required headers through to the application. This can be done by specifying the WSGIPassAuthorization directive in the appropriate context and setting it to 'On'.
# this can go in either server config, virtual host, directory or .htaccess
WSGIPassAuthorization On

Sorry to answer my own question minutes after asking it. But it turns out it was apache2 after all! After crawling the webs and looking through a few search results I found this in a comment:
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
Adding the above lines to my conf file seemed to solve all of my problems! Hopefully this helps users down the road!

It depends on which kind of Django/Apache deployment you did. You need to tell the correct Apache module to allow to pass "Authentication" HTTP header:
Apache/mod_wsgi:
WSGIPassAuthorization On
Apache/mod_fcgid:
FcgidPassHeader Authorization
In other words: many Apache modules filters "Authentication" HTTP header, so Django will not receive it. You have to be sure your Django App is receiving it in request.
See:
django_rest doc and Apache fcgid doc.
NOTE:
After modifying Apache configuration you'll need to restart apache daemon or tell to reload your .cgi file (i.e: touch my_site_fcgifile.fcgi).

The issue is the underscore in the HTTP header HTTP_AUTHORIZATION. Most webservers just ignores the headers with underscores.
Django dev server also exhibits the same, omitting headers with underscores.
This is the reason why Authorization2 works.
A Quick work around is to replace _ underscores in headers with - dashes,
Ex. changing the HTTP_AUTHORIZATION to HTTP-AUTHORIZATION

Related

How can I use mod_rewrite to proxy the userinfo component of a URI?

This has been driving me crazy. I have a web application that's being served via Apache Web Server. The database server that backs the application is Apache CouchDB, which exposes an HTTP API to retrieve documents and stream attachments.
I've secured the CouchDB database by providing a security object, which only allows certain users to access data within the database, and returns 401 for anonymous requests to HTTP endpoints.
I want to be able to map public URLs to document attachments stored within this database. So, I've attempted to create a rewrite rule inside my .htaccess file that proxies requests from certain URLs directly to CouchDB, while hardcoding the user credentials, like so:
## DOWNLOAD STREAM:
RewriteCond %{HTTP_HOST} ^domain.com$
RewriteRule download/(.*) http://user:pass#127.0.0.1:5984/database/$1 [P]
In an ideal world, the above example would take the following URL:
http://domain.com/download/UUID/attachment.ext
And proxy it to:
http://user:pass#127.0.0.1:5984/database/UUID/attachment.ext
This method does indeed proxy the request to CouchDB, but omits the userinfo component of the URI scheme. So, the request is treated as anonymous and I get a 401 error. The attachment is only streamed if I remove security from the database.
I've spent a couple of hours reading up on Apache configuration and experimenting to no avail. Web searches are fruitless because of all the related queries with similar keywords.
How can I ensure that mod_rewrite includes the username and password provided in the rewrite rule when it proxies to CouchDB?
I figured it out! Rather than including the username:password as part of the URI scheme, the Authorization header needs to be set independently. The following solution works completely within a .htaccess file, which is important since OS X periodically blows away settings inside VirtualHost sites:
SetEnvIf Request_URI ^/download/* ADD_COUCH_BASIC_AUTH
RequestHeader set Authorization "Basic XXXXXXXXXXXX" env=ADD_COUCH_BASIC_AUTH
## DOWNLOAD STREAM
RewriteCond %{HTTP_HOST} ^example.com$
RewriteRule download/(.*) http://127.0.0.1:5984/database/$1 [P]
The way this works: we use SetEnvIf to check whether the request path matches the path we want to proxy, and if so, set an arbitrary environment variable ADD_COUCH_BASIC_AUTH
On the subsequent line, we add a Basic Auth header to the outgoing request, only if the environment variable we set exists. So, the basic auth header will only be added when requesting a resource via /download/, thus sending authentication credentials to CouchDB.
Note: you'll have to Base64-encode your username:password credentials, and replace XXXXXXXXXXX with the encoded value. An easy way to do this, on a Mac:
echo -n 'user:pass' | openssl base64
Hope this helps somebody besides me!

Apache + php-fpm : headers stripped?

I had a PHP framework setup to use the php5_module for apache. The framework is custom written for a specific purpose and handles lots of things, for example the headers. On the new dev server there is a php-fpm instance setup and the old:
RewriteRule ^(.*)$ core.php
is replaced by ProxyPassMatch:
ProxyPassMatch ^(.*)$ unix:/tmp/php-fpm.sock|fcgi://127.0.0.1:9000/path/to/core.php
everything works great so far but it seems the headers that the php script adds are stripped or cause some kind of errors in the apache log.
Is it possible to use just the headers that the script adds and apache does not touch the headers at all?
That's just because you a saying that it's proxy server, which means for security reasons that's purged. as far as I can remember it's not really possible easily.
Anyway my suggestion is to use Nginx with PHP-FPM and if you need to use the Apache use the php module for apache as it's designed this way.
Hope it helped answering your question.

Apache 2.4 + PHP-FPM and Authorization headers

Summary:
Apache 2.4's mod_proxy does not seem to be passing the Authorization headers to PHP-FPM. Is there any way to fix this?
Long version:
I am running a server with Apache 2.4 and PHP-FPM. I am using APC for both opcode caching and user caching. As recommended by the Internet, I am using Apache 2.4's mod_proxy_fcgi to proxy the requests to FPM, like this:
ProxyPassMatch ^/(.*\.php)$ fcgi://127.0.0.1:9000/foo/bar/$1
The setup works fine, except one thing: APC's bundled apc.php, used to monitor the status of APC does not allow me to log in (required for looking at user cache entries). When I click "User cache entries" to see the user cache, it asks me to log in, clicking on the login button displays the usual HTTP login form, but entering the correct login and password yields no success. This function is working perfectly when running with mod_php instead of mod_proxy + php-fpm.
After some googling I found that other people had the same issue and figured out that it was because Apache was not passing the Authorization HTTP headers to the external FastCgi process. Unfortunately I only found a fix for mod_fastcgi, which looked like this:
FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9000 -pass-header Authorization
Is there an equivalent setting or some workaround which would also work with mod_proxy_fcgi?
Various Apache modules will strip the Authorization header, usually for "security reasons". They all have different obscure settings you can tweak to overrule this behaviour, but you'll need to determine exactly which module is to blame.
You can work around this issue by passing the header directly to PHP via the env:
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
See also Zend Server Windows - Authorization header is not passed to PHP script
In some scenarios, even this won't work directly and you must also change your PHP code to access $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] rather than $_SERVER['HTTP_AUTHORIZATION']. See When setting environment variables in Apache RewriteRule directives, what causes the variable name to be prefixed with "REDIRECT_"?
This took me a long time to crack, since it's not documented under mod_proxy or mod_proxy_fcgi.
Add the following directive to your apache conf or .htaccess:
CGIPassAuth on
See here for details.
Recently I haven'd problem with this arch.
In my environement, the proxy to php-fpm was configured as follow:
<IfModule proxy_module>
ProxyPassMatch ^/(.*\.php)$ fcgi://127.0.0.1:9000/usr/local/apache2/htdocs/$1
ProxyTimeout 1800
</IfModule>
I fixed the issue set up the SetEnvIf directive as follow:
<IfModule proxy_module>
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
ProxyPassMatch ^/(.*\.php)$ fcgi://127.0.0.1:9000/usr/local/apache2/htdocs/$1
ProxyTimeout 1800
</IfModule>
I didn't find any similar settings with mod_proxy_fcgi BUT it just works for me by default. It asks for user authorization (.htaccess as usual) and the php gets it, and works like with mod_php or fastcgi and pass-header. I don't know if I was helpful...
EDIT:
it only works on teszt.com/ when using the DirectoryIndex... If i pass the php file name (even if the index.php!) it just doesn't work, don't pass the auth to the php. This is a blocker for me, but I don't want to downgrade to apache 2.2 (and mod_fastgi) so I migrate to nginx (on this machine too).

Changing Cookie Domains

I use apache as a proxy to my application web server and would like to on the fly, change the domain name associated with a sessionid cookie.
The cookie has a .company.com domain associated with it, and I would like using apache mod rewrite (or some similar module), transparently change the domain to app.company.com. Is this possible ? and if so, how would one go about it ?
You can only change the domain of a cookie on the client, or when it's being set on the server. Once a cookie has been set, the path and domain information for it only exists on the client. So existing cookies can't have their domain changed on the server, because that information isn't sent from the client to the server.
For example, if you have a cookie that looks like this on your local machine:
MYCOOKIE:123, domain:www.test.com, path:/
Your server will only receive:
MYCOOKIE:123
on the server. Why isn't the path and domain sent? Because the browser keeps that information on the client, and doesnt bother sending it along, since it only sends this cookie to your server if the page is at www.test.com and at the path /.
Since it's your server, you should be able to change your code that creates new cookies. If you felt you needed to do it outside of your code for some reason, you could do so with something like the following, but you'd have to look exactly at how your cookie is being written in the header to match it exactly. The following is an untested guess at a workable solution for this, using Apache's mod_headers:
<IfModule mod_headers.c>
Header edit Set-Cookie (.*)(domain=.company.com;)(.*) $1 domain=app.company.com; $2
</IfModule>
You can also use mod_headers to change the cookie received from the client, like so, if need be:
<IfModule mod_headers.c>
RequestHeader edit Cookie "OLD_COOKIE=([0-9a-zA-Z\-]*);" "NEW_COOKIE_NAME=$1;"
</IfModule>
This would only rename cookies you receive in the request.
ProxyPassReverseCookieDomain company.com app.company.com
or interchanging domains (as you are not clearly defining which is internal/external).
ref: https://httpd.apache.org/docs/2.4/en/mod/mod_proxy.html#ProxyPassReverseCookieDomain
I don’t know any module that provides such feature. So I guess you will need to write your own output filter using mod_ext_filter that does this for you.
But if you have control over the other server, it might suffice to just omit the cookie’s domain value so that the client will automatically choose the requested domain as the cookie’s domain.
I ended up just creating an intermediate page that via javascript changed the cookie domain to the proxy server (by omitting the domain value) and then re-directed user to the target page. That seemed to resolve the issue. Thanks for your answers.
If your web-app is capturing the Host: header and using that to determine the domain= portion of the cookie, you might also consider the Apache directive
ProxyPreserveHost On
which relays the Host: header from the client.
This only works if your app is designed to assume that its domain name is whatever the client suggests with the Host header. If your app is one of these, this will not only fix your cookies, but also any absolute URLs that the application generates, which can save you the overhead of otherwise needing to enable mod_substitute
ProxyPass "/" "http://company.com/"
ProxyPassReverse "/" "http://company.com/"
ProxyPassReverseCookieDomain "company.com" "app.company.com"
Note: If it comes second in ProxyPass it should be first in ProxyPassReverseCookieDomain... I spent half a day or more figuring this out :-/
Also see: https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#proxypassreversecookiedomain

HTTP Authorization Rule in htaccess- convert to Nginx

I have recently moved my application from Apache to Nginx server. We have written
API's in Slim framework. So to enhance the basic security of API we have written token (static) based authentication(if-else loop) logic.
Below rule was added in .htaccess on the apache server which was working fine.
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
Now since we have moved to Nginx so above .htaccess rule will not work.
What changes do i need to make in Nginx config to get the above thing working.
It would be great if someone can explain the process step by step.
Thanks!
Try adding the following to your Nginx configuration.
fastcgi_pass_header Authorization;