{"id":14955786,"url":"https://github.com/deadkennedyx/usualsuspect","last_synced_at":"2025-10-06T06:30:26.797Z","repository":{"id":216583995,"uuid":"739951553","full_name":"DeadKennedyx/UsualSuspect","owner":"DeadKennedyx","description":"Gem that tracks suspicious activity within your user sessions.","archived":false,"fork":false,"pushed_at":"2024-02-03T23:12:25.000Z","size":73,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-24T20:44:34.869Z","etag":null,"topics":["device-fingerprinting","fingerprinting","gem","geolocalization","geolocation","proxy","rails","ruby","ruby-on-rails","rubyonrails","security","security-audit","security-tools","tor","user-behavior-analytics","vpn"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DeadKennedyx.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-07T03:33:00.000Z","updated_at":"2024-05-14T11:22:00.000Z","dependencies_parsed_at":"2024-01-11T01:39:40.298Z","dependency_job_id":"e4f0b52a-01b5-4ed7-b0a0-5dda63cd2eb1","html_url":"https://github.com/DeadKennedyx/UsualSuspect","commit_stats":{"total_commits":33,"total_committers":1,"mean_commits":33.0,"dds":0.0,"last_synced_commit":"804b70e2cae40a6ba567f6a17b89769257662e7b"},"previous_names":["deadkennedyx/usualsuspect"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeadKennedyx%2FUsualSuspect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeadKennedyx%2FUsualSuspect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeadKennedyx%2FUsualSuspect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeadKennedyx%2FUsualSuspect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DeadKennedyx","download_url":"https://codeload.github.com/DeadKennedyx/UsualSuspect/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235504021,"owners_count":19000704,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["device-fingerprinting","fingerprinting","gem","geolocalization","geolocation","proxy","rails","ruby","ruby-on-rails","rubyonrails","security","security-audit","security-tools","tor","user-behavior-analytics","vpn"],"created_at":"2024-09-24T13:11:48.165Z","updated_at":"2025-10-06T06:30:26.788Z","avatar_url":"https://github.com/DeadKennedyx.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# UsualSuspect: Your Rails Guardian Against Suspicious Logins and Behaviour\n\n**UsualSuspect** is a Ruby gem specifically tailored for Rails applications, designed to fortify your user authentication system against an array of security threats. With a focus on real-time analysis and monitoring of user logins, UsualSuspect acts as a vigilant guard, identifying unusual and potentially harmful activities, the response is up to you!\n\n## Key Features\n\n- **Password Change Monitoring**: Detects and logs suspicious password changes immediately after user login, providing an early warning system against account hijacking.\n- **Geo-Velocity Tracking**: Utilizes algorithms to calculate the speed of user movement based on login locations, flagging physically impossible travel scenarios that may indicate account compromises or account multi sharing.\n- **Device Fingerprinting**: Device fingerprinting support.\n- **New Device Detection**: check saved fingerprints to detect new devices.\n- **VPN, Proxy and Tor Detection**: Leverages [vpnapi.io](https://vpnapi.io/api-documentation) robust database to identify logins from VPNs and proxies, enhancing your defense against masked IP addresses and location spoofing.\n- **Session-Specific Analysis**: Each login session is treated uniquely, ensuring precise and context-aware security checks.\n- **Configurable and Extendable**: Tailor the gem's behavior to your application's specific needs with customizable settings and thresholds.\n- **Seamless Rails Integration**: Designed to integrate effortlessly with Rails applications, allowing you to add an extra layer of security with minimal setup.\n\n## Installation\n\nAdd UsualSuspect to your application's Gemfile:\n\n```ruby\ngem 'usual_suspect'\n```\n\nThen execute:\n\n```bash\n$ bundle install\n$ bundle exec rake usual_suspect:setup\n$ rails db:migrate\n```\n\nConfigure UsualSuspect in an initializer with your VPN API key from [vpnapi.io](https://vpnapi.io/api-documentation):\n\n```ruby\nUsualSuspect.configure do |config|\n  config.vpn_api_key = 'YOUR_API_KEY'\nend\n```\nThe free tier on [vpnapi.io](https://vpnapi.io/api-documentation) allows for 1k requests per day, you'll need to upgrade tier if you want more, if you want to make this gem to be available to use in more services feel free to open a pull request or an issue and I'll add support for more services!\n\n## Usage\n\nAdd this to your User model:\n\n```ruby\ninclude UsualSuspect::UserExtension\n\nhas_many :usual_suspect_events\n```\n\nIn your sessions controller add:\n\n```ruby\ninclude UsualSuspect::SessionsControllerExtension\n```\n\nAnd when your session is created and you have a current_user available then add this line:\n\n```\ntrack_usual_suspect_login\n```\n#### Suspicious password change:\n\nTo check for suspicious password change after login you can add this in the controller and method where the user password is updated:\n```\t\ncurrent_user.check_for_suspicious_password_change(session[:usual_suspect_session_token])\n```\n#### Device fingerprinting:\nTo add device fingerprinting you will need to send device_info parameters in your login form. you can get device_info this way with javascript, usual_suspect will take care of the rest.\n\nThis will save the device fingerprint as a sha256 digest `device_fingerprint: \"c18ab96a22bece029c71a7229b48234786b3055880eeda6091942ef5cd136\"` and if a new device logs with a different fingerprint it will save the `new_device` column as true.\n\n```javascript\nvar formId = 'your_login_form_id'; // Replace with your actual form ID\nvar form = document.getElementById(formId);\n\nvar deviceInfo = {\n    language: navigator.language,\n    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n    screen_width: screen.width,\n    screen_height: screen.height,\n    colorDepth: screen.colorDepth,\n    sessionStorage: !!window.sessionStorage,\n    localStorage: !!window.localStorage,\n    cookiesEnabled: navigator.cookieEnabled,\n    deviceMemory: navigator.deviceMemory || 'unknown',\n    hardwareConcurrency: navigator.hardwareConcurrency,\n};\n\nfunction createAndAppendInput(name, value) {\n    var input = document.createElement('input');\n    input.type = 'hidden';\n    input.name = 'device_info[' + name + ']';\n    input.value = value;\n    form.append(input);\n}\n\nfor (var key in deviceInfo) {\n    createAndAppendInput(key, deviceInfo[key]);\n}\n```\n#### Data saved:\nWhen the user logs in, it will create a new record in the table `UsualSuspectEvent`, and depending on the fields you can decide if you should block that user account.\nAlso when the user changes password it will update the `password_change_after_login` to true if the password change was before 5 minutes of being logged in.\n\nUsualSuspectEvent Table:\n```ruby\n#\u003cUsualSuspectEvent:0x00007fdef36c3c00\n id: 18,\n user_id: 2,\n last_sign_in_at: nil,\n sign_in_at: Thu, 25 Jan 2024 01:42:13.557680000 UTC +00:00,\n password_change_after_login: true,\n geovelocity_failed: true,\n using_vpn: true,\n using_proxy: false,\n using_tor: false,\n new_device: false,\n sign_in_ip: \"xxx.xxx.xxx.xx\",\n city: \"Madrid\",\n country: \"ES\",\n latitude: \"xx.xxxx\",\n longitude: \"-xxx.xxxx\",\n session_token: \"[FILTERED]\",\n device_fingerprint: \"c18ab96a22bece029c71a7229b48234786b3055880eeda6091942ef5cd136\",\n created_at: Thu, 25 Jan 2024 01:42:13.562592000 UTC +00:00,\n updated_at: Thu, 25 Jan 2024 01:42:13.562592000 UTC +00:00\u003e\n```\n\n## Contributing\n\nIf you are cloning or using the gem, give me a little star :)!\n\nContributions are more than welcome! If you have ideas for improvements or encounter any issues, please feel free to submit a pull request or an issue and I'll find the time to fix it.\n\n## License\n\nDistributed under the MIT License. See `LICENSE` for more information.\n\n## Acknowledgements\n\n- [vpnapi.io](https://vpnapi.io/api-documentation)\n- [Geocoder](https://github.com/alexreisner/geocoder)\n\nElevate your Rails application's security to the next level with UsualSuspect!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeadkennedyx%2Fusualsuspect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeadkennedyx%2Fusualsuspect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeadkennedyx%2Fusualsuspect/lists"}