iamstarkov / topics-manager

Serverless App to manage GitHub repos' topics
https://topics-manager.now.sh
6 stars 0 forks source link

xsrf defense with oauth state param #56

Closed iamstarkov closed 5 years ago

iamstarkov commented 5 years ago

see https://security.stackexchange.com/questions/20187/oauth2-cross-site-request-forgery-and-state-parameter

iamstarkov commented 5 years ago

also http://www.bubblecode.net/en/2016/01/22/understanding-oauth2/#Vulnerability_in_Authorization_Code_Grant

iamstarkov commented 5 years ago

see more about "hash vs checksum" https://security.stackexchange.com/questions/194600/checksum-vs-hash-differences-and-similarities/194602#194602

iamstarkov commented 5 years ago

also about cryptographic checksums as in "data integrity" https://www.securitydrops.com/data-integrity/

iamstarkov commented 5 years ago

its not secure enough to use general hash or checksum function, its better to generate signed checksum where signature is known only to a oauth client

iamstarkov commented 5 years ago

maybe HMAC is an overkill. it should be enough to use cryptographic hash function over session and some secret

iamstarkov commented 5 years ago
crypto.createHash('sha256').update(some).digest('hex')
iamstarkov commented 5 years ago

also maybe take user-agent or some user-relevant bits of data into consideration

iamstarkov commented 5 years ago

uuid for session perhaps https://en.wikipedia.org/wiki/Universally_unique_identifier

iamstarkov commented 5 years ago

nay, HMACs are easy and don't require set of public && private keys

crypto.createHmac('sha256', 'secret').update('to be signed').digest('hex')
iamstarkov commented 5 years ago

another great article on oauth2 vulnerabilities https://dhavalkapil.com/blogs/Attacking-the-OAuth-Protocol/

iamstarkov commented 5 years ago

for the oauth2 "authorization" server part http://blog.intothesymmetry.com/2015/04/open-redirect-in-rfc6749-aka-oauth-20.html

iamstarkov commented 5 years ago

like what I was saying https://security.stackexchange.com/a/138923/26493:

When deciding how to implement this, one suggestion is to use a private key together with some easily verifiable variables, for example, client ID and a session cookie, to compute a hashed value. This will result in a byte value that will be infeasibility difficult to guess without the private key. After computing such an HMAC, base-64 encode it and pass it to the OAuth server as state parameter. Another suggestion is to hash the current date and time. This requires your application to save the time of transmission in order to verify it or to allow a sliding period of validity (e.g., using TOTP).

iamstarkov commented 5 years ago

also maybe take user-agent or some user-relevant bits of data into consideration

user agent isnt enough. it probably should be full user agent fingerprint

iamstarkov commented 5 years ago

for fingerprinting we can't trust client user agents. but we can use server side information such as: useragent, acceptHeaders, geoip. (reference https://github.com/yusukeshibata/express-fingerprint)

iamstarkov commented 5 years ago

offtopic uuid/guid https://stackoverflow.com/q/105034/1057730

iamstarkov commented 5 years ago

fingerprinting geoip:

both include async and it is responsible for

another difference description:

geoip-lite is fairly light already, boasting 6 microsecond lookups for IPv4 addresses, and 30 microsecond lookups for IPv6 on a Macbook Pro. However, it ships over 60MB of data for handling city/region names, and thus introduces significant memory overhead. If all you want is country data, and not city/regions, then that's a lot of RAM for unused functionality.

Instead, geoip-ultralight includes under 2MB of data, and has negligible memory consumption. If all you need is to identify countries, this will work perfectly with your Digital Ocean or AWS micro instances. —geoip-ultralite's REDME "why" section

iamstarkov commented 5 years ago

geoip-lite deployed on Now v1:

iamstarkov commented 5 years ago

alright, geoip is complicated, lets postpone it until this hits the now v2 https://spectrum.chat/zeit/now/geoip-information~ac41b4b6-4db3-40d3-a79f-a4435448be46?m=MTU1NjgzNjYwNzQ3MQ==

iamstarkov commented 5 years ago

or until there are even lighter (ram/size/cold start) alternatives

iamstarkov commented 5 years ago

actually there is already simple, fast and serverless ready geoip alternative:

var geolite2 = require('geolite2');
var maxmind = require('maxmind');

var ip = "80.212.72.183";

Promise.all([
  geolite2.paths.asn,
  geolite2.paths.country,
  geolite2.paths.city
].map(x => maxmind.open(x)))
.then(xs => (ip) => xs.map(x => x.get(ip)))
.then(geoip => geoip(ip))
.then(x => JSON.stringify(x, null, 2))
.then(console.log, console.error)
/*
[
  {
    "autonomous_system_number": 2119,
    "autonomous_system_organization": "Telenor Norge AS"
  },
  {
    "continent": {
      "code": "EU",
      "geoname_id": 6255148,
      "names": {
        "de": "Europa",
        "en": "Europe",
        "es": "Europa",
        "fr": "Europe",
        "ja": "ヨーロッパ",
        "pt-BR": "Europa",
        "ru": "Европа",
        "zh-CN": "欧洲"
      }
    },
    "country": {
      "geoname_id": 3144096,
      "iso_code": "NO",
      "names": {
        "de": "Norwegen",
        "en": "Norway",
        "es": "Noruega",
        "fr": "Norvège",
        "ja": "ノルウェー王国",
        "pt-BR": "Noruega",
        "ru": "Норвегия",
        "zh-CN": "挪威"
      }
    },
    "registered_country": {
      "geoname_id": 3144096,
      "iso_code": "NO",
      "names": {
        "de": "Norwegen",
        "en": "Norway",
        "es": "Noruega",
        "fr": "Norvège",
        "ja": "ノルウェー王国",
        "pt-BR": "Noruega",
        "ru": "Норвегия",
        "zh-CN": "挪威"
      }
    }
  },
  {
    "city": {
      "geoname_id": 3162306,
      "names": {
        "en": "Austmarka"
      }
    },
    "continent": {
      "code": "EU",
      "geoname_id": 6255148,
      "names": {
        "de": "Europa",
        "en": "Europe",
        "es": "Europa",
        "fr": "Europe",
        "ja": "ヨーロッパ",
        "pt-BR": "Europa",
        "ru": "Европа",
        "zh-CN": "欧洲"
      }
    },
    "country": {
      "geoname_id": 3144096,
      "iso_code": "NO",
      "names": {
        "de": "Norwegen",
        "en": "Norway",
        "es": "Noruega",
        "fr": "Norvège",
        "ja": "ノルウェー王国",
        "pt-BR": "Noruega",
        "ru": "Норвегия",
        "zh-CN": "挪威"
      }
    },
    "location": {
      "accuracy_radius": 20,
      "latitude": 60.1003,
      "longitude": 12.3213,
      "time_zone": "Europe/Oslo"
    },
    "postal": {
      "code": "2224"
    },
    "registered_country": {
      "geoname_id": 3144096,
      "iso_code": "NO",
      "names": {
        "de": "Norwegen",
        "en": "Norway",
        "es": "Noruega",
        "fr": "Norvège",
        "ja": "ノルウェー王国",
        "pt-BR": "Noruega",
        "ru": "Норвегия",
        "zh-CN": "挪威"
      }
    },
    "subdivisions": [
      {
        "geoname_id": 3153403,
        "iso_code": "04",
        "names": {
          "de": "Hedmark",
          "en": "Hedmark",
          "fr": "Hedmark"
        }
      }
    ]
  }
]
*/
iamstarkov commented 5 years ago

but good news, you dont need to geoip to fingerprint, because geoip is derivative from ip and thus ip itself as good as geoip data for fingerprinting

iamstarkov commented 5 years ago

basically xsrf check can be streamlined to two checks: fingerprint and session checksums