<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: cookies</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/cookies.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2024-08-26T20:26:31+00:00</updated><author><name>Simon Willison</name></author><entry><title>Quoting Frederik Braun</title><link href="https://simonwillison.net/2024/Aug/26/frederik-braun/#atom-tag" rel="alternate"/><published>2024-08-26T20:26:31+00:00</published><updated>2024-08-26T20:26:31+00:00</updated><id>https://simonwillison.net/2024/Aug/26/frederik-braun/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://lobste.rs/s/98rp8f/cors_is_stupid#c_9dtjao"&gt;&lt;p&gt;In 2021 we [the Mozilla engineering team] found “samesite=lax by default” isn’t shippable without what you call the &lt;a href="https://simonwillison.net/2021/Aug/3/samesite/#chrome-2-minute-twist"&gt;“two minute twist”&lt;/a&gt; - you risk breaking a lot of websites. If you have that kind of two-minute exception, a lot of exploits that were supposed to be prevented remain possible.&lt;/p&gt;
&lt;p&gt;When we tried rolling it out, we had to deal with a lot of broken websites: Debugging cookie behavior in website backends is nontrivial from a browser.&lt;/p&gt;
&lt;p&gt;Firefox also had a prototype of what I believe is a better protection (including additional privacy benefits) already underway (called &lt;a href="https://blog.mozilla.org/en/mozilla/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/"&gt;total cookie protection&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Given all of this, we paused samesite lax by default development in favor of this.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://lobste.rs/s/98rp8f/cors_is_stupid#c_9dtjao"&gt;Frederik Braun&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mozilla"&gt;mozilla&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/privacy"&gt;privacy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cors"&gt;cors&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/samesite"&gt;samesite&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="cookies"/><category term="firefox"/><category term="mozilla"/><category term="privacy"/><category term="security"/><category term="cors"/><category term="samesite"/></entry><entry><title>Exploring the SameSite cookie attribute for preventing CSRF</title><link href="https://simonwillison.net/2021/Aug/3/samesite/#atom-tag" rel="alternate"/><published>2021-08-03T21:09:02+00:00</published><updated>2021-08-03T21:09:02+00:00</updated><id>https://simonwillison.net/2021/Aug/3/samesite/#atom-tag</id><summary type="html">
    &lt;p&gt;In reading Yan Zhu's excellent write-up of the &lt;a href="https://blog.azuki.vip/csrf/"&gt;JSON CSRF vulnerability&lt;/a&gt; she found in OkCupid one thing puzzled me: I was under the impression that browsers these days default to treating cookies as &lt;code&gt;SameSite=Lax&lt;/code&gt;, so I would expect attacks like the one Yan described not to work in modern browsers.&lt;/p&gt;
&lt;p&gt;This lead me down a rabbit hole of exploring how SameSite actually works, including building &lt;a href="https://samesite-lax-demo.vercel.app/"&gt;an interactive SameSite cookie exploration tool&lt;/a&gt; along the way. Here's what I learned.&lt;/p&gt;
&lt;h4 id="background-csrf"&gt;Background: Cross-Site Request Forgery&lt;/h4&gt;
&lt;p&gt;I've been tracking CSRF (Cross-Site Request Forgery) &lt;a href="https://simonwillison.net/tags/csrf/?page=2"&gt;on this blog&lt;/a&gt; since 2005(!)&lt;/p&gt;
&lt;p&gt;A quick review: let's say you have a page in your application that allows a user to delete their account, at &lt;code&gt;https://www.example.com/delete-my-account&lt;/code&gt;. The user has to be signed in with a cookie in order to activate that feature.&lt;/p&gt;
&lt;p&gt;If you created that page to respond to &lt;code&gt;GET&lt;/code&gt; requests, I as an evil person could create a page at &lt;code&gt;https://www.evil.com/force-you-to-delete-your-account&lt;/code&gt; that does this:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;img&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://www.example.com/delete-my-account&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If I can get you to visit my page, I can force you to delete your account!&lt;/p&gt;
&lt;p&gt;But you're smarter than that, and you know that GET requests should be idempotent. You implement your endpoint to require a POST request instead.&lt;/p&gt;
&lt;p&gt;Turns out I can still force-delete accounts, if I can trick a user into visiting a page with the following evil HTML on it:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;form&lt;/span&gt; &lt;span class="pl-c1"&gt;action&lt;/span&gt;="&lt;span class="pl-s"&gt;https://www.example.com/delete-my-account&lt;/span&gt;" &lt;span class="pl-c1"&gt;method&lt;/span&gt;="&lt;span class="pl-s"&gt;POST&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;submit&lt;/span&gt;" &lt;span class="pl-c1"&gt;value&lt;/span&gt;="&lt;span class="pl-s"&gt;Delete my account&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;form&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-smi"&gt;document&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;forms&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;submit&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The form submits with JavaScript the instant they load the page!&lt;/p&gt;
&lt;p&gt;CSRF is an extremely common and nasty vulnerability - especially since it's a hole by default: if you don't know what CSRF is, you likely have it in your application.&lt;/p&gt;
&lt;p&gt;Traditionally the solution has been to use CSRF tokens - hidden form fields which "prove" that the user came from a form on your own site, and not a form hosted somewhere else. OWASP call this the &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie"&gt;Double Submit Cookie&lt;/a&gt; pattern.&lt;/p&gt;
&lt;p&gt;Web frameworks like Django implement &lt;a href="https://docs.djangoproject.com/en/3.2/ref/csrf/"&gt;CSRF protection&lt;/a&gt; for you. I built &lt;a href="https://github.com/simonw/asgi-csrf"&gt;asgi-csrf&lt;/a&gt; to help add CSRF token protection to ASGI applications.&lt;/p&gt;
&lt;h4 id="samesite-cookie-attribute"&gt;Enter the SameSite cookie attribute&lt;/h4&gt;
&lt;p&gt;Clearly it would be better if we didn't have to worry about CSRF at all.&lt;/p&gt;
&lt;p&gt;As far as I can tell, work on specifying the &lt;code&gt;SameSite&lt;/code&gt; cookie attribute started &lt;a href="https://github.com/httpwg/http-extensions/commit/aa0722c12ccb367b8f4498e982616064d105a006#diff-70cc0c0600a934d002ea91a4a36d5eb0b7d5edebcce5a40c9a811391cc0fecf6"&gt;in June 2016&lt;/a&gt;. The idea was to add an additional attribute to cookies that specifies the policy for if they should be included in requests made to a domain from pages hosted on another domain.&lt;/p&gt;
&lt;p&gt;Today, all modern browsers support SameSite. MDN &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite"&gt;has SameSite documentation&lt;/a&gt;, but a summary is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SameSite=None&lt;/code&gt; - the cookie is sent in "all contexts" - more-or-less how things used to work before SameSite was invented. &lt;strong&gt;Update:&lt;/strong&gt; One major edge-case here is that Safari apparently ignores &lt;code&gt;None&lt;/code&gt; if the "Prevent cross-site tracking" privacy preference is turned on - and since that is on by default, this means that &lt;code&gt;SameSite=None&lt;/code&gt; is effectively useless if you care about Safari or Mobile Safari users.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SameSite=Strict&lt;/code&gt; - the cookie is only sent for requests that originate on the same domain. Even arriving on the site from an off-site link will not see the cookie, unless you subsequently refresh the page or navigate within the site.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SameSite=Lax&lt;/code&gt; - cookie is sent if you navigate to the site through following a link from another domain but &lt;em&gt;not&lt;/em&gt; if you submit a form. This is generally what you want to protect against CSRF attacks!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The attribute is specified by the server in a &lt;code&gt;set-cookie&lt;/code&gt; header that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set-cookie: lax-demo=3473; Path=/; SameSite=lax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why not habitually use &lt;code&gt;SameSite=Strict&lt;/code&gt;? Because then if someone follows a link to your site their first request will be treated as if they are not signed in at all. That's bad!&lt;/p&gt;
&lt;p&gt;So explicitly setting a cookie with &lt;code&gt;SameSite=Lax&lt;/code&gt; should be enough to protect your application from CSRF vulnerabilities... provided your users have a browser that supports it.&lt;/p&gt;
&lt;p&gt;(Can I Use reports &lt;a href="https://caniuse.com/same-site-cookie-attribute"&gt;93.95% global support&lt;/a&gt; for the attribute - not quite high enough for me to stop habitually using CSRF tokens, but we're getting there.)&lt;/p&gt;
&lt;h4 id="samesite-missing"&gt;What if the SameSite attribute is missing?&lt;/h4&gt;
&lt;p&gt;Here's where things get interesting. If a cookie is set without a SameSite attribute at all, how should the browser treat it?&lt;/p&gt;
&lt;p&gt;Over the past year, all of the major browsers have been changing their default behaviour. The goal is for a missing SameSite attribute to be treated as if it was &lt;code&gt;SameSite=Lax&lt;/code&gt; - providing CSRF protection by default.&lt;/p&gt;
&lt;p&gt;I have found it infuriatingly difficult to track down if and when this change has been made:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chrome/Chromium offer &lt;a href="https://www.chromium.org/updates/same-site"&gt;the best documentation&lt;/a&gt; - they claim to have ramped up the new default to 100% of users in August 2020. WebViews in Android still have the old default behaviour, which is scheduled to be fixed in Android 12 (&lt;a href="https://en.wikipedia.org/wiki/Android_12"&gt;not yet released&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Firefox have a &lt;a href="https://hacks.mozilla.org/2020/08/changes-to-samesite-cookie-behavior/"&gt;blog entry from August 2020&lt;/a&gt; which says "Starting with Firefox 79 (June 2020), we rolled it out to 50% of the Firefox Beta user base" - but I've not been able to find any subsequent updates. &lt;strong&gt;Update 26th August 2024:&lt;/strong&gt; It &lt;a href="https://simonwillison.net/2024/Aug/26/frederik-braun/"&gt;turns out&lt;/a&gt; Firefox didn't ship this after all, going with their own &lt;a href="https://blog.mozilla.org/en/mozilla/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/"&gt;Total Cookie Protection&lt;/a&gt; solution instead, which rolled out in April 2023.&lt;/li&gt;
&lt;li&gt;I have no idea at all what's going on with Safari!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I started &lt;a href="https://twitter.com/simonw/status/1422366158171238400"&gt;a Twitter thread&lt;/a&gt; to try and collect more information, so please reply there if you know what's going on in more detail.&lt;/p&gt;
&lt;h4 id="chrome-2-minute-twist"&gt;The Chrome 2-minute twist&lt;/h4&gt;
&lt;p&gt;Assuming all of the above, the mystery remained: how did Yan's exploit fail to be prevented by browsers?&lt;/p&gt;
&lt;p&gt;After some back-and-forth about this on Twitter &lt;a href="https://twitter.com/bcrypt/status/1422370774896177154"&gt;Yan proposed&lt;/a&gt; that the answer may be this detail, tucked away on the &lt;a href="https://www.chromestatus.com/feature/5088147346030592"&gt;Chrome Platform Status page for Feature: Cookies default to SameSite=Lax&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Chrome will make an exception for cookies set without a SameSite attribute less than 2 minutes ago. Such cookies will also be sent with non-idempotent (e.g. POST) top-level cross-site requests despite normal SameSite=Lax cookies requiring top-level cross-site requests to have a safe (e.g. GET) HTTP method. Support for this intervention ("Lax + POST") will be removed in the future.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It looks like OkCupid were setting their authentication cookie without a &lt;code&gt;SameSite&lt;/code&gt; attribute... which opened them up to a form-based CSRF attack but only for the 120 seconds following the cookie being set!&lt;/p&gt;
&lt;h4 id="samesite-explore-tool"&gt;Building a tool to explore SameSite browser behaviour&lt;/h4&gt;
&lt;p&gt;I was finding this all very confusing, so I built a tool.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A screenshot showing the two pages from the demo side-by-side" src="https://static.simonwillison.net/static/2021/samesite-tool.png" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;The code lives in &lt;a href="https://github.com/simonw/samesite-lax-demo"&gt;simonw/samesite-lax-demo&lt;/a&gt; on GitHub, but the tool itself has two sides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A server-side Python (&lt;a href="https://www.starlette.io/"&gt;Starlette&lt;/a&gt;) web application for setting cookies with different &lt;code&gt;SameSite&lt;/code&gt; attributes. This is hosted on Vercel at &lt;a href="https://samesite-lax-demo.vercel.app/"&gt;https://samesite-lax-demo.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;An HTML page on a different domain that links to that cookied site, provides a POST form targetting it, embeds an image from it and executes some &lt;code&gt;fetch()&lt;/code&gt; requests against it. This is at &lt;a href="https://simonw.github.io/samesite-lax-demo/"&gt;https://simonw.github.io/samesite-lax-demo/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hosting on two separate domains is critical for the tool to show what is going on. I chose Vercel and GitHub Pages because they are both trivial to set up to continuously deploy changes from a GitHub repository.&lt;/p&gt;
&lt;p&gt;Using the tool in different browsers helps show exactly what is going on with regards to cross-domain cookies.&lt;/p&gt;
&lt;p&gt;A few of the things I observed using the tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SameSite=Strict&lt;/code&gt; works as you would expect. It's particularly interesting to follow the regular &lt;code&gt;&amp;lt;a href=...&amp;gt;&lt;/code&gt; link from the static site to the application and see how the strict cookie is NOT visible upon arrival - but becomes visible when you refresh that page.&lt;/li&gt;
&lt;li&gt;I included a dynamically generated SVG in a &lt;code&gt;&amp;lt;img src="/cookies.svg"&amp;gt;&lt;/code&gt; image tag, which shows the cookies (using SVG &lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt;) that are visible to the request. That image shows all four types of cookie when embedded on the Vercel domain, but when embedded on the GitHub pages domain it differs wildly:
&lt;ul&gt;
&lt;li&gt;Firefox 89 shows both the &lt;code&gt;SameSite=None&lt;/code&gt; and the missing SameSite cookies&lt;/li&gt;
&lt;li&gt;Chrome 92 shows just the &lt;code&gt;SameSite=None&lt;/code&gt; cookie&lt;/li&gt;
&lt;li&gt;Safari 14.0 shows no cookies at all!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chrome won't let you set a &lt;code&gt;SameSite=None&lt;/code&gt; cookie without including the &lt;code&gt;Secure&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;I also added some JavaScript that makes a cross-domain &lt;code&gt;fetch(..., {credentials: "include"})&lt;/code&gt; call against a &lt;code&gt;/cookies.json&lt;/code&gt; endpoint. This didn't send any cookies at all until I added server-side headers &lt;code&gt;access-control-allow-origin: https://simonw.github.io&lt;/code&gt; and &lt;code&gt;access-control-allow-credentials: true&lt;/code&gt;. Having done that, I got the same results across the three browsers as for the &lt;code&gt;&amp;lt;img&lt;/code&gt; test described above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Safari ignoring &lt;code&gt;SameSite=None&lt;/code&gt; looked like it was this bug: &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=198181"&gt;Cookies with SameSite=None or SameSite=invalid treated as Strict&lt;/a&gt; - it's marked as fixed but it's not clear to me if the fix has been released yet - I still saw that behaviour on my macOS 10.15.6 laptop or my iOS 14.7.1 iPhone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt;  	
&lt;a href="https://news.ycombinator.com/item?id=28092943"&gt;krinchan on Hacker News&lt;/a&gt; has an answer here:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The Safari "bug" is a new setting that's turned on by default: "Prevent cross-site tracking". It treats all cookies as SameSite=Lax, even cookies with SameSite=None.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/"&gt;Full Third-Party Cookie Blocking and More&lt;/a&gt; on the WebKit blog has more about this.&lt;/p&gt;

&lt;p&gt;Most excitingly, I was able to replicate the Chrome two minute window bug using the tool! Each cookie has its value set to the timestamp when it was created, and I added code to display how many seconds ago the cookie was set. Here's an animation showing how Chrome on a form submission navigation can see the cookie that was set with &lt;code&gt;SameSite&lt;/code&gt; missing at 114 seconds old, but that cookie is no longer visible once it passes 120 seconds.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Animated demo of the tool in Chrome" src="https://static.simonwillison.net/static/2021/chrome-samesite-missing-loop.gif" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4 id="consider-subdomains"&gt;Consider your subdomains&lt;/h4&gt;
&lt;p&gt;One last note about CSRF that you should consider:  &lt;code&gt;SameSite=Lax&lt;/code&gt; still allows form submissions from  subdomains of your primary domain to carry their cookies.&lt;/p&gt;
&lt;p&gt;This means that if you have a XSS vulnerability on one of your subdomains the security of your primary domain will be compromised.&lt;/p&gt;
&lt;p&gt;Since it's common for subdomains to host other applications that may have their own security concerns, ditching CSRF tokens for Lax cookies may not be a wise step!&lt;/p&gt;
&lt;h4 id="login-csrf-samesite-lax"&gt;Login CSRF and SameSite=Lax&lt;/h4&gt;
&lt;p&gt;Login CSRF is an interesting variety of CSRF with slightly different rules.&lt;/p&gt;
&lt;p&gt;A Login CSRF attack is when a malicious forces a user to sign into an account controlled by the attacker. Why do this? Because if that user then saves sensitive information the attacker can see it.&lt;/p&gt;
&lt;p&gt;Imagine I trick you into signing into an e-commerce account I control and saving your credit card details. I could then later sign in myself and buy things on your card!&lt;/p&gt;
&lt;p&gt;Here's how that would work: Say the site's login form makes a POST to &lt;code&gt;https://www.example.com/login&lt;/code&gt; with &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; as the form fields. If those credentials match, the site sets an authentication cookie.&lt;/p&gt;
&lt;p&gt;I can set up my evil website with the following form:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;form&lt;/span&gt; &lt;span class="pl-c1"&gt;action&lt;/span&gt;="&lt;span class="pl-s"&gt;https://www.example.com/login&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;hidden&lt;/span&gt;" &lt;span class="pl-c1"&gt;name&lt;/span&gt;="&lt;span class="pl-s"&gt;username&lt;/span&gt;" &lt;span class="pl-c1"&gt;value&lt;/span&gt;="&lt;span class="pl-s"&gt;my-username&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;hidden&lt;/span&gt;" &lt;span class="pl-c1"&gt;name&lt;/span&gt;="&lt;span class="pl-s"&gt;password&lt;/span&gt;" &lt;span class="pl-c1"&gt;value&lt;/span&gt;="&lt;span class="pl-s"&gt;my-password&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;form&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-smi"&gt;document&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;forms&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;submit&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I trick you into visiting my evil pge and you're now signed in to that site using an account that I control. I cross my fingers and hope you don't notice the "you are signed in as X" message in the UI.&lt;/p&gt;
&lt;p&gt;An interesting thing about Login CSRF is that, since it involves setting a cookie but not sending a cookie, &lt;code&gt;SameSite=Lax&lt;/code&gt; would seem to make no difference at all. You need to look to other mechanisms to protect against this attack.&lt;/p&gt;
&lt;p&gt;But actually, you can use &lt;code&gt;SameSite=Lax&lt;/code&gt; to prevent these. The trick is to only allow logins from users that are carrying at least one cookie which you have set in that way - since you know that those cookies could not have been sent if the user originated in a form on another site.&lt;/p&gt;
&lt;p&gt;Another (potentially better) option: check the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin"&gt;HTTP Origin header&lt;/a&gt; on the oncoming request.&lt;/p&gt;
&lt;h4 id="final-recommendations"&gt;Final recommendations&lt;/h4&gt;
&lt;p&gt;As an application developer, you should set all cookies with &lt;code&gt;SameSite=Lax&lt;/code&gt; unless you have a very good reason not to. Most web frameworks do this by default now - Django shipped &lt;a href="https://github.com/django/django/commit/9a56b4b13ed92d2d5bb00d6bdb905a73bc5f2f0a"&gt;support for this&lt;/a&gt; in &lt;a href="https://docs.djangoproject.com/en/3.2/releases/2.1/#requests-and-responses"&gt;Django 2.1&lt;/a&gt; in August 2018.&lt;/p&gt;
&lt;p&gt;Do you still need CSRF tokens as well? I think so: I don't like the idea of users who fire up an older browser (maybe borrowing an obsolete computer) being vulnerable to this attack, and I worry about the subdomain issue described above.&lt;/p&gt;
&lt;p&gt;And if you work for a browser vendor, please make it easier to find information on what the default behaviour is and when it was shipped!&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/samesite"&gt;samesite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/starlette"&gt;starlette&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="chrome"/><category term="cookies"/><category term="csrf"/><category term="security"/><category term="samesite"/><category term="starlette"/></entry><entry><title>Quoting Nat Friedman</title><link href="https://simonwillison.net/2020/Dec/17/nat-friedman/#atom-tag" rel="alternate"/><published>2020-12-17T19:44:20+00:00</published><updated>2020-12-17T19:44:20+00:00</updated><id>https://simonwillison.net/2020/Dec/17/nat-friedman/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://github.blog/2020-12-17-no-cookie-for-you/"&gt;&lt;p&gt;At GitHub, we want to protect developer privacy, and we find cookie banners quite irritating, so we decided to look for a solution. After a brief search, we found one: just don’t use any non-essential cookies. Pretty simple, really. 🤔&lt;/p&gt;
&lt;p&gt;So, we have removed all non-essential cookies from GitHub, and visiting our website does not send any information to third-party analytics services.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://github.blog/2020-12-17-no-cookie-for-you/"&gt;Nat Friedman&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/github"&gt;github&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/privacy"&gt;privacy&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="github"/><category term="privacy"/></entry><entry><title>Tip for changing cookie subdomains: change the cookie name too</title><link href="https://simonwillison.net/2020/Jun/9/change-cookie-name/#atom-tag" rel="alternate"/><published>2020-06-09T18:41:42+00:00</published><updated>2020-06-09T18:41:42+00:00</updated><id>https://simonwillison.net/2020/Jun/9/change-cookie-name/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://stackoverflow.com/questions/49835929/how-to-share-django-sessions-across-newly-added-subdomains/59478959#59478959"&gt;Tip for changing cookie subdomains: change the cookie name too&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is a really useful tip I hadn’t encountered before. If you make a change to the way cookies are configured—changing the cookie domain or path for example—it’s a good idea to change the name of the cookie as well. If you don’t change the cookie name you’ll see weird behaviour for users who have previously had the cookie set using the older configuration. This definitely explains bugs I’ve seen in the past. Filing this tip away for future cookie-related development work.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/></entry><entry><title>Weeknotes: datasette-auth-existing-cookies and datasette-sentry</title><link href="https://simonwillison.net/2020/Jan/29/weeknotes-datasette-cookies-sentry/#atom-tag" rel="alternate"/><published>2020-01-29T05:58:13+00:00</published><updated>2020-01-29T05:58:13+00:00</updated><id>https://simonwillison.net/2020/Jan/29/weeknotes-datasette-cookies-sentry/#atom-tag</id><summary type="html">
    &lt;p&gt;Work on &lt;a href="https://simonwillison.net/tags/datasettecloud/"&gt;Datasette Cloud&lt;/a&gt; continues - I'm tantalizingly close to having a MVP I can start to invite people to try out.&lt;/p&gt;

&lt;p&gt;I'm trying to get as much work as possible done for it using Datasette Plugins. This week I've released two new plugins to assist in the effort.&lt;/p&gt;

&lt;h3&gt;datasette-auth-existing-cookies&lt;/h3&gt;

&lt;p&gt;My first attempt at adding authentication to Datasette was &lt;a href="https://simonwillison.net/2019/Jul/14/sso-asgi/"&gt;datasette-auth-github&lt;/a&gt;, which takes advantages of GitHub's best-in-class OAuth flow to implement sign-in with GitHub and builds a relatively powerful permission system on top of GitHub users, organizations and teams.&lt;/p&gt;

&lt;p&gt;For Datasette Cloud I need to go a step further: I'm definitely going to have regular username/password accounts, and I'll probably implement sign-in-with-Google as well.&lt;/p&gt;

&lt;p&gt;I don't particularly want to implement username/password accounts from scratch. Django (and &lt;a href="https://github.com/ubernostrum/django-registration"&gt;django-registration&lt;/a&gt;) provide robust and very well tested solution for this. How about I use that?&lt;/p&gt;

&lt;p&gt;Datasette Cloud teams will each get their own Datasette instance running on a subdomain. If I implement authentication as a Django app running on &lt;code&gt;example.com&lt;/code&gt; I can set that as the cookie domain - then Datasette instances running on &lt;code&gt;teamname.example.com&lt;/code&gt; will be able to see the resulting authentication cookie.&lt;/p&gt;

&lt;p&gt;Given a Django authentication cookie (which may just be a &lt;code&gt;sessionid&lt;/code&gt;) how can I tell if it corresponds to a logged in user? That's where my new &lt;a href="https://github.com/simonw/datasette-auth-existing-cookies/"&gt;datasette-auth-existing-cookies&lt;/a&gt; plugin comes in.&lt;/p&gt;

&lt;p&gt;The plugin lets you configure Datasette to read in a specified list of cookies and then forward them on as part of an API request to an underlying application. That application then returns JSON showing if the user is signed in or not. The plugin then sets a short-lived signed cookie that persists that information.&lt;/p&gt;

&lt;p&gt;Here's what the configuration looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    "plugins": {
        "datasette-auth-existing-cookies": {
            "api_url": "https://www.example.com/user-from-cookies",
            "auth_redirect_url": "https://www.example.com/login",
            "original_cookies": ["sessionid"]
        }
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Any hits to &lt;code&gt;teamname.example.com&lt;/code&gt; will be checked for a &lt;code&gt;sessionid&lt;/code&gt; cookie. That cookie is forwarded on to &lt;code&gt;https://www.example.com/user-from-cookies&lt;/code&gt; to see if it's valid.&lt;/p&gt;

&lt;p&gt;If the cookie is missing or invalid, the user will be redirected to the following URL:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://www.example.com/login?next=https://teamname.example.com/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The plugin has a few other options: you can request that the &lt;code&gt;?next=&lt;/code&gt; parameter is itself signed to help protect against &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html"&gt;unvalidated redirects&lt;/a&gt; for example. But it's a pretty simple piece of code that hopefully means I won't have to spend much more time thinking about login and registration.&lt;/p&gt;

&lt;h3 id="httpx-testing"&gt;httpx for testing ASGI apps&lt;/h3&gt;

&lt;p&gt;All of my Datasette plugins ship with unit tests - mainly so that I can implement continuous deployment from them, where new tagged releases are automatically shipped to PyPI provided the tests pass.&lt;/p&gt;

&lt;p&gt;For ASGI plugins, this means writing unit tests against the ASGI spec. I've mainly been doing this using the &lt;code&gt;ApplicationCommunicator&lt;/code&gt; class from &lt;a href="https://github.com/django/asgiref"&gt;asgiref&lt;/a&gt;, which provides powerful low-level hooks for interacting with an ASGI application. The tests end up being pretty verbose though!&lt;/p&gt;

&lt;p&gt;Here's &lt;a href="https://github.com/simonw/datasette-auth-existing-cookies/blob/be84a5f85b6f827cfb3f34a1795a6b37e46e38f3/test_datasette_auth_existing_cookies.py#L31-L63"&gt;the ApplicationCommunicator test&lt;/a&gt; I first wrote for &lt;code&gt;datasette-auth-existing-cookies&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I've been exploring Tom Christie's &lt;a href="https://www.python-httpx.org/"&gt;httpx library&lt;/a&gt; for asynchronous HTTP calls in Python recently, and I spotted an interesting capability buried deep in the documentation: you can &lt;a href="https://www.python-httpx.org/async/#calling-into-python-web-apps"&gt;pass it an ASGI app&lt;/a&gt; and make requests directly against the app, without round-tripping through HTTP!&lt;/p&gt;

&lt;p&gt;This looked ideal for unit testing, so I had a go at rewriting my tests using it. &lt;a href="https://github.com/simonw/datasette-auth-existing-cookies/blob/a9f4de0ec1c61956c05caf434e2e95e6952d5474/test_datasette_auth_existing_cookies.py#L33-L46"&gt;The result&lt;/a&gt; was delightful:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;auth_app = ExistingCookiesAuthTest(
    hello_world_app,
    ...
)
async with httpx.AsyncClient(app=auth_app) as client:
    response = await client.get(
        "https://demo.example.com/", allow_redirects=False
    )
    assert 302 == response.status_code
    location = response.headers["location"]
    assert "https://www.example.com/login" == location&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a much nicer way of writing tests for ASGI applications and middleware. I'm going to be using this for all of my projects going forward.&lt;/p&gt;

&lt;h3&gt;datasette-sentry&lt;/h3&gt;

&lt;p&gt;In starting to deploy Datasette Cloud I quickly ran into the need to start collecting and analyzing errors thrown in production.&lt;/p&gt;

&lt;p&gt;I've been enjoing using &lt;a href="https://sentry.io/"&gt;Sentry&lt;/a&gt; for this for several years now, and I was pleased to see that the official Sentry SDK &lt;a href="https://docs.sentry.io/platforms/python/asgi/"&gt;grew support for ASGI&lt;/a&gt; last July.&lt;/p&gt;

&lt;p&gt;Wrapping it up as a Datasette plugin took less than half an hour: &lt;a href="https://github.com/simonw/datasette-sentry"&gt;datasette-sentry&lt;/a&gt;. It's configured like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    "plugins": {
        "datasette-sentry": {
            "dsn": {
                "$env": "SENTRY_DSN"
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The DSN configuring Sentry will then be read from the &lt;code&gt;SENTRY_DSN&lt;/code&gt; environment variable.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sentry"&gt;sentry&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/weeknotes"&gt;weeknotes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette-cloud"&gt;datasette-cloud&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/httpx"&gt;httpx&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="cookies"/><category term="django"/><category term="projects"/><category term="sentry"/><category term="datasette"/><category term="weeknotes"/><category term="datasette-cloud"/><category term="httpx"/></entry><entry><title>Quoting Troy Hunt</title><link href="https://simonwillison.net/2020/Jan/3/troy-hunt/#atom-tag" rel="alternate"/><published>2020-01-03T16:22:42+00:00</published><updated>2020-01-03T16:22:42+00:00</updated><id>https://simonwillison.net/2020/Jan/3/troy-hunt/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/"&gt;&lt;p&gt;Come version 80, any cookie without a SameSite attribute will be treated as "Lax" by Chrome. This is really important to understand because put simply, it'll very likely break a bunch of stuff. [...] The fix is easy, all it needs is for everyone responsible for maintaining any system that uses cookies that might be passed from an external origin to understand what's going on. Can't be that hard, right? Hello? Oh...&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/"&gt;Troy Hunt&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/samesite"&gt;samesite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/troy-hunt"&gt;troy-hunt&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="cookies"/><category term="csrf"/><category term="samesite"/><category term="troy-hunt"/></entry><entry><title>Cookies-over-HTTP Bad</title><link href="https://simonwillison.net/2018/Apr/7/cookies-over-http-bad/#atom-tag" rel="alternate"/><published>2018-04-07T14:39:06+00:00</published><updated>2018-04-07T14:39:06+00:00</updated><id>https://simonwillison.net/2018/Apr/7/cookies-over-http-bad/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/mikewest/cookies-over-http-bad"&gt;Cookies-over-HTTP Bad&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Mike West from the Chrome security team proposes a way for browsers to start discouraging the use of tracking cookies sent over HTTP—which represent a significant threat to user privacy from network attackers. It’s a clever piece of thinking: browsers would slowly ramp up the forced expiry deadline for non-HTTPS cookies, further encouraging sites to switch to HTTPS cookies while giving them ample time to adapt.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/mikewest/status/982253957291524097"&gt;@mikewest&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/https"&gt;https&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/privacy"&gt;privacy&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="https"/><category term="privacy"/></entry><entry><title>Can you mark items on a website as 'unread' without cookies?</title><link href="https://simonwillison.net/2012/Oct/20/can-you-mark-items/#atom-tag" rel="alternate"/><published>2012-10-20T13:44:00+00:00</published><updated>2012-10-20T13:44:00+00:00</updated><id>https://simonwillison.net/2012/Oct/20/can-you-mark-items/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Can-you-mark-items-on-a-website-as-unread-without-cookies/answer/Simon-Willison"&gt;Can you mark items on a website as &amp;#39;unread&amp;#39; without cookies?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's not very exciting, but CSS will let you set different styles for visited vs unvisited links and the technique has worked reliably since the mid 1990s.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html5"&gt;html5&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javafx"&gt;javafx&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/frontend"&gt;frontend&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="cookies"/><category term="html5"/><category term="javafx"/><category term="javascript"/><category term="web-development"/><category term="quora"/><category term="frontend"/></entry><entry><title>Firesheep</title><link href="https://simonwillison.net/2010/Oct/25/firesheep/#atom-tag" rel="alternate"/><published>2010-10-25T09:11:00+00:00</published><updated>2010-10-25T09:11:00+00:00</updated><id>https://simonwillison.net/2010/Oct/25/firesheep/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://codebutler.com/firesheep"&gt;Firesheep&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Oh wow. A Firefox extension that makes sniffing for insecured (non-HTTPS) cookie requests on your current WiFi network and logging in as that person a case of clicking a couple of buttons. Always possible of course, but it’s never been made easy before. Private VPNs are about to become a lot more popular.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://news.ycombinator.com/item?id=1827928"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/wifi"&gt;wifi&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firesheep"&gt;firesheep&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="security"/><category term="wifi"/><category term="recovered"/><category term="firesheep"/></entry><entry><title>Facebook's Instant Personalization: An Analysis of Fundamental Privacy Flaws</title><link href="https://simonwillison.net/2010/Oct/2/instant/#atom-tag" rel="alternate"/><published>2010-10-02T23:53:00+00:00</published><updated>2010-10-02T23:53:00+00:00</updated><id>https://simonwillison.net/2010/Oct/2/instant/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://33bits.org/2010/09/28/instant-personalization-privacy-flaws/"&gt;Facebook&amp;#x27;s Instant Personalization: An Analysis of Fundamental Privacy Flaws&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Oh FFS. “Instant Personalization” means you visit one of Facebook’s “partner websites” and Facebook instantly tells them your full identity and gives them access to full Facebook connect functionality—without you performing any action other than visiting the site. This will not end well.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://news.ycombinator.com/item?id=1750711"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/facebook"&gt;facebook&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/privacy"&gt;privacy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="facebook"/><category term="privacy"/><category term="recovered"/></entry><entry><title>Internet Explorer Cookie Internals (FAQ)</title><link href="https://simonwillison.net/2010/Feb/26/cachebusting/#atom-tag" rel="alternate"/><published>2010-02-26T12:25:24+00:00</published><updated>2010-02-26T12:25:24+00:00</updated><id>https://simonwillison.net/2010/Feb/26/cachebusting/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://blogs.msdn.com/ieinternals/archive/2009/08/20/WinINET-IE-Cookie-Internals-FAQ.aspx"&gt;Internet Explorer Cookie Internals (FAQ)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Grr... IE 6, 7 and 8 don’t support the max-age cookie argument, forcing you to use an explicit expiry date instead. This appears to affect the cache busting cookie pattern, where you set a cookie to expire in 30 seconds for any user who posts content and use the presence of that cookie to skip caches and/or send their queries to a master instead of slave database. If you have to use expires, users with incorrect system clocks may get inconsistent results. Anyone know of a workaround?


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cachebusting"&gt;cachebusting&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/caching"&gt;caching&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/internet-explorer"&gt;internet-explorer&lt;/a&gt;&lt;/p&gt;



</summary><category term="cachebusting"/><category term="caching"/><category term="cookies"/><category term="internet-explorer"/></entry><entry><title>Negative Cashback from Bing Cashback</title><link href="https://simonwillison.net/2009/Nov/23/bing/#atom-tag" rel="alternate"/><published>2009-11-23T21:24:12+00:00</published><updated>2009-11-23T21:24:12+00:00</updated><id>https://simonwillison.net/2009/Nov/23/bing/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://bountii.com/blog/2009/11/23/negative-cashback-from-bing-cashback/"&gt;Negative Cashback from Bing Cashback&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Some online stores show you a higher price if you click through from Bing—and set a cookie that continues to show you the higher price for the next three months. It’s unclear if this is Bing’s fault—comments on Hacker News report that Google Shopping sometimes suffers from the same problem (POST UPDATED: I originally blamed Bing for this).

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://poorbuthappy.com/ease/archives/2009/11/23/4652/bing-gets-more-evil"&gt;Peter Van Dijck&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/affiliates"&gt;affiliates&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/bing"&gt;bing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/microsoft"&gt;microsoft&lt;/a&gt;&lt;/p&gt;



</summary><category term="affiliates"/><category term="bing"/><category term="cookies"/><category term="google"/><category term="microsoft"/></entry><entry><title>Django ponies: Proposals for Django 1.2</title><link href="https://simonwillison.net/2009/Sep/28/ponies/#atom-tag" rel="alternate"/><published>2009-09-28T23:32:04+00:00</published><updated>2009-09-28T23:32:04+00:00</updated><id>https://simonwillison.net/2009/Sep/28/ponies/#atom-tag</id><summary type="html">
    &lt;p&gt;I've decided to step up my involvement in Django development in the run-up to Django 1.2, so I'm currently going through several years worth of accumulated pony requests figuring out which ones are worth advocating for. I'm also ensuring I have the code to back them up - my innocent &lt;a href="http://code.djangoproject.com/wiki/AutoEscaping"&gt;AutoEscaping proposal&lt;/a&gt; a few years ago resulted in an enormous amount of work by Malcolm and I don't think he'd appreciate a repeat performance.&lt;/p&gt;

&lt;p&gt;I'm not a big fan of branches when it comes to exploratory development - they're fine for doing the final implementation once an approach has been agreed, but I don't think they are a very effective way of discussing proposals. I'd much rather see working code in a separate application - that way I can try it out with an existing project without needing to switch to a new Django branch. Keeping code out of a branch also means people can start using it for real development work, making the API much easier to evaluate. Most of my proposals here have accompanying applications on GitHub.&lt;/p&gt;

&lt;p&gt;I've recently got in to the habit of including an "examples" directory with each of my experimental applications. This is a full Django project (with settings.py, urls.py and manage.py files) which serves two purposes. Firstly, it allows developers to run the application's unit tests without needing to install it in to their own pre-configured project, simply by changing in to the examples directory and running &lt;samp&gt;./manage.py test&lt;/samp&gt;. Secondly, it gives me somewhere to put demonstration code that can be viewed in a browser using the runserver command - a further way of making the code easier to evaluate. &lt;a href="http://github.com/simonw/django-safeform"&gt;django-safeform&lt;/a&gt; is a good example of this pattern.&lt;/p&gt;

&lt;p&gt;Here's my current list of ponies, in rough order of priority.&lt;/p&gt;

&lt;h4&gt;Signing and signed cookies&lt;/h4&gt;

&lt;p&gt;Signing strings to ensure they have not yet been tampered with is a crucial technique in web application security. As with all cryptography, it's also surprisingly difficult to do correctly. &lt;a href="http://vnhacker.blogspot.com/2009/09/flickrs-api-signature-forgery.html"&gt;A vulnerability in the signing implementation&lt;/a&gt; used to protect the Flickr API was revealed just today.&lt;/p&gt;

&lt;p&gt;One of the many uses of signed strings is to implement signed cookies. Signed cookies are fantastically powerful - they allow you to send cookies safe in the knowledge that your user will not be able to alter them without you knowing. This dramatically reduces the need for sessions - most web apps use sessions for security rather than for storing large amounts of data, so moving that "logged in user ID" value to a signed cookie eliminates the need for session storage entirely, saving a round-trip to persistent storage on every request.&lt;/p&gt;

&lt;p&gt;This has particularly useful implications for scaling - you can push your shared secret out to all of your front end web servers and scale horizontally, with no need for shared session storage just to handle simple authentication and "You are logged in as X" messages.&lt;/p&gt;

&lt;p&gt;The latest version of my &lt;a href="http://github.com/simonw/django-openid"&gt;django-openid&lt;/a&gt; library uses signed cookies to store the OpenID you log in with, removing the need to configure Django's session storage. I've extracted that code in to &lt;a href="http://github.com/simonw/django-signed"&gt;django-signed&lt;/a&gt;, which I hope to evolve in to something suitable for inclusion in &lt;samp&gt;django.utils&lt;/samp&gt;.&lt;/p&gt;

&lt;p&gt;Please note that django-signed has not yet been vetted by cryptography specialists, something I plan to fix before proposing it for final inclusion in core.&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://github.com/simonw/django-signed"&gt;django-signed&lt;/a&gt; on GitHub&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://code.djangoproject.com/wiki/Signing"&gt;Details of the Signing proposal&lt;/a&gt; on the Django wiki&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://groups.google.com/group/django-developers/browse_thread/thread/133509246caf1d91"&gt;Signing discussion&lt;/a&gt; on the django-developers mailing list&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Improved CSRF support&lt;/h4&gt;

&lt;p&gt;This is mainly Luke Plant's pony, but I'm very keen to see it happen. Django has shipped with CSRF protection for &lt;a href="http://code.djangoproject.com/changeset/2868"&gt;more than three years now&lt;/a&gt;, but the approach (using middleware to rewrite form HTML) is relatively crude and, crucially, the protection isn't turned on by default. Hint: if you aren't 100% positive you are protected against &lt;a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery"&gt;CSRF&lt;/a&gt;, you should probably go and turn it on.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://bitbucket.org/spookylukey/django-trunk-lukeplant/src/05f0530f3207/django/contrib/csrf/"&gt;Luke's approach&lt;/a&gt; is an iterative improvement - a template tag (with a dependency on RequestContext) is used to output the hidden CSRF field, with middleware used to set the cookie and perform the extra validation. I experimented at length with an alternative solution based around extending Django's form framework to treat CSRF as just another aspect of validation - you can see the result in my &lt;a href="http://github.com/simonw/django-safeform"&gt;django-safeform&lt;/a&gt; project. My approach avoids middleware and template tags in favour of a view decorator to set the cookie and a class decorator to add a CSRF check to the form itself.&lt;/p&gt;

&lt;p&gt;While my approach works, the effort involved in upgrading existing code to it is substantial, compared to a much easier upgrade path for Luke's middleware + template tag approach. The biggest advantage of safeform is that it allows CSRF failure messages to be shown inline on the form, without losing the user's submission - the middleware check means showing errors as a full page without redisplaying the form. It looks like it should be possible to bring that aspect of safeform back to the middleware approach, and I plan to put together a patch for that over the next few days.&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Luke's &lt;a href="http://bitbucket.org/spookylukey/django-trunk-lukeplant/src/05f0530f3207/django/contrib/csrf/"&gt;CSRF branch&lt;/a&gt; on bitbucket&lt;/li&gt;
    &lt;li&gt;My &lt;a href="http://github.com/simonw/django-signed"&gt;django-safeform&lt;/a&gt; on GitHub&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://code.djangoproject.com/wiki/CsrfProtection"&gt;Details of the CSRF proposal&lt;/a&gt; on the Django wiki&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://groups.google.com/group/django-developers/browse_thread/thread/3d2dc750082103dc"&gt;CSRF discussion&lt;/a&gt; on the django-developers mailing list&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Better support for outputting HTML&lt;/h4&gt;

&lt;p&gt;This is a major pet peeve of mine. Django's form framework is excellent - one of the best features of the framework. There's just one thing that bugs me about it - it outputs full form widgets (for &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt; and the like) so that it can include the previous value when redisplaying a form during validation, but it does so using XHTML syntax.&lt;/p&gt;

&lt;p&gt;I have a strong preference for an HTML 4.01 strict doctype, and all those &amp;lt;self-closing-tags /&amp;gt; have been niggling away at me for literally &lt;em&gt;years&lt;/em&gt;. Django bills itself as a framework for "perfectionists with deadlines", so I feel justified in getting wound up out of proportion over this one.&lt;/p&gt;

&lt;p&gt;A year ago I started experimenting with a solution, and came up with &lt;a href="http://github.com/simonw/django-html"&gt;django-html&lt;/a&gt;. It introduces two new Django template tags - &lt;code&gt;{% doctype %}&lt;/code&gt; and &lt;code&gt;{% field %}&lt;/code&gt;. The doctype tag serves two purposes - it outputs a particular doctype (saving you from having to remember the syntax) and it records that doctype in Django's template context object. The field tag is then used to output form fields, but crucially it gets to take the current doctype in to account.&lt;/p&gt;

&lt;p&gt;The field tag can also be used to add extra HTML attributes to form widgets from within the template itself, solving another small frustration about the existing form library. The &lt;a href="http://github.com/simonw/django-html/blob/master/README.rst"&gt;README&lt;/a&gt; describes the new tags in detail.&lt;/p&gt;

&lt;p&gt;The way the tags work is currently a bit of a hack - if merged in to Django core they could be more cleanly implemented by refactoring the form library slightly. This refactoring is currently being discussed on the mailing list.&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://github.com/simonw/django-html"&gt;django-html&lt;/a&gt; on GitHub&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://groups.google.com/group/django-developers/browse_thread/thread/bbf75f0eeaf9fa64"&gt;Improved HTML discussion&lt;/a&gt; on the django-developers mailing list&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Logging&lt;/h4&gt;

&lt;p&gt;This is the only proposal for which I don't yet have any code. I want to add official support for Python's standard logging framework to Django. It's possible to use this at the moment (I've done so on several projects) but it's not at all clear what the best way of doing so is, and Django doesn't use it internally at all. I posted a &lt;a href="http://groups.google.com/group/django-developers/browse_thread/thread/8551ecdb7412ab22"&gt;full argument in favour of logging&lt;/a&gt; to the mailing list, but my favourite argument is this one:&lt;/p&gt;

&lt;blockquote cite="http://groups.google.com/group/django-developers/browse_thread/thread/8551ecdb7412ab22"&gt;&lt;p&gt;Built-in support for logging reflects a growing reality of modern Web development: more and more sites have interfaces with external web service APIs, meaning there are plenty of things that could go wrong that are outside the control of the developer. Failing gracefully and logging what happened is the best way to deal with 3rd party problems - much better than throwing a 500 and leaving no record of what went wrong.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I'm not actively pursuing this one yet, but I'm very interesting in hearing people's opinions on the best way to configure and use the Python logging module in production.&lt;/p&gt;

&lt;h4&gt;A replacement for get_absolute_url()&lt;/h4&gt;

&lt;p&gt;Django has a loose convention of encouraging people to add a &lt;code&gt;get_absolute_url&lt;/code&gt; method to their models that returns that object's URL. It's a controversial feature - for one thing, it's a bit of a layering violation since URL logic is meant to live in the &lt;samp&gt;urls.py&lt;/samp&gt; file. It's incredibly convenient though, and since it's good web citizenship for everything to have one and only one URL I think there's a pretty good argument for keeping it.&lt;/p&gt;

&lt;p&gt;The problem is, the name sucks. I first took a look at this in the last few weeks before the release of Django 1.0 - what started as a quick proposal to come up with a better name before we were stuck with it quickly descended in to a quagmire as I realised quite how broken &lt;code&gt;get_absolute_url()&lt;/code&gt; is. The short version: in some cases it means "get a relative URL starting with /", in other cases it means "get a full URL starting with http://" and the name doesn't accurately describe either.&lt;/p&gt;

&lt;p&gt;A full write-up of my investigation is &lt;a href="http://code.djangoproject.com/wiki/ReplacingGetAbsoluteUrl"&gt;available on the Wiki&lt;/a&gt;. My proposed solution was to replace it with two complementary methods - &lt;code&gt;get_url()&lt;/code&gt; and &lt;code&gt;get_url_path()&lt;/code&gt; - with the user implementing one hence allowing the other one to be automatically derived. My &lt;a href="http://github.com/simonw/django-urls"&gt;django-urls&lt;/a&gt; project illustrates the concept via a model mixin class. A year on I still think it's quite a neat idea, though as far as I can tell no one has ever actually used it.&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;a href="http://code.djangoproject.com/wiki/ReplacingGetAbsoluteUrl"&gt;ReplacingGetAbsoluteUrl&lt;/a&gt; on the wiki&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://github.com/simonw/django-urls"&gt;django-urls&lt;/a&gt; on GitHub&lt;/li&gt;
    &lt;li&gt;&lt;a href="http://groups.google.com/group/django-developers/browse_thread/thread/7e69c39c23ec1079"&gt;Recent get_absolute_url discussion&lt;/a&gt; on the django-developers mailing list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comments on this post are open, but if you have anything to say about any of the individual proposals it would be much more useful if you posted it to the relevant mailing list thread.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cryptography"&gt;cryptography&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/logging"&gt;logging&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/luke-plant"&gt;luke-plant&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/markup"&gt;markup&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ponies"&gt;ponies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/signedcookies"&gt;signedcookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/signing"&gt;signing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/xhtml"&gt;xhtml&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="cookies"/><category term="cryptography"/><category term="csrf"/><category term="django"/><category term="html"/><category term="logging"/><category term="luke-plant"/><category term="markup"/><category term="ponies"/><category term="projects"/><category term="python"/><category term="security"/><category term="signedcookies"/><category term="signing"/><category term="xhtml"/></entry><entry><title>Adding signing (and signed cookies) to Django core</title><link href="https://simonwillison.net/2009/Sep/24/signing/#atom-tag" rel="alternate"/><published>2009-09-24T19:31:20+00:00</published><updated>2009-09-24T19:31:20+00:00</updated><id>https://simonwillison.net/2009/Sep/24/signing/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://groups.google.com/group/django-developers/browse_thread/thread/133509246caf1d91"&gt;Adding signing (and signed cookies) to Django core&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I’ve been increasing my participation in Django recently—here’s my proposal for adding signing and signed cookies to Django, which I’d personally like to see ship as part of Django 1.2.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cryptography"&gt;cryptography&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/signedcookies"&gt;signedcookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/signing"&gt;signing&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="cryptography"/><category term="django"/><category term="security"/><category term="signedcookies"/><category term="signing"/></entry><entry><title>You Deleted Your Cookies? Think Again</title><link href="https://simonwillison.net/2009/Aug/17/respawning/#atom-tag" rel="alternate"/><published>2009-08-17T15:23:32+00:00</published><updated>2009-08-17T15:23:32+00:00</updated><id>https://simonwillison.net/2009/Aug/17/respawning/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.wired.com/epicenter/2009/08/you-deleted-your-cookies-think-again/"&gt;You Deleted Your Cookies? Think Again&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Flash cookies last longer than browser cookies and are harder to delete. Some services are sneakily “respawning” their cookies—if you clear the regular tracking cookie it will be reinstated from the Flash data next time you visit a page.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://www.schneier.com/blog/archives/2009/08/flash_cookies.html"&gt;Bruce Schneier&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/flash"&gt;flash&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/privacy"&gt;privacy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/respawning"&gt;respawning&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="flash"/><category term="privacy"/><category term="respawning"/><category term="security"/></entry><entry><title>Towards a Standard for Django Session Messages</title><link href="https://simonwillison.net/2009/Jun/19/caktus/#atom-tag" rel="alternate"/><published>2009-06-19T21:57:36+00:00</published><updated>2009-06-19T21:57:36+00:00</updated><id>https://simonwillison.net/2009/Jun/19/caktus/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.caktusgroup.com/blog/2009/06/19/towards-a-standard-for-django-session-messages/#comment-219"&gt;Towards a Standard for Django Session Messages&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I completely agree that Django’s user.message_set (which I helped design) is unfit for purpose, but I don’t think sessions are the right solution for messages sent to users. A signed cookie containing either the full message or a key referencing the message body on the server is a much more generally useful solution as it avoids the need for a round trip to a persistent store entirely.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/flash"&gt;flash&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/messages"&gt;messages&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sessions"&gt;sessions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/signedcookies"&gt;signedcookies&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="django"/><category term="flash"/><category term="messages"/><category term="python"/><category term="sessions"/><category term="signedcookies"/></entry><entry><title>Integrating Facebook Connect with Django in 15 minutes</title><link href="https://simonwillison.net/2008/Dec/17/integrating/#atom-tag" rel="alternate"/><published>2008-12-17T13:18:38+00:00</published><updated>2008-12-17T13:18:38+00:00</updated><id>https://simonwillison.net/2008/Dec/17/integrating/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://nyquistrate.com/django/facebook-connect/"&gt;Integrating Facebook Connect with Django in 15 minutes&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Django authentication middleware that calls the Facebook REST API  using a cookie set by Facebook Connect and checks if that person is your Facebook friend. Despite most of the magic happening on the server you still need Facebook’s JavaScript to set that cookie in the first place.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/facebook"&gt;facebook&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/facebookconnect"&gt;facebookconnect&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/middleware"&gt;middleware&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="django"/><category term="facebook"/><category term="facebookconnect"/><category term="javascript"/><category term="middleware"/></entry><entry><title>Django snippets: Sign a string using SHA1, then shrink it using url-safe base65</title><link href="https://simonwillison.net/2008/Aug/27/snippets/#atom-tag" rel="alternate"/><published>2008-08-27T22:18:49+00:00</published><updated>2008-08-27T22:18:49+00:00</updated><id>https://simonwillison.net/2008/Aug/27/snippets/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.djangosnippets.org/snippets/1004/"&gt;Django snippets: Sign a string using SHA1, then shrink it using url-safe base65&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I needed a way to create tamper-proof URLs and cookies by signing them, but didn’t want the overhead of a full 40 character SHA1 hash. After some experimentation, it turns out you can knock a 40 char hash down to 27 characters by encoding it using a custom base65 encoding which only uses URL-safe characters.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/base65"&gt;base65&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cryptography"&gt;cryptography&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django-snippets"&gt;django-snippets&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/hashes"&gt;hashes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sha1"&gt;sha1&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/signedcookies"&gt;signedcookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/urls"&gt;urls&lt;/a&gt;&lt;/p&gt;



</summary><category term="base65"/><category term="cookies"/><category term="cryptography"/><category term="django"/><category term="django-snippets"/><category term="hashes"/><category term="python"/><category term="security"/><category term="sha1"/><category term="signedcookies"/><category term="urls"/></entry><entry><title>Major Update to Prism</title><link href="https://simonwillison.net/2008/Mar/10/mozilla/#atom-tag" rel="alternate"/><published>2008-03-10T14:03:29+00:00</published><updated>2008-03-10T14:03:29+00:00</updated><id>https://simonwillison.net/2008/Mar/10/mozilla/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://labs.mozilla.com/2008/03/major-update-to-prism-first-prototype-of-browser-integration/"&gt;Major Update to Prism&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Mozilla’s site-specific browser tool can now use separate profiles (and hence separate cookie jars) for each instance, making it an excellent tool for protecting yourself against CSRF vulnerabilities in the web applications you rely on.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://ajaxian.com/archives/mozilla-prism-update-makes-it-easy-to-create-wrappers"&gt;Ajaxian&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mozilla"&gt;mozilla&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prism"&gt;prism&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sitespecificbrowsers"&gt;sitespecificbrowsers&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="csrf"/><category term="mozilla"/><category term="prism"/><category term="sitespecificbrowsers"/></entry><entry><title>Fluid</title><link href="https://simonwillison.net/2007/Dec/28/fluid/#atom-tag" rel="alternate"/><published>2007-12-28T23:42:43+00:00</published><updated>2007-12-28T23:42:43+00:00</updated><id>https://simonwillison.net/2007/Dec/28/fluid/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://fluidapp.com/"&gt;Fluid&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Another site-specific browser toolkit for OS X (Leopard only), from Todd Ditchendorf. Again, it’s not clear if this does the Right Thing and creates separate cookie jars for every application.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fluid"&gt;fluid&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/leopard"&gt;leopard&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/macos"&gt;macos&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sitespecificbrowsers"&gt;sitespecificbrowsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/toddditchendorf"&gt;toddditchendorf&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="fluid"/><category term="leopard"/><category term="macos"/><category term="sitespecificbrowsers"/><category term="toddditchendorf"/></entry><entry><title>Site-specific browsers and GreaseKit</title><link href="https://simonwillison.net/2007/Oct/25/sitespecific/#atom-tag" rel="alternate"/><published>2007-10-25T07:56:01+00:00</published><updated>2007-10-25T07:56:01+00:00</updated><id>https://simonwillison.net/2007/Oct/25/sitespecific/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://factoryjoe.com/blog/2007/10/23/site-specific-browsers-and-greasekit/"&gt;Site-specific browsers and GreaseKit&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
New site-specific browser tool which lets you include a bunch of Greasemonkey scripts. For me, the killer feature of site-specific browsers is still cookie isolation (to minimise the impact of XSS and CSRF holes) but none of the current batch of tools advertise this as a feature, and most seem to want to share the system-wide cookie jar.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chris-messina"&gt;chris-messina&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/greasekit"&gt;greasekit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/greasemonkey"&gt;greasemonkey&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/safari"&gt;safari&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sitespecificbrowsers"&gt;sitespecificbrowsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webkit"&gt;webkit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/xss"&gt;xss&lt;/a&gt;&lt;/p&gt;



</summary><category term="chris-messina"/><category term="cookies"/><category term="csrf"/><category term="greasekit"/><category term="greasemonkey"/><category term="javascript"/><category term="safari"/><category term="security"/><category term="sitespecificbrowsers"/><category term="webkit"/><category term="xss"/></entry><entry><title>Quoting Mark Finkle</title><link href="https://simonwillison.net/2007/Sep/30/mark/#atom-tag" rel="alternate"/><published>2007-09-30T16:08:58+00:00</published><updated>2007-09-30T16:08:58+00:00</updated><id>https://simonwillison.net/2007/Sep/30/mark/#atom-tag</id><summary type="html">
    &lt;blockquote cite="http://starkravingfinkle.org/blog/2007/09/webrunner-07-new-and-improved/#comment-3686"&gt;&lt;p&gt;Currently WebRunner applications share cookies with other WebRunner applications, but not with Firefox. WebRunner uses its own profile, not Firefox's profile. There is a plan to allow WebRunner applications to create their own, private profiles as well.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="http://starkravingfinkle.org/blog/2007/09/webrunner-07-new-and-improved/#comment-3686"&gt;Mark Finkle&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mark-finkle"&gt;mark-finkle&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sitespecificbrowsers"&gt;sitespecificbrowsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webrunner"&gt;webrunner&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="csrf"/><category term="firefox"/><category term="mark-finkle"/><category term="security"/><category term="sitespecificbrowsers"/><category term="webrunner"/></entry><entry><title>IE vulnerability allows cookie stealing</title><link href="https://simonwillison.net/2007/Jun/6/ie/#atom-tag" rel="alternate"/><published>2007-06-06T09:53:31+00:00</published><updated>2007-06-06T09:53:31+00:00</updated><id>https://simonwillison.net/2007/Jun/6/ie/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://blog.outer-court.com/archive/2007-06-06-n84.html"&gt;IE vulnerability allows cookie stealing&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Full exploit against the same-domain cookie origin policy, so malicious sites can steal cookies from elsewhere. Avoid using IE until this is patched.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/internet-explorer"&gt;internet-explorer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/samedomain"&gt;samedomain&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="internet-explorer"/><category term="samedomain"/><category term="security"/></entry><entry><title>IE and 2-letter domain-names</title><link href="https://simonwillison.net/2007/Feb/15/ie/#atom-tag" rel="alternate"/><published>2007-02-15T00:33:13+00:00</published><updated>2007-02-15T00:33:13+00:00</updated><id>https://simonwillison.net/2007/Feb/15/ie/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://therealcrisp.xs4all.nl/blog/2007/02/12/ie-and-2-letter-domain-names/"&gt;IE and 2-letter domain-names&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
IE won’t let you set a cookie on XX.YY, where YY is anything other than .pl or .gr. Other browsers have better exception lists.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://diveintomark.org/archives/2007/02/14/links-for-2007-02-14"&gt;Mark Pilgrim&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dns"&gt;dns&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/internet-explorer"&gt;internet-explorer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mark-pilgrim"&gt;mark-pilgrim&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="dns"/><category term="internet-explorer"/><category term="mark-pilgrim"/></entry><entry><title>Quoting Már Örlygsson</title><link href="https://simonwillison.net/2007/Feb/6/subdomains/#atom-tag" rel="alternate"/><published>2007-02-06T00:01:59+00:00</published><updated>2007-02-06T00:01:59+00:00</updated><id>https://simonwillison.net/2007/Feb/6/subdomains/#atom-tag</id><summary type="html">
    &lt;blockquote cite="http://simonwillison.net/2007/Feb/4/urls/#c35427"&gt;&lt;p&gt;There's an unfortunate side-effect to altogether eliminating the sub-domain name from your site URLs [...] Every cookie you may want to set for that site will automatically "bleed" down to &lt;em&gt;all&lt;/em&gt; sub-domain-based websites you might want to add later.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="http://simonwillison.net/2007/Feb/4/urls/#c35427"&gt;Már Örlygsson&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/urls"&gt;urls&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="urls"/></entry><entry><title>Ubuntu sugar cookies</title><link href="https://simonwillison.net/2007/Jan/11/cookies/#atom-tag" rel="alternate"/><published>2007-01-11T14:49:12+00:00</published><updated>2007-01-11T14:49:12+00:00</updated><id>https://simonwillison.net/2007/Jan/11/cookies/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://blog.josephhall.com/2006/11/sugar-cookies.html"&gt;Ubuntu sugar cookies&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Different coloured dough is used to bake the Ubuntu logo in to the cookies themselves, kind of like making sushi rolls.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://www.nedbatchelder.com/blog/200701.html#e20070111T090133"&gt;Ned Batchelder&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cooking"&gt;cooking&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ubuntu"&gt;ubuntu&lt;/a&gt;&lt;/p&gt;



</summary><category term="cookies"/><category term="cooking"/><category term="ubuntu"/></entry><entry><title>Thirty five year old cookies</title><link href="https://simonwillison.net/2003/Mar/9/thirtyFiveYearOldCookies/#atom-tag" rel="alternate"/><published>2003-03-09T14:58:49+00:00</published><updated>2003-03-09T14:58:49+00:00</updated><id>https://simonwillison.net/2003/Mar/9/thirtyFiveYearOldCookies/#atom-tag</id><summary type="html">
    &lt;p&gt;I'm finding myself slightly confused about the Google backlash washing around the blogosphere, which is summarised quite well by &lt;a href="http://www.gavinsblog.com/2003/03/06.html#a117"&gt;Gavin Sheridan&lt;/a&gt;. Most of the arguments against using Google unsurprisingly centre around privacy issues, in particular the "35 year cookie". I was under the impression that cookies could only be set for a maximum of a year, but having checked &lt;a href="http://wp.netscape.com/newsref/std/cookie_spec.html"&gt;Netscape's Cookie Specification&lt;/a&gt; and &lt;a href="ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt" title="HTTP State Management Mechanism"&gt;RFC 2965&lt;/a&gt; it appears I was mistaken.&lt;/p&gt;

&lt;p&gt;So let's take a look at the cookies in question, via the Mozilla project's handy &lt;a href="http://webtools.mozilla.org/web-sniffer/view.cgi?url=http%3A%2F%2Fwww.google.com/" title="View http://www.google.com/"&gt;Web Sniffer utility&lt;/a&gt; (the front page for this tool is &lt;a href="http://webtools.mozilla.org/web-sniffer/" title="View HTTP and HTML Source"&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;blockquote cite="http://webtools.mozilla.org/web-sniffer/view.cgi?url=http%3A%2F%2Fwww.google.com/"&gt;&lt;p&gt;&lt;code&gt;
HTTP/1.0 200 OK&lt;br /&gt;
Content-Length: 3403&lt;br /&gt;
Connection: Keep-Alive&lt;br /&gt;
Server: GWS/2.0&lt;br /&gt;
Date: Sun, 09 Mar 2003 14:34:32 GMT&lt;br /&gt;
Content-Type: text/html&lt;br /&gt;
Cache-control: private&lt;br /&gt;
&lt;strong&gt;Set-Cookie: PREF=ID=05ba0c124de8df6e:TM=1047220472:LM=1047220472:S=Ke2RQCqjCEowS1x-; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com&lt;/strong&gt;
&lt;/code&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;There it is - a 35 year cookie. Now let's take a look at some of Google's competitors.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.alltheweb.com/"&gt;AllTheWeb&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote cite="http://webtools.mozilla.org/web-sniffer/view.cgi?url=http%3A%2F%2Fwww.alltheweb.com/"&gt;&lt;p&gt;&lt;code&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Date: Sun, 09 Mar 2003 14:36:42 GMT&lt;br /&gt;
Server: Apache/1.3.27 (Unix) PHP/4.2.3-atw&lt;br /&gt;
&lt;strong&gt;Set-Cookie: atw-uid=CgVSBj5rUXoAAQnFAwSFAg==; path=/; domain=.alltheweb.com; expires=Sat, 09-Mar-13 02:36:42 GMT&lt;/strong&gt;&lt;br /&gt;
X-Powered-By: PHP/4.2.3-atw&lt;br /&gt;
Last-Modified: Sun, 09 Mar 2003 14:35:00 GMT&lt;br /&gt;
Expires: Thu, 19 Apr 2001 04:25:21 GMT&lt;br /&gt;
Cache-Control: max-age=0, private&lt;br /&gt;
&lt;strong&gt;Set-Cookie: PREF=frschk=1:_lm=1047220602; expires=Fri, 07-Mar-08 14:36:42 GMT; path=/&lt;/strong&gt;&lt;br /&gt;
Connection: close&lt;br /&gt;
Content-Type: text/html; charset=iso-8859-1
&lt;/code&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;That's two cookies - one for 5 years and one for 10 years. Interesting to see that they're using their own modified version of &lt;acronym title="PHP: Hypertext Preprocessor"&gt;PHP&lt;/acronym&gt; 4.2.3 :)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.teoma.com/"&gt;Teoma&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote cite="http://webtools.mozilla.org/web-sniffer/view.cgi?url=http%3A%2F%2Fwww.teoma.com/"&gt;&lt;p&gt;&lt;code&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Server: Microsoft-IIS/5.0&lt;br /&gt;
Date: Sun, 09 Mar 2003 14:38:50 GMT&lt;br /&gt;
Connection: Keep-Alive&lt;br /&gt;
Content-Length: 6629&lt;br /&gt;
Content-Type: text/html&lt;br /&gt;
&lt;strong&gt;Set-Cookie: CTST=yes; expires=Sun, 09-Mar-2003 15:03:50 GMT; path=/&lt;/strong&gt;&lt;br /&gt;
Cache-control: private
&lt;/code&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;That cookie lasts for about half an hour and doesn't contain a unique identifier. Plus they're running &lt;acronym title="Internet Information Server"&gt;IIS&lt;/acronym&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.altavista.com/"&gt;Altavista&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote cite="http://webtools.mozilla.org/web-sniffer/view.cgi?url=http%3A%2F%2Fwww.altavista.com/"&gt;&lt;p&gt;&lt;code&gt;
HTTP/1.0 200 OK
&lt;strong&gt;Set-Cookie: AV_POS=pos=1047220999574; path=/; domain=.altavista.com;&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;Set-Cookie: AV_USERKEY=AVS03b87123ae55d80a1c21250000022; expires=Tuesday, 31-Dec-2013 12:00:00 GMT; path=/; domain=altavista.com;&lt;/strong&gt;&lt;br /&gt;
Server: AV/1.0.1&lt;br /&gt;
MIME-Version: 1.0&lt;br /&gt;
Cache-Control: no-cache,no-store,max-age=0&lt;br /&gt;
pragma: no-cache&lt;br /&gt;
Expires: Sun, 09 Mar 2003 14:43:19 GMT&lt;br /&gt;
&lt;strong&gt;Set-Cookie: AV_MKT=1; Domain=altavista.com; Path=/; Expires=Thu, 01-Dec-1994 16:00:00 GMT&lt;/strong&gt;&lt;br /&gt;
Content-Type: text/html; charset=ISO-8859-1&lt;br /&gt;
Content-Length: 10020&lt;br /&gt;
Date: Sun, 09 Mar 2003 14:43:19 GMT
&lt;/code&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;What a mess! There's a session cookie (which only lasts until the browser s closed) recording what looks like the time I first visited the front page, a 10 year cookie with a unique ID and another cookie set to expire in 1994, possibly in an attempt to wipe out cookies set by an older version of the site.&lt;/p&gt;

&lt;p&gt;So what have we learnt? Both AllTheWeb and Altavista set 10 year unique identifier cookies, while Teoma appears not to set any. At the end of the day though, what is the difference between a 10 year and a 35 year cookie? How many people are going to go a whole ten years without losing their browser's cookies, through a browser upgrade, PC upgrade, change of job or just wiping the cookie directory? Thee answer to that question is self evident, so in practise a 10 year unique identifier cookie is just as big an invasion of privacy as a 35 year cookie.&lt;/p&gt;

&lt;p&gt;On the privacy front, AllTheWeb and Altavista are just as guilty as Google.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/privacy"&gt;privacy&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="cookies"/><category term="google"/><category term="privacy"/></entry></feed>