|
| 1 | +--- |
| 2 | +layout: epic |
| 3 | +title: "Hacking Around Safari's 7-day Cookie Limit" |
| 4 | +subtitle: What we learned while trying to fix our cookie consent banner |
| 5 | +date: 2022-08-23 |
| 6 | +categories: [Safari Cookies Consent Banner] |
| 7 | +author: [chris] |
| 8 | +--- |
| 9 | + |
| 10 | +Amongst the many, many things that organizations have to contend with around |
| 11 | +cookie consent laws is Apple's very own Safari. Did you know that Safari will |
| 12 | +only persist client-side cookies for 7 days? This is in support of Apple's |
| 13 | +[Intelligent Tracking Prevention (ITP)](https://clearcode.cc/blog/intelligent-tracking-prevention-faq), |
| 14 | +designed to improve user privacy. |
| 15 | + |
| 16 | +These privacy efforts are great for users, but in hand with laws like the |
| 17 | +[GDPR](https://gdpr-info.eu/) and [CCPA](https://oag.ca.gov/privacy/ccpa) they |
| 18 | +create a UX nightmare for users. Here at Artsy we've landed on a way to make |
| 19 | +things slightly less bad and want to share our approach. |
| 20 | + |
| 21 | +<!-- more --> |
| 22 | + |
| 23 | +Scenario: Imagine you're a resident of the EU and visit Artsy.net for the first |
| 24 | +time. A banner appears asking you to `Accept` or `Deny` tracking cookies from |
| 25 | +our site. You don't like tracking cookies, so you click the 'Deny' button and |
| 26 | +the banner disappears. All good, right? Nope! You visit Artsy a week later and |
| 27 | +again, a banner appears asking you about your preference. This happens again and |
| 28 | +again until you switch browsers and realize that what you were just experiencing |
| 29 | +was Apple's ITP in action; even though you've rejected tracking cookies, the |
| 30 | +cookie we've used to store your preferences is erased after 7 days, |
| 31 | +necessitating another interaction. |
| 32 | + |
| 33 | +While pondering how to work around this UX issue we landed on a pretty simple |
| 34 | +solution, prompted by a comment from a Safari Engineer we were able schedule a |
| 35 | +conference call with. She mentioned that the 7-day cookie limitation only |
| 36 | +applies to _client-side cookies_, and that **same-domain, secure, server-side |
| 37 | +cookies aren't limited to these constraints**. |
| 38 | + |
| 39 | +This got us thinking. Our 3rd party cookie-consent management service sets a |
| 40 | +client-side cookie, not a server-side cookie. Could we perhaps "hijack" the |
| 41 | +client-side cookie with a server-side cookie _of the same name_ and trick safari |
| 42 | +into persisting the user preferences across the 7-day limit? |
| 43 | + |
| 44 | +We gave it a try and... Yes. We. Can! And this means that you can too. (And it's |
| 45 | +also real easy to implement.) |
| 46 | + |
| 47 | +--- |
| 48 | + |
| 49 | +First, define a server-side endpoint: |
| 50 | + |
| 51 | +```tsx |
| 52 | +const app = express() |
| 53 | + |
| 54 | +app.get("/set-tracking-preferences", (req, res) => { |
| 55 | + const { trackingPreferences } = req.query |
| 56 | + |
| 57 | + const cookieConfig = { |
| 58 | + maxAge: 10000000, |
| 59 | + httpOnly: false, |
| 60 | + secure: true, // important! |
| 61 | + } |
| 62 | + |
| 63 | + if (trackingPreferences !== "undefined") { |
| 64 | + // Overwrite client-side cookie with cloned, secure server-side version |
| 65 | + res.cookie("trackingPreferences", trackingPreferences, cookieConfig) |
| 66 | + } |
| 67 | + |
| 68 | + res.send("Tracking preferences set.") |
| 69 | +}) |
| 70 | +``` |
| 71 | + |
| 72 | +Next, call the server-side endpoint from the client when your app boots or when |
| 73 | +the preferences have been set by the user: |
| 74 | + |
| 75 | +```tsx |
| 76 | +import cookies from "cookies-js" |
| 77 | + |
| 78 | +const App = () => { |
| 79 | + useEffect(() => { |
| 80 | + const trackingPreferences = encodeURIComponent(cookies.get("trackingPreferences")) |
| 81 | + |
| 82 | + fetch( |
| 83 | + `/set-tracking-preferences?trackingPreferences=${trackingPreferences}` |
| 84 | + ) |
| 85 | + }, []) |
| 86 | + |
| 87 | + return <CookieConsentBanner onClick={...} /> |
| 88 | +} |
| 89 | +``` |
| 90 | + |
| 91 | +And that's it. While I'm not sure of the exact reason why we can overwrite |
| 92 | +cookies in this way, I suspect it has to do with Safari's assumption that a |
| 93 | +server we control will always take precedence thus assumes things are safe. And |
| 94 | +in seven days, when Safari returns to erase the cookie, it will see that it's |
| 95 | +now `secure` and will ignore, preserving the user's preferences. |
0 commit comments