Skip to main content

Command Palette

Search for a command to run...

Default to Deny for More Secure Apps

Published
4 min read
Default to Deny for More Secure Apps
T

Every successful project starts with trust. We make this concept evident from the start of every new relationship. Before we write the first line of code, we will seek to understand you and your team as human beings. We start each partnership by building a foundation of mutual trust and respect. We'll ask about your business, your goals, and what you envision when you think of your future product. This ensures we are aligned on how to build your product and give you the best possible outcomes based on our understanding of your business.

Based in Nashville and founded in 2017, Twin Sun set out to bring humanity to software development consulting. We understand the challenges you face when selecting a development partner. We've worked with teams of all sizes, from one-person startups to global corporations, and we are ready to help navigate all aspects of product design, development, and long-term support to help make your effort as successful as possible.

Every product we build deals with user authorization. Users may only access certain features or data based on their permissions within the app. While we want to ensure users can access everything they should, we also want to ensure they can't access anything they shouldn't. This is why we default to deny all user privileges when starting a new app.

What is Default to Deny?

Network security practitioners are familiar with a similar concept. The National Institute of Standards and Technology (NIST) issued security guidance that describes a "deny by default / allow by exception" security control. The idea is simple: deny all network traffic by default and only allow traffic that is explicitly authorized. This concept is more broadly known as the principle of least privilege.

"Default to deny" is our application of this principle to mobile and web apps. When we start a new app, users are initially denied access to all features and data. We then explicitly grant access to the things the user is permitted to access.

Why is Default to Deny Important?

Defaulting to deny is an important safeguard for user data. Let's say we have an app that lets users purchase books. Users save their credit card information in the app so they don't have to re-enter it every time they make a purchase.

If we let every user access everything by default, what might happen? We could miss all of the ways that credit card data could be accessed. A malicious user could probably find a way to steal credit card numbers from other user accounts. We certainly don't want that! So why don't we start with the complete opposite position?

If we deny all access by default, no one can steal credit card information. Users also wouldn't be able to access their own credit card details. That isn't great, but it's much better than letting anyone see all credit card numbers. And opening up user permissions just a little bit is much easier than trying to find all the ways sensitive data might be accessed.

By defaulting to deny, we can be confident that no one is doing anything unless it is explicitly allowed. Our users' data is protected from accidental exposure and malicious attacks.

How to Implement Default to Deny

As an example of how to default to deny, consider a Ruby on Rails app (as we tend to do). The primary way a user interacts with the app is through API endpoints powered by controllers. We use Pundit, a popular authorization library for Rails, to manage user permissions.

Using a BaseController class, we can define an after_action that ensures an authorization check is performed on all requests by default.

class BaseController < ActionController::Base
  include Pundit::Authorization
  after_action :verify_authorized
end

Then we can define a base Pundit policy that denies all access by default. Note that we don't need to define any actions in the policy. Pundit will automatically check for a policy method that matches the controller action. However, actions are defined in our example to make it clear that we are denying access by default.

class BasePolicy
  def index?
    false
  end

  def show?
    false
  end

  def create?
    false
  end

  def update?
    false
  end

  def destroy?
    false
  end
end

Now, we can have any controller inherit from BaseController and Pundit will deny all access to all actions. For each model class we define, we can add a new Pundit policy that inherits from BasePolicy. Then we can explicitly allow access to the actions we want to allow. Consider the following policy example for a Book model. All users can use the index or show actions, but only administrators can use the create, update, or destroy actions.

class BookPolicy < BasePolicy
  def index?
    true
  end

  def show?
    true
  end

  def create?
    user.admin?
  end

  def update?
    user.admin?
  end

  def destroy?
    user.admin?
  end
end

Starting With Security

Default to deny is a simple concept that can have a big impact on the security of your app. By denying all access by default, you can be confident that no one is doing anything unless it is explicitly allowed. This is especially important for apps that deal with sensitive user data. Consider defaulting to deny when starting your next app and see how much more confident you feel about your authorization rules.