DEV Community

Greg Molnar
Greg Molnar

Posted on • Originally published at

Rails Authentication for Compliance

Suppose you are working on a Rails application that needs to meet specific security compliance requirements like PCI, ISO 27001, or SOC2. In that case, one of the objectives is to have proper authentication and access control.

The requirements differ between standards, but I gathered the most important ones from all of them to go through them.

Authenticate access to critical assets

Let's see what we need to do to satisfy this requirement.

First of all, you must have a strong password policy.

I recommend asking for a minimum of 12 characters, with at least one uppercase letter and one number.

You can use Active Record validations for this. If you have a password attribute on your model, you can add a validation similar to this example:

validate :password_complexity

def password_complexity
  if password.present? and !password.match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{12,}$/)
    errors.add :password, "must include at least one lowercase letter, one uppercase letter, one digit, and needs to be minimum 12 characters."
Enter fullscreen mode Exit fullscreen mode

Additionally, you should validate that the password is not leaked. Luckily, there is a gem for that:

After installing the gem, All you need to do is add the following validation to the model:

  validates :password, not_pwned: { on_error: :valid }
Enter fullscreen mode Exit fullscreen mode

This will make a request to, mark the password as invalid if it has been pwned, and mark it as valid in case of a network or API error. You can find information about various configurations in the readme of the gem.

The final thing to prevent is credentials leaking. For this, you should store the passwords hashed with a robust hashing algorithm such as bcrypt.

By default, restrict all access to critical assets to only those accounts and services that require such access

This one is more of an authorization than an authentication requirement. You should always use a whitelist approach for permissions and only permit access to certain information or features to the accounts that require it.

Accompany authentication information by additional authentication factors

Your authentication mechanism should include multiple factors, something the user knows and something the user has. If you are using Devise, you can use the devise-two-factor gem. If you have custom authentication, you can use the rotp gem to generate OTP codes and verify those during login.

One common mistake with multi-factor authentication implementations is to not require the second factor after a password reset, which defeats the purpose of the feature, so make sure that doesn't happen.

Don't display sensitive system or application information to avoid providing an unauthorized user with any unnecessary assistance

You should show a generic message if someone tries to access a page requiring authentication.

During the login process, you should never indicate which part of the login credentials are incorrect, otherwise it is possible to enumerate the usernames. Also, you should validate the username and password together to ensure there is no indication of which one is incorrect.

When validating the password, you should prevent timing attacks by using a constant time algorithm during password comparison. Devise does this by default. If you have a custom authentication implementation, ensure you are not vulnerable to this attack.

Protect against brute force log-on attempts and credential stuffing

You should ensure the password of accounts in your system can't be brute-forced or taken over by a credential stuffing attack from a list of leaked user credentials.

The first line of defense should be to put rate-limiting on your login endpoints. rack-attack can help with that. I recommend to limit the login attempts to 5 per minute for a username and block the IP for 30 minutes. You should also limit the number of login attempts from the same IP address, but this needs to be adjusted to the application you are working on, because if it is a tool used in classrooms, it might be legit to have 50 logins within a few minutes from the same IP. (I have a few post written about rack-attack)

After configuring rate-limiting, you should also introduce a captcha on the login page and trigger that for suspicious login attempts.

You could also send an email with a verification link in case of a suspicious login, for instance, from a non-usual location, device, etc.

Storing successful and unsuccessful login attempts can help to detect suspicious attempts. If you are using Devise, you can use the authtrail gem for that.

Don't leak clear text passwords

In 2023, everybody should use SSL/TLS, which prevents leaking clear text passwords over the network connections.

Also, you should never log passwords in clear text or let them leak if an exception is raised.

Terminate inactive sessions after a defined period of inactivity, especially in high-risk locations

Your "remember me" feature should be opt-in, and the session length should be adjusted to the necessary security level of the application.

Prevent concurrent sessions

You shouldn't allow concurrent sessions for an account to prevent session hijacking. To achieve this, you can save a fingerprint of the user on a successful login and trigger an MFA verification if the fingerprint changes during the requests.

I believe I covered all of the important points for compliance of the authentication, but if you think I missed something, please reach out.

Top comments (3)

phlash profile image
Phil Ashby

Excellent practical advice for Rails developers, thank you Greg! 🙏

I think you meant ISO 27001 at the top, as ISO 2007 is about testing rubber plasticity ( 😁

In addition to the advice above, I would also add:

  • Be very sure that you need to perform local authentication: consider delegating this to a specialist 3rd party (such as Auth0, Okta or AzureAD), possibly also federate back to customers where identity management already exists, since local authentication introduces several attack vectors to your system / service, additional workload for operations (often in call centres for password reset or forgotton identity flows and audits) and development (management interfaces for password resets, account provisioning, audit and removal).
  • Design your failure flows (particularly lost identities, forgotton passwords) to ensure they are no less secure than success flows. Typically these include humans in the loop: be aware and mitigate social engineering at process / information flow level.
  • Talk to the customers (if you can!), business people and the lawyers in your company to ensure your customers get what they need, not what you thought they did (yes, I got bitten by this one!) and that there are no obvious legal holes should something (inevitably) go wrong. Write your incident response plan with these same people before you ship anything!

All that said - have fun 😉

gregmolnar profile image
Greg Molnar

Oh, I didn't notice that. My spellchecker must have changed it :D
I meant ISO 27001, I have no clue about rubber plasticity :)
Thanks for letting me know.

With the recent Okta issues, I am not sure outsourcing your auth is the best idea, and for compliance, it might introduce complexities.

I completely agree with the other two points.

phlash profile image
Phil Ashby

Yep - good point about Okta having issues, outsourcing authentication comes with a shift in risk ownership, not necessarily a reduction - definitely one for a conversation with the auditor! In my last position, we discovered that the majority of our customers had existing identity management systems in place and that by federating with them (using OpenID Connect): we could avoid managing individual user identities ourselves; they got the benefits of local authorisation management without us in the loop (by assigning/removing user roles in their system) and their leavers process removed access to our services automatically, reducing abuse risk for both of us. We still had some smaller customers without their own identity management solution, so for them we provided an equivalent, now less coupled, identity management service (built with IdentityServer4) that could be separately charged for.