How do I set a P3P compact privacy policy from Django so that IE accepts cookies from my site when the security settings are on HIGH - i.e. no cookies accepted unless there's a Compact Privacy Policy.
Cheers
Guy
Middleware is the preferred way to do things like this on an "every request" basis. For instance, here is a simple bit of middleware to add the same (example) P3P header to every response Django generates:
In settings.py:
P3P_COMPACT = 'policyref="http://www.example.com/p3p.xml", CP="NON DSP COR CURa TIA"'
MIDDLEWARE_CLASSES += ('myapp.middleware.P3PHeaderMiddleware',)
In myapp/middleware.py:
from django.conf import settings
class P3PHeaderMiddleware(object):
def process_response(self, request, response):
response['P3P'] = getattr(settings, 'P3P_COMPACT', None)
return response
You could also get a similar effect in a single view by setting the P3P header in the response:
def my_view(request):
response = render_to_response('my_template.html')
response['P3P'] = 'CP="NON DSP COR CURa TIA"'
return response
To expand on the topic a little bit, cookies and headers such as the P3P header are both sent at the same time, as part of the response; in fact, under the hood, cookies are set with another response header. You can see the cookie header using curl:
$ curl --head http://www.google.com/
HTTP/1.1 200 OK
Date: Wed, 13 Jan 2010 00:04:59 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
Set-Cookie: PREF=ID=d2c09762c479f94e:TM=1263341099:LM=1263341099:S=oJby3NpU4RsRfuYa; expires=Fri, 13-Jan-2012 00:04:59 GMT; path=/; domain=.google.com
Set-Cookie: NID=30=kdKrd5e-u6Xs7cUe3p4eaNDtv6SO88uBL5v6_M1XMTSRmkh7okxrWLOm-l_uZdN37PxQIe4dBlekFFVCpTFXGyIDlUrz1hEwhgVLvXfIik_VeVWGmWzKbA5qu_Zq0sOi; expires=Thu, 15-Jul-2010 00:04:59 GMT; path=/; domain=.google.com; HttpOnly
Server: gws
X-XSS-Protection: 0
Transfer-Encoding: chunked
I don't know terribly much about p3p but I did a little digging and found this:
http://www.w3.org/TR/P3P11/#Well_Known_Location
You put the file at /w3c/p3p.xml
It looks as though p3p policies are similar to robots.txt files.
Additionally you can set p3p headers on all your pages if the robots.txt method isn't the way you want to go. That's a side-note, however, since you want the compact version which I'm assuming is the p3p.xml file.
Hope this helps get you on the right track.
Related
My Angular4 app (running on http://127.0.0.1:4200 development server) is supposed to access a django REST backend on the web. The backend is under my control and is available only via HTTPS (running Apache that tunnels the request to a gunicorn server running on an internal port). Let's say that this is https://example.com/. For historical reasons, logging the user in is done using sessions, because I want the users to be able to also use Django's admin interface after they logged in. The workflow is as follows:
Users opens http://127.0.0.1:4200, I perform a GET request to https://example.com/REST/is_logged_in which returns a 403 when the user isn't logged in via sessions yet, 200 otherwise. In the former case, the user is redirected to https://example.com/login/, rendered by Django's template engine, allowing the user to log in. Once logged in, the user is redirected to http://127.0.0.1:4200
When clicking on some button in my Angular UI, a POST request is performed. This post request fails with 403, even though the preflight OPTIONS request explicitly lists POST as allowed actions.
Here is my CORS configuration in Django:
NG_APP_ABSOLUTE_URL = 'http://127.0.0.1:4200'
# adapt Django's to Angular's presumed XSRF cookie/header names
CSRF_COOKIE_NAME = "XSRF-TOKEN"
CSRF_HEADER_NAME = "HTTP_X_XSRF_TOKEN"
CORS_ORIGIN_WHITELIST = (
urlparse(NG_APP_ABSOLUTE_URL).netloc
)
CSRF_TRUSTED_ORIGINS = (
urlparse(NG_APP_ABSOLUTE_URL).netloc
)
CORS_ALLOW_HEADERS = default_headers + (
'x-xsrf-token',
)
CORS_ALLOW_CREDENTIALS = True
This is what Chrome reports for the (successful, 200) first REST GET request to check whether the user is logged in (after he successfully did) in the response:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://127.0.0.1:4200
Allow:GET, HEAD, OPTIONS
Connection:close
Content-Type:application/json
Date:Wed, 26 Apr 2017 15:09:26 GMT
Server:gunicorn/19.6.0
Set-Cookie:XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:09:26 GMT; Max-Age=31449600; Path=/
Transfer-Encoding:chunked
Vary:Accept,Cookie,Origin
X-Frame-Options:SAMEORIGIN
The corresponding request had this:
Cookie:sessionid=...; XSRF-TOKEN=...
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/
Now, to the actual problem:
Preflight request:
Request URL:https://example.com/REST/change_user_data/
Request Method:OPTIONS
Status Code:200 OK
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/dashboard/account
Preflight response:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with, x-xsrf-token
Access-Control-Allow-Methods:DELETE, GET, OPTIONS, PATCH, POST, PUT
Access-Control-Allow-Origin:http://127.0.0.1:4200
Access-Control-Max-Age:86400
Connection:close
Content-Length:0
Content-Type:text/html; charset=utf-8
Date:Wed, 26 Apr 2017 15:36:56 GMT
Server:gunicorn/19.6.0
Vary:Origin
X-Frame-Options:SAMEORIGIN
Now my failing (403) POST request:
Accept:application/json
Accept-Encoding:gzip, deflate, br
Accept-Language:de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Content-Length:60
Content-Type:application/json
Cookie:sessionid=...; XSRF-TOKEN=...
Host:example.com
Origin:http://127.0.0.1:4200
Referer:http://127.0.0.1:4200/dashboard/account
The response headers:
HTTP/1.1 403 Forbidden
Date: Wed, 26 Apr 2017 15:36:56 GMT
Server: gunicorn/19.6.0
Vary: Accept,Cookie,Origin
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Access-Control-Allow-Credentials: true
Allow: POST, OPTIONS
Access-Control-Allow-Origin: http://127.0.0.1:4200
Set-Cookie: XSRF-TOKEN=...; expires=Wed, 25-Apr-2018 15:36:56 GMT; Max-Age=31449600; Path=/
Connection: close
Transfer-Encoding: chunked
Why wouldn't this request work? It makes little sense to me!
Best regards!
I had the same problem, trying to send a POST request to Django (port 8000) from my Angular CLI (port 4200). I thought it was a problem of Django so I installed cors package however the "problem" is with the browser (actually is not a problem, it is a security issue, see here). Anyway, I solved the problem adding a proxy rule for my Angular CLI, as follows:
First, instead of sending my requests to http://localhost:8000/api/... is send them to /api/ (i.e. to my ng server running at port 4200).
Then I added a file in my Angular project called "proxy.conf.json" with the following content:
{
"/api": {
"target": "http://localhost:8000",
"secure": false
}
}
Finally, run your ng server with the flag "--proxy-config":
ng serve --watch --proxy-config proxy.conf.json
All API requests will be sent to the port 4200 and Angular will internally redirect them to Django, avoiding the CORS problem.
Note that this is only valid for development and won't be used when you build your app code and add it as the static code of your Django server.
Finally, with this solution I didn't need anymore the python module for cors so you could remove it.
I've enabled SECURE_SSL_REDIRECT in the settings of my django deployment, so now these headers are getting sent to the client:
< HTTP/1.1 301 MOVED PERMANENTLY
< Date: Fri, 19 Feb 2016 15:57:50 GMT
< Server: Apache/2.2.15 (Red Hat)
< Location: https://www.example.com/
< Content-Length: 0
< Content-Type: text/html; charset=utf-8
The main disadvantage with 301 redirects is that they tend to be cached for a very long time by browsers, so I would quite like to add a Cache-Control: max-age=604800, must-revalidate header to this. Preferably, I would like a way that doesn't involve re-implementing SECURE_SSL_REDIRECT.
You can try overriding Django's SecurityMiddleware to add the http headers you need. Below is a full implementation of the middleware:
class CustomSecurityMiddleware(SecurityMiddleware):
def process_request(self, request):
response = super(CustomSecurityMiddleware, self).process_request(request)
# SecurityMiddleware returns an HttpResponsePermanentRedirect only if
# the request should be redirected
if response is not None:
response['Cache-Control'] = 'max-age=604800, must-revalidate'
return response
This implementation retains everything that Django's SecurityMiddleware does already, while adding the custom http headers you need.
The custom middleware should replace SecurityMiddleware in settings.MIDDLEWARE_CLASSES.
I have decorated a Django view with cache_control as follows:
#cache_control(
private=True,
max_age=5 * 60, # 5 minutes
)
def my_view(req):
…
When I try it with the local test server, it works as expected: subsequent page views in Chrome use the cached resource and don't make a request. When deployed in production, though, Chrome seems to ignore the Cache-control header and makes a new request every time I hit that page.
Here's the full list of headers that the production server responds with:
Cache-Control:private, max-age=300
Connection:close
Content-Encoding:gzip
Content-Length:13135
Content-Type:text/html; charset=utf-8
Date:Wed, 22 Jan 2014 20:39:29 GMT
P3P:CP="IDC CURa ADMa OUR IND PHY ONL COM STA"
Server:nginx/1.4.1
Set-Cookie:csrftoken=87y26bT5uPmyA9wt51N7m4blyqBH5nSo; expires=Wed, 21-Jan-2015 20:39:29 GMT; Max-Age=31449600; Path=/
Vary:Cookie,Accept-Encoding
What could be going wrong? Any ideas? Thanks in advance!
Got it: it was a combination of Google Analytics' cookie and the Vary:Cookie header (set by Django's SessionMiddleware). Analytics' cookie changes with each request, but since ga.js doesn't load when working on localhost, the problem only showed up in production.
The block of code for your reference is given below:
String hostname = "Hostname Value";
URI uri = new URI(hostname + "/solr/add/story/" + story.getId() + ".html");
final HTTPConnection con = new HTTPConnection(uri);
con.setAllowUserInteraction(false);
final HTTPResponse response = con.Get(uri.getPathAndQuery());
Here, while accessing the response i am getting below exception:
[ WARN] [com.thestreet.cms.integration.solr.SolrService] 12/02/2013 22:52:54-Unable
update front end search engine index with story 10446446
java.net.ProtocolException: Bad Set-Cookie header: FV=OID-|PID-|MID-|PUC-|DATE-
529D5595; path=/; expires=Tue, 01-Jan-2035 00:00:00 GMT; domain=.thestreet.com;,
BRIS=C0.A8.41.91|529D55951FB74EF; path=/; expires=Tue, 01-Jan-2035 00:00:00 GMT;
domain=.thestreet.com;,
RGIS=-1386042773,192.168.65.145,BA42A8C0,1076F795713A21E010941898- 0-1386042773-;
path=/; expires=Tue, 01-Jan-2035 00:00:00 GMT; domain=.thestreet.com;,
JSESSIONID=8A8A377CF937F6184D3F4774CC6F4CBA; Version=1; Path="/"; HttpOnly
No '=' found for token starting at position 432
at HTTPClient.Cookie.parse(Cookie.java:243)
at HTTPClient.CookieModule.handleCookie(CookieModule.java:454)
at HTTPClient.CookieModule.responsePhase1Handler(CookieModule.java:403)
at HTTPClient.HTTPResponse.handleResponse(HTTPResponse.java:724)
at HTTPClient.HTTPResponse.getStatusCode(HTTPResponse.java:190)
at com.thestreet.cms.integration.solr.SolrService$1.run(SolrService.java:450)
at java.lang.Thread.run(Thread.java:722)
This seems to be caused by Httponly bit in the cookie header since its not in the form of key-value pair. Is there any way to avoid a cookie header or cookie check while reading the response? Please help.
Thanks in advance.
The problem is your http request/response contains "HttpOnly" in its header. It appears to be that this value is not supported by application servers anymore. In order to fix this I wrote a workaround where "HttpOnly" is removed from the response on a server side.
String header = resp.getHeader("Set-Cookie");
if (header != null && header.endsWith("HttpOnly")) {
resp.setHeader("Set-Cookie", header.substring(0, header.length() - 8));
}
But the best solution is to remove "HttpOnly" from the header on the http client side.
I am trying to detect if my application has set a cookie that holds an "alert message" for the user on the next page, where the Javascript displays it if detected.
In my vcl_fetch(), I need to detect if the specific cookie value "alert_message" appears anywhere in the Set-Cookie header (presumably in the VCL variable beresp.http.Set-Cookie). If detected, then I do not want to cache that next page (since Varnish strips the Set-Cookie header by default, which would obliterate the alert message before it makes it back to the browser).
So here is my simple test:
if(beresp.http.Set-Cookie ~ "alert_message") {
set req.http.no_cache = 1;
}
Strangely, it fails to evaluate to true.
So I throw the variable into the Server header to see what it looks like:
set beresp.http.Server = " MyApp Varnish implementation - test reading set-cookie: "+beresp.http.Set-Cookie;
But for some reason this only displays the FIRST Set-Cookie line in the response headers.
Here are the relevant response headers:
Server: MyApp Varnish implementation - test reading cookie: elstats_session=7d7279kjmsnkel31lre3s0vu24; expires=Wed, 10-Oct-2012 00:03:32 GMT; path=/; HttpOnly
Set-Cookie:app_session=7d7279kjmsnkel31lre3s0vu24; expires=Wed, 10-Oct-2012 00:03:32 GMT; path=/; HttpOnly
Set-Cookie:alert_message=Too+many+results.; expires=Tue, 09-Oct-2012 20:13:32 GMT; path=/; domain=.my.app.com
Set-Cookie:alert_key=flash_error; expires=Tue, 09-Oct-2012 20:13:32 GMT; path=/; domain=.my.app.com
Vary:Accept-Encoding
How do I read and run string detection on ALL Set-Cookie header lines?
You can resolve it with header.get function from vmod header (Varnish version >= 3)
For example, I have simple PHP script and more than one Set-Cookie in it:
<?php
setcookie ("Foo", "test", time() + 3600);
setcookie ("Bar", "test", time() + 3600);
setcookie ("TestCookie", "test", time() + 3600);
?>
By default, only first Set-Cookie header will be parsed with ' if(beresp.http.Set-Cookie ~ "somedata" '.
Of course, we can use std.collect procedure from vmod std (already comes with Varnish 3 and not requires compilation) to collapse all our Set-Cookie headers to one, but it will break cookies - Bar and TestCookie will not set.
header.get avoids this defect: it will check all headers for regex match:
if (header.get(beresp.http.set-cookie,"TestCookie=") ~ "TestCookie")
{
set beresp.http.cookie-test = 1;
return(hit_for_pass);
}
So, with it I got in response headers on first and next requests:
cookie-test:1
Set-Cookie:Foo=test; expires=Tue, 09-Oct-2012 22:33:37 GMT
Set-Cookie:Bar=test; expires=Tue, 09-Oct-2012 22:33:37 GMT
Set-Cookie:TestCookie=test; expires=Tue, 09-Oct-2012 22:33:37 GMT
X-Cache:MISS
If I comment out setcookie for cookie TestCookie, then I will got HITs on next requests.