├── Gemfile ├── config.ru ├── dist ├── Sakurity_files │ ├── main.js │ ├── 687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d5a7047536b6747534436592f5554426e644b31737962492f414141414141414142706b2f66765749556e64456579672f733332302f6d616c2e706e67 │ ├── 687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d58726a474e3634526f65342f5554426f357171643767492f41414141414141414270302f58435467305061534761552f733332302f6d616c2b2832292e706e67 │ ├── 687474703a2f2f322e62702e626c6f6773706f742e636f6d2f2d4f5831494c38786e306b4d2f545f4c6e316478344769492f4141414141414141414d6b2f6f4b654678795a757430492f733332302f53637265656e73686f742b2d2b30373033323031322b2d2b30343a33373a30362b504d2e706e67 │ ├── 687474703a2f2f332e62702e626c6f6773706f742e636f6d2f2d33726e39786a75335169492f545f4c6e312d6834585a492f4141414141414141414d6f2f504c736a316a4a444154492f733332302f53637265656e73686f742b2d2b30373033323031322b2d2b30343a33383a32352b504d2e706e67 │ ├── 687474703a2f2f332e62702e626c6f6773706f742e636f6d2f2d436e5151396b6a506f56732f5576545f4f306d357571492f4141414141414141446b452f5f526c5f455976344143512f73313630302f53637265656e2b53686f742b323031342d30322d30352b61742b352e31352e33392b504d2e706e67 │ ├── 687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d6755755872315f473548412f55325073625a746f3143492f41414141414141414472382f56616a337357664b426e4d2f73313630302f53637265656e2b53686f742b323031342d30352d30322b61742b332e30342e31302b504d2e706e67 │ ├── css │ ├── modernizr.js │ └── ga.js └── Sakurity.html ├── Gemfile.lock ├── app.rb ├── .gitignore └── README.md /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | gem 'sinatra' 3 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require './app.rb' 2 | run Sinatra::Application 3 | 4 | -------------------------------------------------------------------------------- /dist/Sakurity_files/main.js: -------------------------------------------------------------------------------- 1 | console.log("Hacker? We might need your help: info@sakurity.com"); -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | rack (1.5.2) 5 | rack-protection (1.5.3) 6 | rack 7 | sinatra (1.4.5) 8 | rack (~> 1.4) 9 | rack-protection (~> 1.4) 10 | tilt (~> 1.3, >= 1.3.4) 11 | tilt (1.4.1) 12 | 13 | PLATFORMS 14 | ruby 15 | 16 | DEPENDENCIES 17 | sinatra 18 | -------------------------------------------------------------------------------- /app.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'sinatra' 3 | 4 | 5 | 6 | 7 | set :views, settings.root 8 | set :public_folder, 'dist' 9 | 10 | #set :show_exceptions, false 11 | 12 | disable :sessions 13 | disable :protection 14 | 15 | 16 | get '/' do 17 | # use index.haml for readme 18 | File.open('dist/Sakurity.html') 19 | #markdown :a #, :layout => :index 20 | end 21 | 22 | get '/d' do 23 | File.open('a.html') 24 | end -------------------------------------------------------------------------------- /dist/Sakurity_files/687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d5a7047536b6747534436592f5554426e644b31737962492f414141414141414142706b2f66765749556e64456579672f733332302f6d616c2e706e67: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snyff/oauthsecurity/HEAD/dist/Sakurity_files/687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d5a7047536b6747534436592f5554426e644b31737962492f414141414141414142706b2f66765749556e64456579672f733332302f6d616c2e706e67 -------------------------------------------------------------------------------- /dist/Sakurity_files/687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d58726a474e3634526f65342f5554426f357171643767492f41414141414141414270302f58435467305061534761552f733332302f6d616c2b2832292e706e67: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snyff/oauthsecurity/HEAD/dist/Sakurity_files/687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d58726a474e3634526f65342f5554426f357171643767492f41414141414141414270302f58435467305061534761552f733332302f6d616c2b2832292e706e67 -------------------------------------------------------------------------------- /dist/Sakurity_files/687474703a2f2f322e62702e626c6f6773706f742e636f6d2f2d4f5831494c38786e306b4d2f545f4c6e316478344769492f4141414141414141414d6b2f6f4b654678795a757430492f733332302f53637265656e73686f742b2d2b30373033323031322b2d2b30343a33373a30362b504d2e706e67: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snyff/oauthsecurity/HEAD/dist/Sakurity_files/687474703a2f2f322e62702e626c6f6773706f742e636f6d2f2d4f5831494c38786e306b4d2f545f4c6e316478344769492f4141414141414141414d6b2f6f4b654678795a757430492f733332302f53637265656e73686f742b2d2b30373033323031322b2d2b30343a33373a30362b504d2e706e67 -------------------------------------------------------------------------------- /dist/Sakurity_files/687474703a2f2f332e62702e626c6f6773706f742e636f6d2f2d33726e39786a75335169492f545f4c6e312d6834585a492f4141414141414141414d6f2f504c736a316a4a444154492f733332302f53637265656e73686f742b2d2b30373033323031322b2d2b30343a33383a32352b504d2e706e67: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snyff/oauthsecurity/HEAD/dist/Sakurity_files/687474703a2f2f332e62702e626c6f6773706f742e636f6d2f2d33726e39786a75335169492f545f4c6e312d6834585a492f4141414141414141414d6f2f504c736a316a4a444154492f733332302f53637265656e73686f742b2d2b30373033323031322b2d2b30343a33383a32352b504d2e706e67 -------------------------------------------------------------------------------- /dist/Sakurity_files/687474703a2f2f332e62702e626c6f6773706f742e636f6d2f2d436e5151396b6a506f56732f5576545f4f306d357571492f4141414141414141446b452f5f526c5f455976344143512f73313630302f53637265656e2b53686f742b323031342d30322d30352b61742b352e31352e33392b504d2e706e67: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snyff/oauthsecurity/HEAD/dist/Sakurity_files/687474703a2f2f332e62702e626c6f6773706f742e636f6d2f2d436e5151396b6a506f56732f5576545f4f306d357571492f4141414141414141446b452f5f526c5f455976344143512f73313630302f53637265656e2b53686f742b323031342d30322d30352b61742b352e31352e33392b504d2e706e67 -------------------------------------------------------------------------------- /dist/Sakurity_files/687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d6755755872315f473548412f55325073625a746f3143492f41414141414141414472382f56616a337357664b426e4d2f73313630302f53637265656e2b53686f742b323031342d30352d30322b61742b332e30342e31302b504d2e706e67: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snyff/oauthsecurity/HEAD/dist/Sakurity_files/687474703a2f2f342e62702e626c6f6773706f742e636f6d2f2d6755755872315f473548412f55325073625a746f3143492f41414141414141414472382f56616a337357664b426e4d2f73313630302f53637265656e2b53686f742b323031342d30352d30322b61742b332e30342e31302b504d2e706e67 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | # Ignore bundler config 8 | /.bundle 9 | 10 | # Ignore that homakov is an Apple fanboy ;) 11 | .DS_Store 12 | 13 | # Ignore the default SQLite database. 14 | /db/*.sqlite3 15 | 16 | # Ignore all logfiles and tempfiles. 17 | /log/*.log 18 | /tmp 19 | -------------------------------------------------------------------------------- /dist/Sakurity_files/css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto'; 3 | font-style: normal; 4 | font-weight: 300; 5 | src: local('Roboto Light'), local('Roboto-Light'), url(https://themes.googleusercontent.com/static/fonts/roboto/v11/Hgo13k-tfSpn0qi1SFdUfbO3LdcAZYWl9Si6vvxL-qU.woff) format('woff'); 6 | } 7 | @font-face { 8 | font-family: 'Roboto'; 9 | font-style: normal; 10 | font-weight: 400; 11 | src: local('Roboto Regular'), local('Roboto-Regular'), url(https://themes.googleusercontent.com/static/fonts/roboto/v11/CrYjSnGjrRCn0pd9VQsnFOvvDin1pK8aKteLpeZ5c0A.woff) format('woff'); 12 | } 13 | @font-face { 14 | font-family: 'Roboto'; 15 | font-style: normal; 16 | font-weight: 700; 17 | src: local('Roboto Bold'), local('Roboto-Bold'), url(https://themes.googleusercontent.com/static/fonts/roboto/v11/d-6IYplOFocCacKzxwXSOLO3LdcAZYWl9Si6vvxL-qU.woff) format('woff'); 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OAuth Security Cheatsheet 2 | 3 | This document aims to describe common OAuth/Single Sign On/OpenID-related vulnerabilities. Many cross-site interactions are vulnerable to different kinds of leakings and hijackings. 4 | 5 | Both hackers and developers can benefit from reading it. 6 | 7 | OAuth is a critical functionality. It is responsible for access to sensitive user data, authentication and authorization. **Poorly implemented OAuth is a reliable way to take over an account**. Unlike XSS, it is easy to exploit, but hard to mitigate for victims (NoScript won't help, JavaScript is not required). 8 | 9 | Because of OAuth many startups including Soundcloud, Foursquare, Airbnb, About.me, Bit.ly, Pinterest, Digg, Stumbleupon, Songkick had an account hijacking vulnerability. And a lot of websites are still vulnerable. **Our motivation is to make people aware of "Social login" risks, and we encourage you to use OAuth very carefully.** 10 | 11 | The cheatsheet **does not** explain how OAuth flows work, please look for it on [the official website](http://oauth.net/). 12 | 13 | ## Authorization Code flow 14 | 15 | ### Client account hijacking by connecting attacker's Provider account. 16 | 17 | [Also known as The Most Common OAuth2 Vulnerability](http://homakov.blogspot.com/2012/07/saferweb-most-common-oauth2.html) 18 | 19 | Provider returns `code` by redirecting user-agent to `SITE/oauth/callback?code=CODE` 20 | Now client must send `code` along with client credentials and `redirect_uri` to obtain `access_token`. 21 | 22 | If client implementation doesn't use `state` parameter to mitigate CSRF, we can easily connect **our provider account** to **victim's client account**. 23 | 24 | ![](http://4.bp.blogspot.com/-ZpGSkgGSD6Y/UTBndK1sybI/AAAAAAAABpk/fvWIUndEeyg/s320/mal.png) 25 | 26 | It works for clients with social login and ability to add a login option to existing master account (screenshots of pinterest.com below). 27 | 28 | ![](http://2.bp.blogspot.com/-OX1IL8xn0kM/T_Ln1dx4GiI/AAAAAAAAAMk/oKeFxyZut0I/s320/Screenshot+-+07032012+-+04:37:06+PM.png) 29 | 30 | ![connect options](http://3.bp.blogspot.com/-3rn9xju3QiI/T_Ln1-h4XZI/AAAAAAAAAMo/PLsj1jJDATI/s320/Screenshot+-+07032012+-+04:38:25+PM.png) 31 | 32 | 33 | **Remediation**: Before sending user to provider generate a random nonce and save it in cookies or session. When user is back make sure `state` you received is equal one from cookies. 34 | 35 | **State fixation bug**: It was possible to fixate `state` [in omniauth](https://github.com/mkdynamic/omniauth-facebook/wiki/CSRF-vulnerability:-CVE-2013-4562) because of [legacy code](https://github.com/mkdynamic/omniauth-facebook/blob/c277322722b6e8fba1eadf9de74927b73fbb86ea/lib/omniauth/strategies/facebook.rb#L105) which utilized user supplied `/connect?state=user_supplied` instead of generating a random one. 36 | 37 | This is another OAuth design issue - sometimes developers want to use `state` for own purposes. Although you can send both values concatenated `state=welcome_landing.random_nonce`, but no doubt it looks ugly. 38 | 39 | ### Client account hijacking abusing session fixation on provider 40 | This is a higher level of the first vulnerability. Even when client properly validates `state` we are able to replace auth cookies on provider with attacker's account: using CSRF on login (VK, Facebook), header injection, cookie forcing or tossing. 41 | 42 | Then we just load a GET request triggering connect (`/user/auth/facebook` in omniauth), Facebook will respond with attacker's user info (uid=attacker's uid) and it will eventually connect attacker's provider account to victim's client account. 43 | 44 | 45 | **Remediation**: make sure that adding a new social connection requires a valid csrf_token, so it is not possible to trigger process with CSRF. Ideally, use POST instead of GET. 46 | 47 | [Facebook refused to fix CSRF on login from their side.](http://homakov.blogspot.com/2014/01/two-severe-wontfix-vulnerabilities-in.html) 48 | This threat is client's business too, but many libraries remain vulnerable. **Do not expect providers to give you reliable authentication data**. 49 | 50 | 51 | ### Account hijacking by leaking authorization code. 52 | OAuth documentation makes it clear that providers must check the first `redirect_uri` is equal `redirect_uri` client uses to obtain `access_token`. 53 | We didn't really check this because it looked too hard to get it wrong. 54 | Surprisingly **many** providers got it wrong: Foursquare (reported), VK ([report, in Russian](http://habrahabr.ru/post/150756/#comment_5116061)), Github ([could be used to leak tokens to private repos](http://homakov.blogspot.com/2014/02/how-i-hacked-github-again.html)), and a lot of "home made" Single Sign Ons. 55 | 56 | The attack is straightforward: find a leaking page on client's domain, insert cross domain image or a link to your website, then use this page as `redirect_uri`. 57 | When your victim will load crafted URL it will send him to `leaking_page?code=CODE` and victim's user-agent will expose the code in the Referrer header. 58 | 59 | ![](http://3.bp.blogspot.com/-CnQQ9kjPoVs/UvT_O0m5uqI/AAAAAAAADkE/_Rl_EYv4ACQ/s1600/Screen+Shot+2014-02-05+at+5.15.39+PM.png) 60 | 61 | Now you can re-use leaked authorization code on the actual `redirect_uri` to log in in victim account. 62 | 63 | **Remediation**: flexible `redirect_uri` is a bad practise. But if you need it, store redirect_uri for every code you issue and verify it on access_token creation. 64 | 65 | 66 | ## Implicit flow 67 | 68 | ### Leaking access_token/signed_request with an open redirect. 69 | It was a media hype [called "cover redirect"](http://homakov.blogspot.com/2014/05/covert-redirect-faq.html) but in fact it was known for years. You simply need to find an open redirect on client's domain or its subdomains, send it as `redirect_uri` and replace `response_type` with `token,signed_request`. 302 redirect will preserve #fragment, and attacker's Javascript code will have access to location.hash. 70 | 71 | Leaked access_tokens can be used for spam and ruining your privacy. 72 | Furthermore, leaked signed_request is even more sensitive data. By finding an open redirect on client you compromise Login with Facebook completely. 73 | 74 | **Remediation**: whitelist only one redirect_uri in app's settings: 75 | 76 | ![](http://4.bp.blogspot.com/-gUuXr1_G5HA/U2PsbZto1CI/AAAAAAAADr8/Vaj3sWfKBnM/s1600/Screen+Shot+2014-05-02+at+3.04.10+PM.png) 77 | 78 | ###Account hijacking by using access_token issued for attacker's client. 79 | [Also known as One Token to Rule Them All](http://homakov.blogspot.com/2012/08/oauth2-one-accesstoken-to-rule-them-all.html). 80 | This bug is relevant to mobile and client-side apps, because they often use access_token directly supplied by user. 81 | 82 | Imagine, user has many "authorization rings" and gives a ring to every new website where he wants to log in. A malicious website admin can use rings of its users to log in other websites the users use. 83 | 84 | ![](http://4.bp.blogspot.com/-XrjGN64Roe4/UTBo5qqd7gI/AAAAAAAABp0/XCTg0PaSGaU/s320/mal+(2).png) 85 | 86 | **Remediation**: Before accepting user supplied access_token check if it was issued for your client_id at `https://graph.facebook.com/app?fields=id&access_token=TOKEN` 87 | 88 | 89 | ## Transport and JS SDK bugs 90 | (to be continued) 91 | 92 | ## Extra 93 | ### Leaked client credentials threat 94 | Client credetials are not as important as it sounds. All you can do is using leaking pages to leak auth code, then manually getting an access_token for them (providing leaking redirect_uri instead of actual). Even this threat can be mitigated when providers use static redirect_uri. 95 | 96 | ### Session fixation (OAuth1.0) 97 | Main difference between OAuth2 and 1 is the way you transfer parameters to providers. In the first version you send all parameters to provider and obtain according request_token. Then you navigate user to provider?request_token=TOKEN and after authorization user is redirected back to `client/callback?request_token=SAME_TOKEN`. 98 | 99 | The idea of fixation here is we can trick user into accepting Token1 supplied by us which was issued for us, then re-use Token1 on client's callback. 100 | 101 | This is not a severe vulnerability because it is mostly based on phishing. [FYI Paypal express checkout has this bug](http://homakov.blogspot.com/2014/01/token-fixation-in-paypal.html) 102 | 103 | 104 | ### Provider In The Middle. 105 | Many startups have Facebook Connect, and at the same time they are providers too. Being providers, they must redirect users to 3rd party websites, and those are "open redirects" you just cannot fix. It makes this chain possible: Facebook -> Middleware Provider -> Client's token leakage. 106 | 107 | To fix this problem Facebook adds `#_=_` in the end of callback URLs. Your startup should "kill" fragment to prevent leaking. Redirect this way: 108 | 109 | `Location: YOUR_CLIENT/callback?code=code#` 110 | 111 | ### Tricks to bypass redirect_uri validation 112 | 113 | If you are allowed to set subdirectory here are path traversal tricks: 114 | 115 | 1. /old/path/../../new/path 116 | 117 | 2. /old/path/%2e%2e/%2e%2e/new/path 118 | 119 | 3. /old/path/%252e%252e/%252e%252e/new/path 120 | 121 | 4. /new/path///../../old/path/ 122 | 123 | 5. /old/path/.%0a./.%0d./new/path (For Rails, because it strips \n\d\0) 124 | 125 | ### Replay attack. 126 | `code` is sent via GET and potentionally will be stored in the logs. Providers must delete it after use or expire in 5 minutes. 127 | 128 | ## Contributors 129 | [@homakov](http://twitter.com/homakov) and [you?](http://github.com/homakov/oauthsecurity) 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /dist/Sakurity_files/modernizr.js: -------------------------------------------------------------------------------- 1 | window.Modernizr=function(e,t,n){function r(e){b.cssText=e}function o(e,t){return r(S.join(e+";")+(t||""))}function a(e,t){return typeof e===t}function i(e,t){return!!~(""+e).indexOf(t)}function c(e,t){for(var r in e){var o=e[r];if(!i(o,"-")&&b[o]!==n)return"pfx"==t?o:!0}return!1}function s(e,t,r){for(var o in e){var i=t[e[o]];if(i!==n)return r===!1?e[o]:a(i,"function")?i.bind(r||t):i}return!1}function u(e,t,n){var r=e.charAt(0).toUpperCase()+e.slice(1),o=(e+" "+k.join(r+" ")+r).split(" ");return a(t,"string")||a(t,"undefined")?c(o,t):(o=(e+" "+T.join(r+" ")+r).split(" "),s(o,t,n))}function l(){p.input=function(n){for(var r=0,o=n.length;o>r;r++)j[n[r]]=!!(n[r]in E);return j.list&&(j.list=!(!t.createElement("datalist")||!e.HTMLDataListElement)),j}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),p.inputtypes=function(e){for(var r,o,a,i=0,c=e.length;c>i;i++)E.setAttribute("type",o=e[i]),r="text"!==E.type,r&&(E.value=x,E.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(o)&&E.style.WebkitAppearance!==n?(g.appendChild(E),a=t.defaultView,r=a.getComputedStyle&&"textfield"!==a.getComputedStyle(E,null).WebkitAppearance&&0!==E.offsetHeight,g.removeChild(E)):/^(search|tel)$/.test(o)||(r=/^(url|email)$/.test(o)?E.checkValidity&&E.checkValidity()===!1:E.value!=x)),P[e[i]]=!!r;return P}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d,f,m="2.8.1",p={},h=!0,g=t.documentElement,v="modernizr",y=t.createElement(v),b=y.style,E=t.createElement("input"),x=":)",w={}.toString,S=" -webkit- -moz- -o- -ms- ".split(" "),C="Webkit Moz O ms",k=C.split(" "),T=C.toLowerCase().split(" "),N={svg:"http://www.w3.org/2000/svg"},M={},P={},j={},$=[],D=$.slice,F=function(e,n,r,o){var a,i,c,s,u=t.createElement("div"),l=t.body,d=l||t.createElement("body");if(parseInt(r,10))for(;r--;)c=t.createElement("div"),c.id=o?o[r]:v+(r+1),u.appendChild(c);return a=["­",'"].join(""),u.id=v,(l?u:d).innerHTML+=a,d.appendChild(u),l||(d.style.background="",d.style.overflow="hidden",s=g.style.overflow,g.style.overflow="hidden",g.appendChild(d)),i=n(u,e),l?u.parentNode.removeChild(u):(d.parentNode.removeChild(d),g.style.overflow=s),!!i},z=function(t){var n=e.matchMedia||e.msMatchMedia;if(n)return n(t)&&n(t).matches||!1;var r;return F("@media "+t+" { #"+v+" { position: absolute; } }",function(t){r="absolute"==(e.getComputedStyle?getComputedStyle(t,null):t.currentStyle).position}),r},A=function(){function e(e,o){o=o||t.createElement(r[e]||"div"),e="on"+e;var i=e in o;return i||(o.setAttribute||(o=t.createElement("div")),o.setAttribute&&o.removeAttribute&&(o.setAttribute(e,""),i=a(o[e],"function"),a(o[e],"undefined")||(o[e]=n),o.removeAttribute(e))),o=null,i}var r={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return e}(),L={}.hasOwnProperty;f=a(L,"undefined")||a(L.call,"undefined")?function(e,t){return t in e&&a(e.constructor.prototype[t],"undefined")}:function(e,t){return L.call(e,t)},Function.prototype.bind||(Function.prototype.bind=function(e){var t=this;if("function"!=typeof t)throw new TypeError;var n=D.call(arguments,1),r=function(){if(this instanceof r){var o=function(){};o.prototype=t.prototype;var a=new o,i=t.apply(a,n.concat(D.call(arguments)));return Object(i)===i?i:a}return t.apply(e,n.concat(D.call(arguments)))};return r}),M.flexbox=function(){return u("flexWrap")},M.flexboxlegacy=function(){return u("boxDirection")},M.canvas=function(){var e=t.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))},M.canvastext=function(){return!(!p.canvas||!a(t.createElement("canvas").getContext("2d").fillText,"function"))},M.webgl=function(){return!!e.WebGLRenderingContext},M.touch=function(){var n;return"ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch?n=!0:F(["@media (",S.join("touch-enabled),("),v,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(e){n=9===e.offsetTop}),n},M.geolocation=function(){return"geolocation"in navigator},M.postmessage=function(){return!!e.postMessage},M.websqldatabase=function(){return!!e.openDatabase},M.indexedDB=function(){return!!u("indexedDB",e)},M.hashchange=function(){return A("hashchange",e)&&(t.documentMode===n||t.documentMode>7)},M.history=function(){return!(!e.history||!history.pushState)},M.draganddrop=function(){var e=t.createElement("div");return"draggable"in e||"ondragstart"in e&&"ondrop"in e},M.websockets=function(){return"WebSocket"in e||"MozWebSocket"in e},M.rgba=function(){return r("background-color:rgba(150,255,150,.5)"),i(b.backgroundColor,"rgba")},M.hsla=function(){return r("background-color:hsla(120,40%,100%,.5)"),i(b.backgroundColor,"rgba")||i(b.backgroundColor,"hsla")},M.multiplebgs=function(){return r("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(b.background)},M.backgroundsize=function(){return u("backgroundSize")},M.borderimage=function(){return u("borderImage")},M.borderradius=function(){return u("borderRadius")},M.boxshadow=function(){return u("boxShadow")},M.textshadow=function(){return""===t.createElement("div").style.textShadow},M.opacity=function(){return o("opacity:.55"),/^0.55$/.test(b.opacity)},M.cssanimations=function(){return u("animationName")},M.csscolumns=function(){return u("columnCount")},M.cssgradients=function(){var e="background-image:",t="gradient(linear,left top,right bottom,from(#9f9),to(white));",n="linear-gradient(left top,#9f9, white);";return r((e+"-webkit- ".split(" ").join(t+e)+S.join(n+e)).slice(0,-e.length)),i(b.backgroundImage,"gradient")},M.cssreflections=function(){return u("boxReflect")},M.csstransforms=function(){return!!u("transform")},M.csstransforms3d=function(){var e=!!u("perspective");return e&&"webkitPerspective"in g.style&&F("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(t){e=9===t.offsetLeft&&3===t.offsetHeight}),e},M.csstransitions=function(){return u("transition")},M.fontface=function(){var e;return F('@font-face {font-family:"font";src:url("https://")}',function(n,r){var o=t.getElementById("smodernizr"),a=o.sheet||o.styleSheet,i=a?a.cssRules&&a.cssRules[0]?a.cssRules[0].cssText:a.cssText||"":"";e=/src/i.test(i)&&0===i.indexOf(r.split(" ")[0])}),e},M.generatedcontent=function(){var e;return F(["#",v,"{font:0/0 a}#",v,':after{content:"',x,'";visibility:hidden;font:3px/1 a}'].join(""),function(t){e=t.offsetHeight>=3}),e},M.video=function(){var e=t.createElement("video"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),n.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),n.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""))}catch(r){}return n},M.audio=function(){var e=t.createElement("audio"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),n.mp3=e.canPlayType("audio/mpeg;").replace(/^no$/,""),n.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),n.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(r){}return n},M.localstorage=function(){try{return localStorage.setItem(v,v),localStorage.removeItem(v),!0}catch(e){return!1}},M.sessionstorage=function(){try{return sessionStorage.setItem(v,v),sessionStorage.removeItem(v),!0}catch(e){return!1}},M.webworkers=function(){return!!e.Worker},M.applicationcache=function(){return!!e.applicationCache},M.svg=function(){return!!t.createElementNS&&!!t.createElementNS(N.svg,"svg").createSVGRect},M.inlinesvg=function(){var e=t.createElement("div");return e.innerHTML="",(e.firstChild&&e.firstChild.namespaceURI)==N.svg},M.smil=function(){return!!t.createElementNS&&/SVGAnimate/.test(w.call(t.createElementNS(N.svg,"animate")))},M.svgclippaths=function(){return!!t.createElementNS&&/SVGClipPath/.test(w.call(t.createElementNS(N.svg,"clipPath")))};for(var H in M)f(M,H)&&(d=H.toLowerCase(),p[d]=M[H](),$.push((p[d]?"":"no-")+d));return p.input||l(),p.addTest=function(e,t){if("object"==typeof e)for(var r in e)f(e,r)&&p.addTest(r,e[r]);else{if(e=e.toLowerCase(),p[e]!==n)return p;t="function"==typeof t?t():t,"undefined"!=typeof h&&h&&(g.className+=" "+(t?"":"no-")+e),p[e]=t}return p},r(""),y=E=null,function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=y.elements;return"string"==typeof e?e.split(" "):e}function o(e){var t=v[e[h]];return t||(t={},g++,e[h]=g,v[g]=t),t}function a(e,n,r){if(n||(n=t),l)return n.createElement(e);r||(r=o(n));var a;return a=r.cache[e]?r.cache[e].cloneNode():p.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!a.canHaveChildren||m.test(e)||a.tagUrn?a:r.frag.appendChild(a)}function i(e,n){if(e||(e=t),l)return e.createDocumentFragment();n=n||o(e);for(var a=n.frag.cloneNode(),i=0,c=r(),s=c.length;s>i;i++)a.createElement(c[i]);return a}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return y.shivMethods?a(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(y,t.frag)}function s(e){e||(e=t);var r=o(e);return!y.shivCSS||u||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||c(e,r),e}var u,l,d="3.7.0",f=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",g=0,v={};!function(){try{var e=t.createElement("a");e.innerHTML="",u="hidden"in e,l=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){u=!0,l=!0}}();var y={elements:f.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:d,shivCSS:f.shivCSS!==!1,supportsUnknownElements:l,shivMethods:f.shivMethods!==!1,type:"default",shivDocument:s,createElement:a,createDocumentFragment:i};e.html5=y,s(t)}(this,t),p._version=m,p._prefixes=S,p._domPrefixes=T,p._cssomPrefixes=k,p.mq=z,p.hasEvent=A,p.testProp=function(e){return c([e])},p.testAllProps=u,p.testStyles=F,p.prefixed=function(e,t,n){return t?u(e,t,n):u(e,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(h?" js "+$.join(" "):""),p}(this,this.document); -------------------------------------------------------------------------------- /dist/Sakurity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | OAuth Security 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 38 | 39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 |

48 | OAuth Security Cheatsheet

49 | 50 |

This document aims to describe common OAuth/Single Sign On/OpenID-related vulnerabilities. Many cross-site interactions are vulnerable to different kinds of leakings and hijackings.

51 | 52 |

Both hackers and developers can benefit from reading it.

53 | 54 |

OAuth is a critical functionality. It is responsible for access to sensitive user data, authentication and authorization. Poorly implemented OAuth is a reliable way to take over an account. Unlike XSS, it is easy to exploit, but hard to mitigate for victims (NoScript won't help, JavaScript is not required).

55 | 56 |

Because of OAuth many startups including Soundcloud, Foursquare, Airbnb, About.me, Bit.ly, Pinterest, Digg, Stumbleupon, Songkick had an account hijacking vulnerability. And a lot of websites are still vulnerable. Our motivation is to make people aware of "Social login" risks, and we encourage you to use OAuth very carefully.

57 | 58 |

The cheatsheet does not explain how OAuth flows work, please look for it on the official website.

59 | 60 |

61 | Authorization Code flow

62 | 63 |

64 | Client account hijacking by connecting attacker's Provider account.

65 | 66 |

Also known as The Most Common OAuth2 Vulnerability

67 | 68 |

Provider returns code by redirecting user-agent to SITE/oauth/callback?code=CODE 69 | Now client must send code along with client credentials and redirect_uri to obtain access_token.

70 | 71 |

If client implementation doesn't use state parameter to mitigate CSRF, we can easily connect our provider account to victim's client account.

72 | 73 |

74 | 75 |

It works for clients with social login and ability to add a login option to existing master account (screenshots of pinterest.com below).

76 | 77 |

78 | 79 |

connect options

80 | 81 |

Remediation: Before sending user to provider generate a random nonce and save it in cookies or session. When user is back make sure state you received is equal one from cookies.

82 | 83 |

State fixation bug: It was possible to fixate state in omniauth because of legacy code which utilized user supplied /connect?state=user_supplied instead of generating a random one.

84 | 85 |

This is another OAuth design issue - sometimes developers want to use state for own purposes. Although you can send both values concatenated state=welcome_landing.random_nonce, but no doubt it looks ugly.

86 | 87 |

88 | Client account hijacking abusing session fixation on provider

89 | 90 |

This is a higher level of the first vulnerability. Even when client properly validates state we are able to replace auth cookies on provider with attacker's account: using CSRF on login (VK, Facebook), header injection, cookie forcing or tossing.

91 | 92 |

Then we just load a GET request triggering connect (/user/auth/facebook in omniauth), Facebook will respond with attacker's user info (uid=attacker's uid) and it will eventually connect attacker's provider account to victim's client account.

93 | 94 |

Remediation: make sure that adding a new social connection requires a valid csrf_token, so it is not possible to trigger process with CSRF. Ideally, use POST instead of GET.

95 | 96 |

Facebook refused to fix CSRF on login from their side. 97 | This threat is client's business too, but many libraries remain vulnerable. Do not expect providers to give you reliable authentication data.

98 | 99 |

100 | Account hijacking by leaking authorization code.

101 | 102 |

OAuth documentation makes it clear that providers must check the first redirect_uri is equal redirect_uri client uses to obtain access_token. 103 | We didn't really check this because it looked too hard to get it wrong. 104 | Surprisingly many providers got it wrong: Foursquare (reported), VK (report, in Russian), Github (could be used to leak tokens to private repos), and a lot of "home made" Single Sign Ons.

105 | 106 |

The attack is straightforward: find a leaking page on client's domain, insert cross domain image or a link to your website, then use this page as redirect_uri. 107 | When your victim will load crafted URL it will send him to leaking_page?code=CODE and victim's user-agent will expose the code in the Referrer header.

108 | 109 |

110 | 111 |

Now you can re-use leaked authorization code on the actual redirect_uri to log in in victim account.

112 | 113 |

Remediation: flexible redirect_uri is a bad practise. But if you need it, store redirect_uri for every code you issue and verify it on access_token creation.

114 | 115 |

116 | Implicit flow

117 | 118 |

119 | Leaking access_token/signed_request with an open redirect.

120 | 121 |

It was a media hype called "cover redirect" but in fact it was known for years. You simply need to find an open redirect on client's domain or its subdomains, send it as redirect_uri and replace response_type with token,signed_request. 302 redirect will preserve #fragment, and attacker's Javascript code will have access to location.hash.

122 | 123 |

Leaked access_tokens can be used for spam and ruining your privacy. 124 | Furthermore, leaked signed_request is even more sensitive data. By finding an open redirect on client you compromise Login with Facebook completely.

125 | 126 |

Remediation: whitelist only one redirect_uri in app's settings:

127 | 128 |

129 | 130 |

131 | Account hijacking by using access_token issued for attacker's client.

132 | 133 |

Also known as One Token to Rule Them All. 134 | This bug is relevant to mobile and client-side apps, because they often use access_token directly supplied by user.

135 | 136 |

Imagine, user has many "authorization rings" and gives a ring to every new website where he wants to log in. A malicious website admin can use rings of its users to log in other websites the users use.

137 | 138 |

139 | 140 |

Remediation: Before accepting user supplied access_token check if it was issued for your client_id at https://graph.facebook.com/app?fields=id&access_token=TOKEN

141 | 142 |

143 | Transport and JS SDK bugs

144 | 145 |

(to be continued)

146 | 147 |

148 | Extra

149 | 150 |

151 | Leaked client credentials threat

152 | 153 |

Client credetials are not as important as it sounds. All you can do is using leaking pages to leak auth code, then manually getting an access_token for them (providing leaking redirect_uri instead of actual). Even this threat can be mitigated when providers use static redirect_uri.

154 | 155 |

156 | Session fixation (OAuth1.0)

157 | 158 |

Main difference between OAuth2 and 1 is the way you transfer parameters to providers. In the first version you send all parameters to provider and obtain according request_token. Then you navigate user to provider?request_token=TOKEN and after authorization user is redirected back to client/callback?request_token=SAME_TOKEN.

159 | 160 |

The idea of fixation here is we can trick user into accepting Token1 supplied by us which was issued for us, then re-use Token1 on client's callback.

161 | 162 |

This is not a severe vulnerability because it is mostly based on phishing. FYI Paypal express checkout has this bug

163 | 164 |

165 | Provider In The Middle.

166 | 167 |

Many startups have Facebook Connect, and at the same time they are providers too. Being providers, they must redirect users to 3rd party websites, and those are "open redirects" you just cannot fix. It makes this chain possible: Facebook -> Middleware Provider -> Client's token leakage.

168 | 169 |

To fix this problem Facebook adds #_=_ in the end of callback URLs. Your startup should "kill" fragment to prevent leaking. Redirect this way:

170 | 171 |

Location: YOUR_CLIENT/callback?code=code#

172 | 173 |

174 | Tricks to bypass redirect_uri validation

175 | 176 |

If you are allowed to set subdirectory here are path traversal tricks:

177 | 178 |
    179 |
  1. /old/path/../../new/path

  2. 180 |
  3. /old/path/%2e%2e/%2e%2e/new/path

  4. 181 |
  5. /old/path/%252e%252e/%252e%252e/new/path

  6. 182 |
  7. /new/path///../../old/path/

  8. 183 |
  9. /old/path/.%0a./.%0d./new/path (For Rails, because it strips \n\d\0)

  10. 184 |

185 | Replay attack.

186 | 187 |

code is sent via GET and potentionally will be stored in the logs. Providers must delete it after use or expire in 5 minutes.

188 | 189 |

190 | Contributors

191 | 192 |

@homakov and you?

193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 |
201 | 202 |
203 |
204 | 205 | 210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /dist/Sakurity_files/ga.js: -------------------------------------------------------------------------------- 1 | (function(){var aa=encodeURIComponent,ba=Infinity,ca=setTimeout,da=isNaN,m=Math,ea=decodeURIComponent;function Ie(a,b){return a.onload=b}function Je(a,b){return a.onerror=b}function ha(a,b){return a.name=b} 2 | var n="push",ia="test",ja="slice",p="replace",ka="load",la="floor",ma="charAt",na="value",q="indexOf",oa="match",pa="port",qa="createElement",ra="path",r="name",g="getTime",u="host",v="toString",w="length",x="prototype",sa="clientWidth",y="split",ta="stopPropagation",ua="scope",z="location",va="search",A="protocol",wa="clientHeight",xa="href",B="substring",ya="apply",za="navigator",C="join",D="toLowerCase",E;function Aa(a,b){switch(b){case 0:return""+a;case 1:return 1*a;case 2:return!!a;case 3:return 1E3*a}return a}function Ba(a){return"function"==typeof a}function Ca(a){return void 0!=a&&-1<(a.constructor+"")[q]("String")}function F(a,b){return void 0==a||"-"==a&&!b||""==a}function Da(a){if(!a||""==a)return"";for(;a&&-1<" \n\r\t"[q](a[ma](0));)a=a[B](1);for(;a&&-1<" \n\r\t"[q](a[ma](a[w]-1));)a=a[B](0,a[w]-1);return a}function Ea(){return m.round(2147483647*m.random())}function Fa(){} 3 | function G(a,b){if(aa instanceof Function)return b?encodeURI(a):aa(a);H(68);return escape(a)}function I(a){a=a[y]("+")[C](" ");if(ea instanceof Function)try{return ea(a)}catch(b){H(17)}else H(68);return unescape(a)}var Ga=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,!!d):a.attachEvent&&a.attachEvent("on"+b,c)},Ha=function(a,b,c,d){a.removeEventListener?a.removeEventListener(b,c,!!d):a.detachEvent&&a.detachEvent("on"+b,c)}; 4 | function Ia(a,b){if(a){var c=J[qa]("script");c.type="text/javascript";c.async=!0;c.src=a;c.id=b;var d=J.getElementsByTagName("script")[0];d.parentNode.insertBefore(c,d);return c}}function K(a){return a&&0a[y]("/")[0][q](":")&&(a=k+f[2][B](0,f[2].lastIndexOf("/"))+"/"+a):a=k+f[2]+(a||Be);d.href=a;e=c(d);return{protocol:(d[A]||"")[D](),host:e[0], 6 | port:e[1],path:e[2],Oa:d[va]||"",url:a||""}}function Na(a,b){function c(b,c){a.contains(b)||a.set(b,[]);a.get(b)[n](c)}for(var d=Da(b)[y]("&"),e=0;ef?c(d[e],"1"):c(d[e][B](0,f),d[e][B](f+1))}}function Pa(a,b){if(F(a)||"["==a[ma](0)&&"]"==a[ma](a[w]-1))return"-";var c=J.domain;return a[q](c+(b&&"/"!=b?b:""))==(0==a[q]("http://")?7:0==a[q]("https://")?8:0)?"0":a};var Qa=0;function Ra(a,b,c){1<=Qa||1<=100*m.random()||ld()||(a=["utmt=error","utmerr="+a,"utmwv=5.5.0","utmn="+Ea(),"utmsp=1"],b&&a[n]("api="+b),c&&a[n]("msg="+G(c[B](0,100))),M.w&&a[n]("aip=1"),Sa(a[C]("&")),Qa++)};var Ta=0,Ua={};function N(a){return Va("x"+Ta++,a)}function Va(a,b){Ua[a]=!!b;return a} 7 | var Wa=N(),Xa=Va("anonymizeIp"),Ya=N(),$a=N(),ab=N(),bb=N(),O=N(),P=N(),cb=N(),db=N(),eb=N(),fb=N(),gb=N(),hb=N(),ib=N(),jb=N(),kb=N(),lb=N(),nb=N(),ob=N(),pb=N(),qb=N(),rb=N(),sb=N(),tb=N(),ub=N(),vb=N(),wb=N(),xb=N(),yb=N(),zb=N(),Ab=N(),Bb=N(),Cb=N(),Db=N(),Eb=N(),Fb=N(!0),Gb=Va("currencyCode"),Hb=Va("page"),Ib=Va("title"),Jb=N(),Kb=N(),Lb=N(),Mb=N(),Nb=N(),Ob=N(),Pb=N(),Qb=N(),Rb=N(),Q=N(!0),Sb=N(!0),Tb=N(!0),Ub=N(!0),Vb=N(!0),Wb=N(!0),Zb=N(!0),$b=N(!0),ac=N(!0),bc=N(!0),cc=N(!0),R=N(!0),dc=N(!0), 8 | ec=N(!0),fc=N(!0),gc=N(!0),hc=N(!0),ic=N(!0),jc=N(!0),S=N(!0),kc=N(!0),lc=N(!0),mc=N(!0),nc=N(!0),oc=N(!0),pc=N(!0),qc=N(!0),rc=Va("campaignParams"),sc=N(),tc=Va("hitCallback"),uc=N();N();var vc=N(),wc=N(),xc=N(),yc=N(),zc=N(),Ac=N(),Bc=N(),Cc=N(),Dc=N(),Ec=N(),Fc=N(),Gc=N(),Hc=N(),Ic=N();N();var Mc=N(),Nc=N(),Oc=N(),Oe=Va("uaName"),Pe=Va("uaDomain"),Qe=Va("uaPath");var Re=function(){function a(a,c,d){T($[x],a,c,d)}a("_createTracker",$[x].r,55);a("_getTracker",$[x].oa,0);a("_getTrackerByName",$[x].u,51);a("_getTrackers",$[x].pa,130);a("_anonymizeIp",$[x].aa,16);a("_forceSSL",$[x].la,125);a("_getPlugin",Pc,120)},Se=function(){function a(a,c,d){T(U[x],a,c,d)}Qc("_getName",$a,58);Qc("_getAccount",Wa,64);Qc("_visitCode",Q,54);Qc("_getClientInfo",ib,53,1);Qc("_getDetectTitle",lb,56,1);Qc("_getDetectFlash",jb,65,1);Qc("_getLocalGifPath",wb,57);Qc("_getServiceMode", 9 | xb,59);V("_setClientInfo",ib,66,2);V("_setAccount",Wa,3);V("_setNamespace",Ya,48);V("_setAllowLinker",fb,11,2);V("_setDetectFlash",jb,61,2);V("_setDetectTitle",lb,62,2);V("_setLocalGifPath",wb,46,0);V("_setLocalServerMode",xb,92,void 0,0);V("_setRemoteServerMode",xb,63,void 0,1);V("_setLocalRemoteServerMode",xb,47,void 0,2);V("_setSampleRate",vb,45,1);V("_setCampaignTrack",kb,36,2);V("_setAllowAnchor",gb,7,2);V("_setCampNameKey",ob,41);V("_setCampContentKey",tb,38);V("_setCampIdKey",nb,39);V("_setCampMediumKey", 10 | rb,40);V("_setCampNOKey",ub,42);V("_setCampSourceKey",qb,43);V("_setCampTermKey",sb,44);V("_setCampCIdKey",pb,37);V("_setCookiePath",P,9,0);V("_setMaxCustomVariables",yb,0,1);V("_setVisitorCookieTimeout",cb,28,1);V("_setSessionCookieTimeout",db,26,1);V("_setCampaignCookieTimeout",eb,29,1);V("_setReferrerOverride",Jb,49);V("_setSiteSpeedSampleRate",Dc,132);a("_trackPageview",U[x].Fa,1);a("_trackEvent",U[x].F,4);a("_trackPageLoadTime",U[x].Ea,100);a("_trackSocial",U[x].Ga,104);a("_trackTrans",U[x].Ia, 11 | 18);a("_sendXEvent",U[x].t,78);a("_createEventTracker",U[x].ia,74);a("_getVersion",U[x].qa,60);a("_setDomainName",U[x].B,6);a("_setAllowHash",U[x].va,8);a("_getLinkerUrl",U[x].na,52);a("_link",U[x].link,101);a("_linkByPost",U[x].ua,102);a("_setTrans",U[x].za,20);a("_addTrans",U[x].$,21);a("_addItem",U[x].Y,19);a("_clearTrans",U[x].ea,105);a("_setTransactionDelim",U[x].Aa,82);a("_setCustomVar",U[x].wa,10);a("_deleteCustomVar",U[x].ka,35);a("_getVisitorCustomVar",U[x].ra,50);a("_setXKey",U[x].Ca,83); 12 | a("_setXValue",U[x].Da,84);a("_getXKey",U[x].sa,76);a("_getXValue",U[x].ta,77);a("_clearXKey",U[x].fa,72);a("_clearXValue",U[x].ga,73);a("_createXObj",U[x].ja,75);a("_addIgnoredOrganic",U[x].W,15);a("_clearIgnoredOrganic",U[x].ba,97);a("_addIgnoredRef",U[x].X,31);a("_clearIgnoredRef",U[x].ca,32);a("_addOrganic",U[x].Z,14);a("_clearOrganic",U[x].da,70);a("_cookiePathCopy",U[x].ha,30);a("_get",U[x].ma,106);a("_set",U[x].xa,107);a("_addEventListener",U[x].addEventListener,108);a("_removeEventListener", 13 | U[x].removeEventListener,109);a("_addDevId",U[x].V);a("_getPlugin",Pc,122);a("_setPageGroup",U[x].ya,126);a("_trackTiming",U[x].Ha,124);a("_initData",U[x].v,2);a("_setVar",U[x].Ba,22);V("_setSessionTimeout",db,27,3);V("_setCookieTimeout",eb,25,3);V("_setCookiePersistence",cb,24,1);a("_setAutoTrackOutbound",Fa,79);a("_setTrackOutboundSubdomains",Fa,81);a("_setHrefExamineLimit",Fa,80)};function Pc(a){var b=this.plugins_;if(b)return b.get(a)} 14 | var T=function(a,b,c,d){a[b]=function(){try{return void 0!=d&&H(d),c[ya](this,arguments)}catch(a){throw Ra("exc",b,a&&a[r]),a;}}},Qc=function(a,b,c,d){U[x][a]=function(){try{return H(c),Aa(this.a.get(b),d)}catch(e){throw Ra("exc",a,e&&e[r]),e;}}},V=function(a,b,c,d,e){U[x][a]=function(f){try{H(c),void 0==e?this.a.set(b,Aa(f,d)):this.a.set(b,e)}catch(Be){throw Ra("exc",a,Be&&Be[r]),Be;}}},Te=function(a,b){return{type:b,target:a,stopPropagation:function(){throw"aborted";}}};var Rc=new RegExp(/(^|\.)doubleclick\.net$/i),Sc=function(a,b){return Rc[ia](J[z].hostname)?!0:"/"!==b?!1:0!=a[q]("www.google.")&&0!=a[q](".google.")&&0!=a[q]("google.")||-1b[w]||ad(b[0],c))return!1;b=b[ja](1)[C](".")[y]("|");0=b[w])return!0; 19 | b=b[1][y](-1==b[1][q](",")?"^":",");for(c=0;cb[w]||ad(b[0],c))return a.set(ec,void 0),a.set(fc,void 0),a.set(gc,void 0),a.set(ic,void 0),a.set(jc,void 0),a.set(nc,void 0),a.set(oc,void 0),a.set(pc,void 0),a.set(qc,void 0),a.set(S,void 0),a.set(kc,void 0),a.set(lc,void 0),a.set(mc,void 0),!1;a.set(ec,1*b[1]);a.set(fc,1*b[2]);a.set(gc,1*b[3]);Ve(a,b[ja](4)[C]("."));return!0},Ve=function(a,b){function c(a){return(a=b[oa](a+"=(.*?)(?:\\|utm|$)"))&& 21 | 2==a[w]?a[1]:void 0}function d(b,c){c?(c=e?I(c):c[y]("%20")[C](" "),a.set(b,c)):a.set(b,void 0)}-1==b[q]("=")&&(b=I(b));var e="2"==c("utmcvr");d(ic,c("utmcid"));d(jc,c("utmccn"));d(nc,c("utmcsr"));d(oc,c("utmcmd"));d(pc,c("utmctr"));d(qc,c("utmcct"));d(S,c("utmgclid"));d(kc,c("utmgclsrc"));d(lc,c("utmdclid"));d(mc,c("utmdsid"))},ad=function(a,b){return b?a!=b:!/^\d+$/[ia](a)};var Uc=function(){this.filters=[]};Uc[x].add=function(a,b){this.filters[n]({name:a,s:b})};Uc[x].cb=function(a){try{for(var b=0;b=100*a.get(vb)&&a[ta]()}function kd(a){ld(a.get(Wa))&&a[ta]()}function md(a){"file:"==J[z][A]&&a[ta]()}function Ge(a){He()&&a[ta]()}function nd(a){a.get(Ib)||a.set(Ib,J.title,!0);a.get(Hb)||a.set(Hb,J[z].pathname+J[z][va],!0)};var od=new function(){var a=[];this.set=function(b){a[b]=!0};this.Xa=function(){for(var b=[],c=0;c=b[0]||0>=b[1]?"":b[C]("x");a.Wa=d}catch(k){H(135)}qd=a}},td=function(){sd();for(var a= 23 | qd,b=W[za],a=b.appName+b.version+a.language+b.platform+b.userAgent+a.javaEnabled+a.Q+a.P+(J.cookie?J.cookie:"")+(J.referrer?J.referrer:""),b=a[w],c=W.history[w];0d?(this.i=b[B](0,d),this.l=b[B](d+1,c),this.h=b[B](c+1)):(this.i=b[B](0,d),this.h=b[B](d+1));this.k=a[ja](1);this.Ma=!this.l&&"_require"==this.h;this.J=!this.i&&!this.l&&"_provide"==this.h}},Y=function(){T(Y[x],"push",Y[x][n],5);T(Y[x],"_getPlugin",Pc,121);T(Y[x], 25 | "_createAsyncTracker",Y[x].Sa,33);T(Y[x],"_getAsyncTracker",Y[x].Ta,34);this.I=new Ja;this.p=[]};E=Y[x];E.Na=function(a,b,c){var d=this.I.get(a);if(!Ba(d))return!1;b.plugins_=b.plugins_||new Ja;b.plugins_.set(a,new d(b,c||{}));return!0};E.push=function(a){var b=Z.Va[ya](this,arguments),b=Z.p.concat(b);for(Z.p=[];0e?b+"#"+d:b+"&"+d;c="";f=b[q]("?");0f?b+"?"+d+c:b+"&"+d+c},$d=function(a,b,c,d){for(var e=0;3>e;e++){for(var f=0;3>f;f++){if(d==Yc(a+b+c))return H(127),[b,c]; 40 | var Be=b[p](/ /g,"%20"),k=c[p](/ /g,"%20");if(d==Yc(a+Be+k))return H(128),[Be,k];Be=Be[p](/\+/g,"%20");k=k[p](/\+/g,"%20");if(d==Yc(a+Be+k))return H(129),[Be,k];try{var s=b[oa]("utmctr=(.*?)(?:\\|utm|$)");if(s&&2==s[w]&&(Be=b[p](s[1],G(I(s[1]))),d==Yc(a+Be+c)))return H(139),[Be,c]}catch(t){}b=I(b)}c=I(c)}};var de="|",fe=function(a,b,c,d,e,f,Be,k,s){var t=ee(a,b);t||(t={},a.get(Cb)[n](t));t.id_=b;t.affiliation_=c;t.total_=d;t.tax_=e;t.shipping_=f;t.city_=Be;t.state_=k;t.country_=s;t.items_=t.items_||[];return t},ge=function(a,b,c,d,e,f,Be){a=ee(a,b)||fe(a,b,"",0,0,0,"","","");var k;t:{if(a&&a.items_){k=a.items_;for(var s=0;sb[w]||!/^\d+$/[ia](b[0])||(b[0]=""+c,Fd(a,"__utmx",b[C]("."),void 0))},be=function(a,b){var c=$c(a.get(O),pd("__utmx"));"-"==c&&(c="");return b?G(c):c},Ye=function(a){try{var b=La(J[z][xa],!1),c=ea(L(b.d.get("utm_referrer")))||"";c&&a.set(Jb,c);var d=ea(K(b.d.get("utm_expid")))||"";d&&(d=d[y](".")[0],a.set(Oc,""+d))}catch(e){H(146)}},l=function(a){var b=W.gaData&&W.gaData.expId;b&&a.set(Oc,""+b)};var ke=function(a,b){var c=m.min(a.b(Dc,0),100);if(a.b(Q,0)%100>=c)return!1;c=Ze()||$e();if(void 0==c)return!1;var d=c[0];if(void 0==d||d==ba||da(d))return!1;0a[b])return!1;return!0},le=function(a){return da(a)|| 42 | 0>a?0:5E3>a?10*m[la](a/10):5E4>a?100*m[la](a/100):41E5>a?1E3*m[la](a/1E3):41E5},je=function(a){for(var b=new yd,c=0;cc[w])){for(var d=[],e=0;e=f)return!1;c=1*(""+c);if(""==a||!wd(a)||""==b||!wd(b)||!xd(c)||da(c)||0>c||0>f||100=a||a>e.get(yb))a=!1;else if(!b||!c||128=a&&Ca(b)&&""!=b){var c=this.get(Fc)||[];c[a]=b;this.set(Fc,c)}};E.V=function(a){a=""+a;if(a[oa](/^[A-Za-z0-9]{1,5}$/)){var b=this.get(Ic)||[];b[n](a);this.set(Ic,b)}};E.v=function(){this.a[ka]()};E.Ba=function(a){a&&""!=a&&(this.set(Tb,a),this.a.j("var"))};var ne=function(a){"trans"!==a.get(sc)&&500<=a.b(cc,0)&&a[ta]();if("event"===a.get(sc)){var b=(new Date)[g](),c=a.b(dc,0),d=a.b(Zb,0),c=m[la]((b-(c!=d?c:1E3*c))/1E3*1);0=a.b(R,0)&&a[ta]()}},pe=function(a){"event"===a.get(sc)&&a.set(R,m.max(0,a.b(R,10)-1))};var qe=function(){var a=[];this.add=function(b,c,d){d&&(c=G(""+c));a[n](b+"="+c)};this.toString=function(){return a[C]("&")}},re=function(a,b){(b||2!=a.get(xb))&&a.Za(cc)},se=function(a,b){b.add("utmwv","5.5.0");b.add("utms",a.get(cc));b.add("utmn",Ea());var c=J[z].hostname;F(c)||b.add("utmhn",c,!0);c=a.get(vb);100!=c&&b.add("utmsp",c,!0)},te=function(a,b){b.add("utmht",(new Date)[g]());b.add("utmac",Da(a.get(Wa)));a.get(Oc)&&b.add("utmxkey",a.get(Oc),!0);a.get(vc)&&b.add("utmni",1);var c=a.get(Ic); 56 | c&&0=a[w])gf(a,b,c);else if(8192>=a[w]){if(0<=W[za].userAgent[q]("Firefox")&&![].reduce)throw new De(a[w]);df(a,b)||ef(a,b)||Ee(a,b)}else throw new Ce(a[w]);},gf=function(a,b,c){c=c||Ne()+"/__utm.gif?";var d=new Image(1,1);d.src=c+a;Ie(d, 63 | function(){Ie(d,null);Je(d,null);b()});Je(d,function(){Ie(d,null);Je(d,null);b()})},ef=function(a,b){var c;c=W.XDomainRequest;if(!c)return!1;c=new c;c.open("POST",Ne()+"/p/__utm.gif");Je(c,function(){b()});Ie(c,b);c.send(a);return!0},df=function(a,b){var c=W.XMLHttpRequest;if(!c)return!1;var d=new c;if(!("withCredentials"in d))return!1;d.open("POST",Ne()+"/p/__utm.gif",!0);d.withCredentials=!0;d.setRequestHeader("Content-Type","text/plain");d.onreadystatechange=function(){4==d.readyState&&(b(),d= 64 | null)};d.send(a);return!0},Ee=function(a,b){if(J.body){a=aa(a);try{var c=J[qa]('')}catch(d){c=J[qa]("iframe"),ha(c,a)}c.height="0";c.width="0";c.style.display="none";c.style.visibility="hidden";var e=J[z],e=Ne()+"/u/post_iframe.html#"+aa(e[A]+"//"+e[u]+"/favicon.ico"),f=function(){c.src="";c.parentNode&&c.parentNode.removeChild(c)};Ga(W,"beforeunload",f);var Be=!1,k=0,s=function(){if(!Be){try{if(9>21:b;return b};})(); 67 | --------------------------------------------------------------------------------