├── .gitignore ├── .npmrc ├── .travis.yml ├── LICENSE ├── README.md ├── browser.html ├── cli.js ├── index.js ├── package.json ├── screenshot.png ├── test.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5" 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dheeraj Joshi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # passw0rd [![travis][travis-image]][travis-url] [![npm][npm-image]][npm-url] [![XO code style][xo-image]][xo-url] 2 | 3 | [travis-image]: https://img.shields.io/travis/djadmin/passw0rd/master.svg 4 | [travis-url]: https://travis-ci.org/djadmin/passw0rd 5 | [npm-image]: https://img.shields.io/npm/v/passw0rd.svg 6 | [npm-url]: https://npmjs.org/package/passw0rd 7 | [xo-image]: https://img.shields.io/badge/code_style-XO-5ed9c7.svg 8 | [xo-url]: https://github.com/xojs/xo 9 | 10 | 🔑 securely checks a password to see if it has been previously exposed in a data breach 11 | 12 | ## CLI 💻 13 | 14 | * Keeps your password hidden 15 | * Clears your clipboard automatically 16 | 17 | ### Installation 🚀 18 | 19 | Ensure you have Node.js version 5 or higher installed. Then run the following: 20 | 21 | ```$ npm install --global passw0rd``` 22 | 23 | ### Checking your password 🔍 24 | 25 | ```$ passw0rd``` 26 | 27 | ![CLI](./screenshot.png) 28 | 29 | ## API 📝 30 | 31 | ### Installation 32 | 33 | ```$ npm install passw0rd``` 34 | 35 | ### Usage 36 | 37 | ```js 38 | const passw0rd = require('passw0rd'); 39 | 40 | passw0rd.check('passw0rd').then(res => { 41 | console.log(`Password was found ${res.count} times`); 42 | }); 43 | ``` 44 | 45 | **Browser** 46 | 47 | Run the following command to get [UMD](https://github.com/umdjs/umd) version of the library under the `dist` folder 48 | 49 | `$ npm run build` 50 | 51 | ```html 52 | 53 | ``` 54 | You can find the library on `window.passw0rd`. A very simple POC is available at [browser.html](./browser.html) 55 | 56 | ## How it works ⚙ 57 | 58 | [Pwned Passwords](https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/) has implemented a k-Anonymity model that allows a password to be searched for by partial hash. This allows the first 5 characters of a SHA-1 password hash (not case-sensitive) to be passed to the API. 59 | 60 | `GET https://api.pwnedpasswords.com/range/{first 5 hash chars}` 61 | 62 | **passw0rd** is using [Pwned Passwords API](https://haveibeenpwned.com/API/v2#PwnedPasswords) which searches through a database of more than 500 million passwords collected from various breaches. 63 | 64 | ## Todo 65 | 66 | * Add CLI Help Menu 67 | * Add Icon / GIF 68 | * Add babel 69 | * Reduce bundle size using webpack 70 | * Write unit test cases 71 | * Improve performance for browser api 72 | * Improve browser POC 73 | * Move cli/lib to a different repo 74 | * Add security checks 75 | * Add to node-awesomejs 76 | 77 | ## FAQ - Why is it named passw0rd? 💫 78 | 79 | > `passw0rd` is one of the most commonly used passwords and has been found 200297 times in various data breaches! 80 | 81 | ## See Also 82 | 83 | * Active Directory - [Checking for Breached Passwords in Active Directory](https://jacksonvd.com/checking-for-breached-passwords-ad-using-k-anonymity/) 84 | * 1Password - [Check your 1password exported passwords](https://github.com/eblin/1passpwnedcheck) 85 | 86 | ## License 87 | 88 | MIT © [Dheeraj Joshi](https://djadmin.in) 89 | -------------------------------------------------------------------------------- /browser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | passw0rd.js demo 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 25 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const Prompt = require('prompt-password'); 5 | const clipboardy = require('clipboardy'); 6 | const cowsay = require('cowsay'); 7 | const chalk = require('chalk'); 8 | const logSymbols = require('log-symbols'); 9 | const boxen = require('boxen'); 10 | const passw0rd = require('.'); 11 | 12 | const prompt = new Prompt({ 13 | type: 'password', 14 | message: 'Enter your password please', 15 | name: 'pass' 16 | }); 17 | 18 | let isCopied = false; // For clipboard checking 19 | 20 | /** 21 | * Check clipboard if the given password was copied 22 | * @param {string} pass - The password to check 23 | */ 24 | const checkClipboard = pass => { 25 | return clipboardy.readSync() === pass; 26 | }; 27 | 28 | /** 29 | * Clear clipboard util 30 | * @param {string} res - Result from the pwned password 31 | */ 32 | const clearClipboard = res => { 33 | if (!isCopied) { 34 | return; 35 | } 36 | console.log(`\n${logSymbols.info} clearing clipboard...`); 37 | // Appreciate :) 38 | clipboardy.writeSync(res.pwned ? '' : '😇 Thanks for using a strong password'); 39 | }; 40 | 41 | const showWarning = () => { 42 | const msg = `This password should ${chalk.red('never')} be used.`; 43 | // Use a password manager to have strong, unique passwords for each app 44 | const opt = { 45 | padding: 1, 46 | margin: 0, 47 | align: 'center', 48 | borderColor: 'yellow', 49 | borderStyle: 'round' 50 | }; 51 | console.log(boxen(msg, opt)); 52 | }; 53 | 54 | /** 55 | * Display information for the given password 56 | * @param {Object} res - results obtained from passw0rd check 57 | */ 58 | const showResult = res => { 59 | const messages = { 60 | pwned: `${logSymbols.error} Pwned ${chalk.yellow(res.count)} times!`, 61 | safe: `${logSymbols.success} Yay! No records found!` 62 | }; 63 | console.log(); 64 | if (res.pwned) { 65 | console.log(cowsay.think({text: messages.pwned, e: 'oO'})); 66 | showWarning(); 67 | } else { 68 | console.log(cowsay.say({text: messages.safe, f: 'tux'})); 69 | } 70 | return res; 71 | }; 72 | 73 | /** 74 | * Check given password 75 | * @param {string} pass - user provided password 76 | */ 77 | const checkPass = pass => { 78 | if (!pass) { 79 | console.error(`${logSymbols.warning} Specify a password`); 80 | process.exit(1); 81 | } 82 | isCopied = checkClipboard(pass); 83 | return passw0rd.check(pass); 84 | }; 85 | 86 | prompt.run() 87 | .then(checkPass) 88 | .then(showResult) 89 | .then(clearClipboard); 90 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const sha1 = require('js-sha1'); 3 | const fetch = require('node-fetch'); 4 | 5 | /** 6 | * Search for a hash in a list of hashes separately 7 | * @param {string} body - A list of partially matches hashed 8 | * @param {string} hash - A hash to be matched 9 | * @returns {Object} returns { boolean, count } 10 | */ 11 | const search = (body, hash) => { 12 | const result = {pwned: false, count: 0}; 13 | 14 | // Every password hash is followed by a colon (:) and the password count 15 | const pattern = new RegExp(hash + ':(\\d+)'); 16 | const match = body.match(pattern); 17 | if (match) { 18 | result.pwned = true; 19 | result.count = match[1]; 20 | } 21 | return result; 22 | }; 23 | 24 | /** 25 | * Check if a password has appeared in any data breach 26 | * using hibp (https://haveibeenpwned.com/Passwords) API 27 | * @param {string} pass - The password to check 28 | * @returns {Promise} returns a promise with the result 29 | */ 30 | const checkPassword = pass => { 31 | const hash = sha1(pass).toUpperCase(); 32 | const hashPrefix = hash.substring(0, 5); 33 | const hashSuffix = hash.substring(5); 34 | 35 | return fetch('https://api.pwnedpasswords.com/range/' + hashPrefix) 36 | .then(res => res.text()) 37 | .then(body => search(body, hashSuffix)) 38 | .catch(error => { 39 | console.log(error); 40 | }); 41 | }; 42 | 43 | module.exports = { 44 | check: checkPassword 45 | }; 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passw0rd", 3 | "version": "1.0.2", 4 | "description": "checks your password against a list of compromised credentials", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "xo", 8 | "build": "webpack -p", 9 | "watch": "webpack --watch", 10 | "precommit": "npm test" 11 | }, 12 | "keywords": [ 13 | "pwned", 14 | "password", 15 | "checker", 16 | "hibp", 17 | "breach", 18 | "security" 19 | ], 20 | "author": { 21 | "name": "Dheeraj Joshi", 22 | "email": "dheerajjoshi1991@gmail.com", 23 | "url": "https://djadmin.in" 24 | }, 25 | "license": "MIT", 26 | "devDependencies": { 27 | "husky": "^0.14.3", 28 | "uglifyjs-webpack-plugin": "^1.2.4", 29 | "webpack": "^4.0.1", 30 | "webpack-cli": "^2.0.10", 31 | "xo": "^0.20.3" 32 | }, 33 | "dependencies": { 34 | "boxen": "^1.3.0", 35 | "chalk": "^2.3.2", 36 | "clipboardy": "^1.2.3", 37 | "cowsay": "^1.2.1", 38 | "js-sha1": "^0.6.0", 39 | "log-symbols": "^2.2.0", 40 | "node-fetch": "^2.1.1", 41 | "prompt-password": "^1.2.0" 42 | }, 43 | "bin": { 44 | "passw0rd": "cli.js" 45 | }, 46 | "repository": "github:djadmin/passw0rd" 47 | } 48 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djadmin/passw0rd/29a06f8c267fc6523c625df4ad8cf1a523f12960/screenshot.png -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const checkPass = require('.'); 2 | 3 | checkPass.check('password') 4 | .then(z => console.log(z.pwned === true)); 5 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | output: { 6 | filename: 'passw0rd.js', 7 | library: 'passw0rd', 8 | libraryTarget: 'umd' 9 | }, 10 | devtool: 'source-map', 11 | plugins: [ 12 | new UglifyJSPlugin({ 13 | sourceMap: true 14 | }) 15 | ] 16 | }; 17 | --------------------------------------------------------------------------------