Bola Idor in Hanami
How Bola Idor Manifests in Hanami
BOLA (Broken Object Level Authorization) and IDOR (Insecure Direct Object Reference) vulnerabilities in Hanami applications typically arise from improper authorization checks when handling resource identifiers in URLs or request parameters. These vulnerabilities allow authenticated users to access or manipulate objects they don't own by simply changing identifiers in requests.
In Hanami, these vulnerabilities often manifest in controller actions that accept IDs from parameters without verifying resource ownership. For example, a typical vulnerable pattern looks like this:
module Web::Controllers::Posts
class Show
include Web::Action
def call(params)
@post = PostRepository.new.find(params[:id])
# No authorization check
end
end
endThe repository pattern in Hanami, while elegant, can hide authorization logic. Developers often assume that finding a record by ID is sufficient, but this creates a critical security gap. An attacker who knows another user's post ID can simply modify the URL to access unauthorized content.
Another common manifestation occurs in update/delete operations:
module Web::Controllers::Posts
class Update
include Web::Action
def call(params)
@post = PostRepository.new.find(params[:id])
PostRepository.new.update(@post.id, params[:post])
# No check that current_user owns @post
end
end
endHanami's slice architecture can exacerbate these issues when business logic is scattered across slices. A Posts slice might handle CRUD operations without awareness of user context, while the Accounts slice manages authentication separately. This separation can lead to authorization logic being forgotten or bypassed.
RESTful routing in Hanami applications often creates predictable URL patterns (e.g., /posts/:id, /users/:id), making it easier for attackers to enumerate and test for BOLA/IDOR vulnerabilities. The framework's convention-over-configuration approach means developers might not consider security implications when following standard patterns.
Hanami-Specific Detection
Detecting BOLA/IDOR vulnerabilities in Hanami applications requires examining controller actions for proper authorization patterns. The key indicators include:
- Repository calls using params[:id] without subsequent authorization checks
- Missing before_actions that verify resource ownership
- Actions that accept user IDs or resource IDs without validating the current user's permissions
middleBrick's scanner can detect these patterns through black-box testing of your Hanami API endpoints. The scanner automatically tests for BOLA vulnerabilities by:
- Identifying endpoints that accept resource identifiers
- Modifying those identifiers to access other users' data
- Analyzing responses to determine if authorization is properly enforced
For Hanami applications, middleBrick specifically looks for:
# Scan your Hanami API endpoint
middlebrick scan https://api.yourapp.comThe scanner tests common Hanami patterns like:
- /api/v1/posts/:id - testing if users can access other users' posts
- /api/v1/users/:id - checking if user profile data is properly protected
- /api/v1/comments/:id - verifying comment ownership enforcement
middleBrick's OpenAPI analysis also examines your Hanami application's spec files to identify parameter definitions that could be vulnerable to BOLA attacks, then correlates these with runtime findings.
Manual code review should focus on:
# Look for patterns like this
post = PostRepository.new.find(params[:id])
# Is there a check like this?
raise Unauthorized unless post.user_id == current_user.id?Hanami's dependency injection can sometimes obscure authorization logic. When repositories are injected without proper scoping, they may return records regardless of the current user's permissions.
Hanami-Specific Remediation
Remediating BOLA/IDOR vulnerabilities in Hanami requires implementing proper authorization checks at the repository or controller level. The most effective approach uses Hanami's slice architecture to create scoped repositories.
First, create a scoped repository pattern:
# apps/web/lib/web/repositories/scoped_post_repository.rb
module Web::Repositories
class ScopedPostRepository
def initialize(current_user)
@current_user = current_user
@repository = PostRepository.new
end
def find(id)
post = @repository.find(id)
raise Unauthorized unless post.user_id == @current_user.id
post
end
def all
@repository.where(user_id: @current_user.id)
end
def create(data)
@repository.create(data.merge(user_id: @current_user.id))
end
def update(id, data)
post = find(id)
@repository.update(post.id, data)
end
def delete(id)
post = find(id)
@repository.delete(post.id)
end
end
endThen use this in your controller:
module Web::Controllers::Posts
class Show
include Web::Action
def initialize(scoped_post_repository: ScopedPostRepository.new(current_user))
@scoped_post_repository = scoped_post_repository
end
def call(params)
@post = @scoped_post_repository.find(params[:id])
end
end
endAlternatively, use Hanami's policies for authorization:
# apps/web/lib/web/policies/post_policy.rb
module Web::Policies
class PostPolicy
def initialize(current_user, post)
@current_user = current_user
@post = post
end
def can_view?
@post.user_id == @current_user.id
end
def can_edit?
can_view?
end
def can_delete?
can_view?
end
end
endIntegrate policies into controllers:
module Web::Controllers::Posts
class Show
include Web::Action
def call(params)
@post = PostRepository.new.find(params[:id])
policy = PostPolicy.new(current_user, @post)
raise Unauthorized unless policy.can_view?
end
end
endFor Hanami applications using JWT authentication, ensure the current_user is properly injected:
# apps/web/lib/web/middleware/auth_middleware.rb
module Web::Middleware
class AuthMiddleware
def initialize(app, options = {})
@app = app
end
def call(env)
request = Rack::Request.new(env)
token = request.get_header('HTTP_AUTHORIZATION')&.split&.last
if token
payload = JwtService.decode(token)
env['hanami.current_user'] = UserRepository.new.find(payload['user_id'])
end
@app.call(env)
end
end
endFinally, implement comprehensive testing:
# spec/web/controllers/posts_controller_spec.rb
RSpec.describe Web::Controllers::Posts::Show do
let(:post) { PostRepository.new.create(user_id: other_user.id, title: 'Test') }
it 'prevents access to other users posts' do
response = subject.call(id: post.id)
expect(response.status).to eq(403)
end
endRelated CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |