:key: Keyscope
Keyscope is a key and secret workflow (validation, invalidation, etc.) tool built in Rust, powered by service_policy_kit.
Current workflows supported:
- Validation
🦀 Why Rust?
- With Rust, "If it compiles, it works." and also, it compiles to many platforms.
- Rust is fast, has no VM, or unnecessary cruft (typically 5-8mb binaries with LOTS of code and libraries).
- Multi purpose, safe, and generalistic - makes for healthy and expressive mission critical code. Adding code or abstraction doesn't increase bloat, doesn't hurt performance, doesn't increase chance for bugs in a radical way (less edge cases).
- Amazing package manager:
Cargo. Productive installing and running of tasks and examples. - Rust is getting headlines in the security community as the go-to language for security tools. Equally interesting is offensive security + Rust here and here.
:rocket: Quick Start
Grab a release from releases, or install via Homebrew:
brew tap spectralops/tap && brew install keyscope
Using keyscope
You can try out validating a key for a provider, say, Github (assuming the key is in the GITHUB_TOKEN environment variable):
$ keyscope validate github $GITHUB_TOKEN
You can see which other providers are supported by running:
$ keyscope validate --list
.
:
.
twilio:validation
keyscope validate twilio -p twilio_1 -p twilio_2
twitter:validation
keyscope validate twitter -p twitter_1
zendesk:validation
keyscope validate zendesk -p zendesk_1 -p zendesk_2
Total 44 providers available.
$
And what parameters are required for a certain provider by running (say, stripe):
$ keyscope requirements stripe
provider stripe requires:
- param: p1
desc: stripe key
$
Finally the general structure of the validate command is:
$ keyscope validate PROVIDER -p PARAM1 -p PARAM2 .. -p PARAM_N
:white_check_mark: Validation: key should be active
You can validate a specific provider like so:
$ keyscope validate twilio -p $TWILIO_KEY
With the general pattern of:
$ keyscope validate PROVIDER -p PARAM1 -p PARAM2 ...
The number of keys/params would change based on authentication type:
Bearer- usually just a single key (token)Basic Auth- usually 2 keys: user, passwordOAuth- usually 2 keys: client_id, client_secret- And others.
Each provider in Keyscope will tell you what it requires using requirements:
$ keyscope requirements twilio
You'll get a report:
$ keyscope --verbose validate stripe -p $STRIPE_KEY
✔ stripe:validation: ok 766ms
Ran 1 interactions with 1 checks in 766ms
Success: 1
Failure: 0
Error: 0
Skipped: 0
And an executable exit code that reflects success/failure.
You can use the --verbose flag to see API responses:
$ keyscope --verbose validate stripe -p $STRIPE_KEY
✗ stripe:validation: failed 413ms
status_code: 200 != 401 Unauthorized
Ran 1 interactions with 1 checks in 413ms
Success: 0
Failure: 1
Error: 0
Skipped: 0
In this case the exit code is 1 (failure).
:x: Validation: key should be inactive
When you are validating keys that are supposed to be inactive, you can use the flip flag. In this mode, a failed API access is a good thing, and the exit code will reflect that.
$ keyscope --flip validate stripe -p $STRIPE_KEY
✔ stripe:validation: ok 766ms
Ran 1 interactions with 1 checks in 766ms
In this case, the key is active - which is bad for us. Using --flip, the exit code will be 1 (failure).
:runner: Setting up a validation job
Audit active keys
You can set up a CI job (or other form of scheduled job you like) to perform an audit, by reading all parameters from a dedicated CSV file like so:
$ keyscope validate --csv-in report.csv
The format of the CSV that you need to prepare should include a header line and look like this:
provider,key1,key2,key3
twilio,tw-key1,,,
You can specify as many key columns as you like, as long as you provide an empty value for providers which don't have that many keys, and all rows contain the same amount of cells.
Audit inactive keys
If you have a dump of keys from your vault that are stale have expiry and should have been rotated, you want to test that they are all stale:
$ keyscope --flip validate --csv-in my-key-audit.csv
:link: Supported providers
We're always adding new providers, keep a link to this list or watch this repo to get updated.
We use our service_policy_kit library to specify interactions with services and their policies, if you find a service not in this list feel free to open an issue or contribute back.
testerTester: valid key
validation
tester_1 - hookbin ID (https://hookb.in)tester_2 - fake key to put as a query param
keyscope validate tester -p TESTER_1 -p TESTER_2
infurainfura API key
validation
infura_1 - infura API key
keyscope validate infura -p INFURA_1
covalenthqCovalent: valid key
validation
covalenthq_1 - covalent token
keyscope validate covalenthq -p COVALENTHQ_1
asanaAsana: valid token
validation
asana_1 - asana token
keyscope validate asana -p ASANA_1
bitlyBit.ly: valid access token
validation
bitly_1 - bit.ly token
keyscope validate bitly -p BITLY_1
ipstackipstack access key
validation
ipstack_1 - ipstack access key
keyscope validate ipstack -p IPSTACK_1
localyticsLocalytics: valid API credentials
validation
localytics_1 - localytics userlocalytics_2 - localytics key
keyscope validate localytics -p LOCALYTICS_1 -p LOCALYTICS_2
algoliaAlgolia: valid API credentials
validation
algolia_1 - algolia application IDalgolia_2 - algolia indexalgolia_3 - algolia API key
keyscope validate algolia -p ALGOLIA_1 -p ALGOLIA_2 -p ALGOLIA_3
branchiobranch.io: valid API credentials
validation
branchio_1 - branch.io keybranchio_2 - branch.io secret
keyscope validate branchio -p BRANCHIO_1 -p BRANCHIO_2
browserstackbrowserstack: valid API credentials
validation
browserstack_1 - browserstack keybrowserstack_2 - browserstack secret
keyscope validate browserstack -p BROWSERSTACK_1 -p BROWSERSTACK_2
buildkiteBuildkite: valid token
validation
buildkite_1 - buildkite token
keyscope validate buildkite -p BUILDKITE_1
datadogdatadog: valid API credentials
validation
datadog_1 - datadog API key
keyscope validate datadog -p DATADOG_1
githubgithub: valid API credentials
validation
github_1 - github token
keyscope validate github -p GITHUB_1
github-entGithub Enterprise: valid API token
validation
github-ent_1 - github enterprise instance (without http)github-ent_2 - github token
keyscope validate github-ent -p GITHUB-ENT_1 -p GITHUB-ENT_2
dropboxdropbox: valid API credentials
validation
dropbox_1 - dropbox token
keyscope validate dropbox -p DROPBOX_1
gitlabgitlab: valid API credentials
validation
gitlab_1 - gitlab token
keyscope validate gitlab -p GITLAB_1
herokuheroku: valid API credentials
validation
heroku_1 - heroku token
keyscope validate heroku -p HEROKU_1
mailchimpmailchimp: valid API credentials
validation
mailchimp_1 - mailchimp datacenter IDmailchimp_2 - mailchimp key
keyscope validate mailchimp -p MAILCHIMP_1 -p MAILCHIMP_2
mailgunmailgun: valid API credentials
validation
mailgun_1 - mailgun key
keyscope validate mailgun -p MAILGUN_1
pagerdutypagerduty: valid API credentials
validation
pagerduty_1 - pagerduty token
keyscope validate pagerduty -p PAGERDUTY_1
circlecicircleci: valid API credentials
validation
circleci_1 - circleci key
keyscope validate circleci -p CIRCLECI_1
facebook-access-tokenfacebook: valid API token
validation
facebook-access-token_1 - facebook token
keyscope validate facebook-access-token -p FACEBOOK-ACCESS-TOKEN_1
salesforcesalesforce: valid API credentials
validation
salesforce_1 - salesforce instance namesalesforce_2 - salesforce token
keyscope validate salesforce -p SALESFORCE_1 -p SALESFORCE_2
jumpcloudjumpcloud: valid API credentials
validation
jumpcloud_1 - jumpcloud key
keyscope validate jumpcloud -p JUMPCLOUD_1
saucelabs-ussaucelabs-us: valid API credentials
validation
saucelabs-us_1 - saucelabs usersaucelabs-us_2 - saucelabs key
keyscope validate saucelabs-us -p SAUCELABS-US_1 -p SAUCELABS-US_2
saucelabs-eusaucelabs-eu: valid API credentials
validation
saucelabs-eu_1 - saucelabs usersaucelabs-eu_2 - saucelabs key
keyscope validate saucelabs-eu -p SAUCELABS-EU_1 -p SAUCELABS-EU_2
sendgridsendgrid: valid API credentials
validation
sendgrid_1 - sendgrid key
keyscope validate sendgrid -p SENDGRID_1
slackslack: valid API credentials
validation
slack_1 - slack key
keyscope validate slack -p SLACK_1
slack-webhookslack-webook: valid API credentials
validation
slack-webhook_1 - slack webhook
keyscope validate slack-webhook -p SLACK-WEBHOOK_1
stripestripe: valid API credentials
validation
stripe_1 - stripe key
keyscope validate stripe -p STRIPE_1
traviscitravisci: valid API credentials
validation
travisci_1 - travisci domain, choose 'org' or 'com'travisci_2 - travisci key
keyscope validate travisci -p TRAVISCI_1 -p TRAVISCI_2
twiliotwilio: valid API credentials
validation
twilio_1 - twilio account sidtwilio_2 - twilio token
keyscope validate twilio -p TWILIO_1 -p TWILIO_2
twittertwitter: valid API credentials
validation
twitter_1 - twitter API token
keyscope validate twitter -p TWITTER_1
zendeskzendesk: valid API credentials
validation
zendesk_1 - zendesk domainzendesk_2 - zendesk key
keyscope validate zendesk -p ZENDESK_1 -p ZENDESK_2
firebasefirebase: valid API credentials
validation
firebase_1 - firebase API keyfirebase_2 - firebase ID token
keyscope validate firebase -p FIREBASE_1 -p FIREBASE_2
awsaws: valid API credentials
validation
aws_1 - AWS IDaws_2 - AWS secret
keyscope validate aws -p AWS_1 -p AWS_2
elastic-apm-secretElastic APM: secret key validation
validation
elastic-apm-secret_1 - Elastic APM host address and port, including 'http/s' partelastic-apm-secret_2 - Elastic APM secret
keyscope validate elastic-apm-secret -p ELASTIC-APM-SECRET_1 -p ELASTIC-APM-SECRET_2
artifactoryArtifactory: token validation
validation
artifactory_1 - Artifactory host (including http(s) part)artifactory_2 - Artifactory token
keyscope validate artifactory -p ARTIFACTORY_1 -p ARTIFACTORY_2
ibm-cosIBM: cloud object storage key validation (HMAC)
validation
ibm-cos_1 - IBM HMAC IDibm-cos_2 - IBM HMAC secret
keyscope validate ibm-cos -p IBM-COS_1 -p IBM-COS_2
ibm-iamIBM: cloud key validation (IAM)
validation
ibm-iam_1 - IBM cloud key
keyscope validate ibm-iam -p IBM-IAM_1
ibm-cloudantIBM: cloudant key validation (legacy)
validation
ibm-cloudant_1 - IBM cloudant hostnameibm-cloudant_2 - IBM cloudant useribm-cloudant_3 - IBM cloudant key
keyscope validate ibm-cloudant -p IBM-CLOUDANT_1 -p IBM-CLOUDANT_2 -p IBM-CLOUDANT_3
softlayerSoftlayer: validate credentials
validation
softlayer_1 - Softlayer hostnamesoftlayer_2 - Softlayer token
keyscope validate softlayer -p SOFTLAYER_1 -p SOFTLAYER_2
squareSquare: valid token
validation
square_1 - Square token
keyscope validate square -p SQUARE_1
telegram-bottelegram-bot: valid bot token
validation
telegram-bot_1 - bot key
keyscope validate telegram-bot -p TELEGRAM-BOT_1
bingmapsBing Maps API: valid access token
validation
bingmaps_1 - Bing Maps token
keyscope validate bingmaps -p BINGMAPS_1
buttercmsButterCMS: valid bot token
validation
buttercms_1 - ButterCMS API key
keyscope validate buttercms -p BUTTERCMS_1
wakatimewakatime: valid api token
validation
wakatime_1 - WakeTime API key
keyscope validate wakatime -p WAKATIME_1
calendlycalendly: valid API credentials
validation
calendly_1 - calendly API key
keyscope validate calendly -p CALENDLY_1
shodanshodan: valid api token
validation
shodan_1 - Shodan API key
keyscope validate shodan -p SHODAN_1
opsgenieopsgenie: valid api token
validation
opsgenie_1 - opsgenie API key
keyscope validate opsgenie -p OPSGENIE_1
pendopendo: valid api token
validation
pendo_1 - pendo API key
keyscope validate pendo -p PENDO_1
hubspothubspot: valid api token
validation
hubspot_1 - hubspot API key
keyscope validate hubspot -p HUBSPOT_1
lokaliselokalise: valid api token
validation
lokalise_1 - lokalise token
keyscope validate lokalise -p LOKALISE_1
:cake: Adding your own providers
You can specify a custom definitions file (here is an example):
$ keyscope -f your-definitions.yaml validate --list
Which is suitable for adding your own internal services, key issuing policies, and infrastructure.
You can also use custom definitions to test out new providers that you'd like to contribute back to keyscope :smile:
Basics of a definition
All definitions represent an interaction. A request being made, and a policy that's being checked against it.
providers:
hookbin:
validation:
#
# the request part
#
request:
params:
- name: hookbin_1
desc: hookbin ID (https://hookb.in)
- name: hookbin_2
desc: fake key to put as a query param
id: "postbin:validation"
desc: "Postbin: valid key"
# variable interpolation is good for all fields
uri: "https://hookb.in/{{hookbin_1}}?q={{hookbin_2}}"
method: post
# you can also use headers, body, form, basic_auth and more (see defs.yaml)
#
# the policy part: all values are actually regular expressions and are matched against service response
#
response:
status_code: "200"
body: ok
When in doubt, you can check keyscope's own defs.yaml for real examples of how to use this infrastructure.
Tutorial: adding Dropbox
To validate if a dropbox API key works, we first need to learn about the canonical way to authenticate against that API.
First stop, API docs:
- Dropbox has an API Explorer which is super useful
Next stop, we want to find an API call that is a representative for:
- Has to be authenticated
- Has to indicate that when accessed successfully with our candidate key, the key has some authoritative value. Which means, that if exposed, contains significant risk.
For this example, getting our current account sounds like something that only when we identify who we are - we're able to do.
We'll select get_current_account.
Let's start forming our interaction. First the needed skeleton: containing the name of the provider (dropbox), its ID and description below, as well as parameters required and their name and description:
dropbox:
validation:
request:
id: "dropbox:validation"
desc: "dropbox: valid API credentials"
params:
- name: dropbox_1
desc: dropbox token
We keep the name of the parameter with a special convention that helps when feeding keyscope automatically:
PROVIDER_N
Where 'N' starts in 1 e.g.:
dropbox_1
dropbox_2
aws_1
...
Then, details about actually making an HTTP call, as required by Dropbox (Bearer token authentication).
uri: https://api.dropboxapi.com/2/users/get_current_account
method: post
headers:
Authorization:
- Bearer {{dropbox_1}}
Note that per standard, all HTTP header fields are actually arrays. It's OK to always make an array of size one if you only have one value (most common case).
We also see variable interpolation here. Where {{dropbox_1}} will get replaced by keyscope in time before making the actual call.
Finally, we want to make sure we answer the question:
- What does it mean to have a successful call?
In our case, the Dropbox API call returns HTTP OK on success, which means a 200 status code.
And the final, complete result is this:
dropbox:
validation:
request:
id: "dropbox:validation"
desc: "dropbox: valid API credentials"
params:
- name: dropbox_1
desc: dropbox token
uri: https://api.dropboxapi.com/2/users/get_current_account
method: post
headers:
Authorization:
- Bearer {{dropbox_1}}
response:
status_code: "200"
Meanwhile, you can drop this provider in your own providers.yaml file and run keyscope:
$ keyscope -f providers.yaml validate dropbox -p MY_KEY
Now you can keep this in your private providers.yaml file or contribute it back to keyscope if you think other people might enjoy using it - we're happy to accept pull requests.
Thanks
To all Contributors - you make this happen, thanks!
Copyright
Copyright (c) 2021 @jondot. See LICENSE for further details.