Greetings, code warriors. Welcome back to another Programming Thursday session. Today, we’re going to dip our toes into the deep waters of Ruby - Rails routing. Now, don’t be alarmed, this isn’t your average tutorial. We’re going to delve into advanced routing concepts and techniques. Get ready to crack some serious code. So, gear up, as it’s time to navigate the winding roads of Rails routing.
Routing in Rails: The Basics
First off, a quick recap for our Ruby initiates. In the Rails universe, routing is the wizard behind the curtains, orchestrating the grand play between a user’s request and the corresponding action. Here’s a little diagram to visualize this:
Routing bridges the gap between the HTTP request and the controller action. It’s the translator, the traffic cop, and the switchboard operator, all rolled into one. But, like everything else in Rails, it’s also a pliable tool you can bend to your will, once you know its ins and outs. That’s where we’re heading today.
Route Customization: Going Beyond The Basics
Most Rails apps will get by just fine with the basic ‘resources’ routing structure, which auto-generates a set of RESTful routes. But when you’re a pen tester or a red teamer, ‘just fine’ rarely cuts it, right? You need to have complete command of your tools.
resources :photos
The code above is compact, but it also provides a complete set of routes for a Photos resource:
GET /photos # maps to the index action in the Photos controller
GET /photos/:id # maps to the show action
GET /photos/new # maps to the new action
POST /photos # maps to the create action
GET /photos/:id/edit # maps to the edit action
PATCH /photos/:id # maps to the update action
DELETE /photos/:id # maps to the destroy action
That’s efficient, and we love efficiency. But let’s push the boundaries.
Controlling Routes Verbosity
Imagine a scenario where you only need the show and index actions. It’s overkill to define all those routes, right? Don’t worry, Rails got your back:
resources :photos, only: [:index, :show]
Or the opposite scenario. If you don’t want destroy to be available for security reasons:
resources :photos, except: [:destroy]
Nice and clean, isn’t it?
Nesting Routes
Now, let’s make it complex, just for fun. How about nested routes?
resources :photos do
resources :comments
end
This will give you routes for comments that are nested under photos. So a path will look
like /photos/:photo_id/comments/:id
.
And just like before, we can limit the routes for nested resources.
resources :photos do
resources :comments, only: [:create, :destroy]
end
In this case, a comment can only be created or destroyed in the context of a photo. Makes sense, doesn’t it?
More Than A Resource: Singular Resources and Non-CRUD Actions
In Rails, not everything has to be a full set of RESTful routes. What if we have something that does not need an ID, something singular like a profile or a session?
resource :session
Notice resource instead of resources. It only makes routes that apply to a single thing. There are no index or show routes here, because we don’t need an ID to find our resource.
How about non-CRUD actions? In addition to the standard set of routes that Rails creates with the resources method, you might want to add routes for some non-standard actions. Here’s how you do it:
resources :photos do
member do
get 'preview'
end
end
This will recognize /photos/1/preview
with a GET
, and route to the preview action of PhotosController
.
If you want to add a route that applies to all photos rather than a single photo, you can use the collection method:
resources :photos do
collection do
get 'search'
end
end
This will recognize /photos/search
with a GET
, and route to the search action of PhotosController
.
Advanced Constraints
Being able to control what goes into your routes is fantastic. But sometimes you want even more control, like being able to specify constraints. This is where advanced routing comes into play.
Dynamic Segments
Let’s start with dynamic segments:
get 'photos/:year/:month/:day', to: 'photos#show'
In this case, :year
, :month
, and :day
are dynamic segments. They’ll be available in your action
as params[:year]
, params[:month]
, and params[:day]
.
Constraints
But what if we only want to accept valid dates? We don’t want someone typing in /photos/2023/25/66
and expecting it to
work, right? Constraints to the rescue!
get 'photos/:year/:month/:day',
to: 'photos#show',
constraints: {
year: /\d{4}/,
month: /0[1-9]|1[0-2]/,
day: /0[1-9]|[12][0-9]|3[01]/
}
In the above code, we’ve used regular expressions to define valid values for each dynamic segment. Now, a request
to /photos/2023/25/66
will not be recognized by this route.
Advanced Constraints with Request Properties
That’s cool, but we can go even further. Did you know you can apply constraints based on other properties of the request? For example, if you want a route to be only accessible for a specific IP:
get '/admin_panel', to: 'admin#dashboard', constraints: { ip: /192\.168\.1\.\d+/ }
This route will only be recognized if the request comes from an IP address in the 192.168.1.*
subnet.
Catch-all and Globbing Routes
Lastly, let’s discuss wildcard segments, also known as “catch-all” and “globbing” routes. These are especially handy when dealing with unknown or unexpected request paths.
get '*path', to: 'errors#not_found'
The wildcard segment *path will match any route that hasn’t been matched so far. In this example, it’ll route to
the not_found
action of ErrorsController
.
For security reasons, you may want to monitor such traffic.
get '*path', to: 'monitoring#unmatched_request'
Now, all unmatched requests will be routed to MonitoringController
’s unmatched_request
action. This will allow you
to log and inspect the rogue requests.
Cybersecurity Implications of Routing
Routes, my fellow hackers, can reveal more about an application than you may initially think. They are a window to the app’s inner workings and, if not properly secured, can be exploited.
Information Leakage
Routes often reveal the structure of an application. Consider the route GET /admin/users
. It gives away the existence
of an admin namespace, along with a users resource. This is a treasure trove for anyone trying to infiltrate your
system. Therefore, obscuring route names could be a good first defense line.
# Instead of this
namespace :admin do
resources :users
end
# Do this
namespace :a1b2c3 do
resources :x4y5z6
end
The new route GET /a1b2c3/x4y5z6
doesn’t spill any beans about your structure. Of course, you have to balance security
with usability here.
Unauthorized Access
Routes must always be authenticated and authorized. Let’s say you have an OrdersController
and a destroy action that
deletes an order. If it’s not properly authorized, any user could potentially delete any order!
# Before
def destroy
Order.find(params[:id]).destroy
redirect_to orders_url, notice: 'Order was successfully destroyed.'
end
# After
def destroy
current_user.orders.find(params[:id]).destroy
redirect_to orders_url, notice: 'Order was successfully destroyed.'
end
With the latter example, a user can only delete an order that belongs to them.
Route Injection
Dynamic segments are a blessing and a curse. They make routes flexible but also introduce the potential for injection attacks.
# routes.rb
get 'products/:name', to: 'products#search'
# products_controller.rb
def search
@products = Product.where("name LIKE '%#{params[:name]}%'")
end
In this case, a savvy attacker could use a SQL Injection through the :name
parameter. The solution? Never trust user
input and always sanitize it.
# products_controller.rb
def search
@products = Product.where("name LIKE ?", "%#{params[:name]}%")
end
Analyzing Routing for Vulnerabilities
As you hunt for vulnerabilities, you will often start by enumerating all the available routes. If you have access to the codebase, that’s as simple as running rake routes. If you don’t, it may involve spidering the site or even educated guessing.
Once you have your list, it’s time to put those routes under a microscope. Look for patterns. Are the routes RESTful? Do they use any dynamic segments? What about nested resources?
Armed with this information, you can start your attack. This is where your favorite tools like Burp Suite or OWASP ZAP come into play. You want to see how these routes react under stress.
- Does the route leak any information in its response?
- Can you access the route without authentication?
- Does the route accept verbs it shouldn’t?
- Can you manipulate a dynamic segment to gain unauthorized access?
Remember, as a pen tester or a red teamer, you’re looking to probe every nook and cranny of the application.
Securing Routes
In this world of constant threats, it is crucial to ensure your routing does not become an entry point for attacks. Here are a few tips:
- Always authenticate and authorize: If a route should only be accessed by a logged-in user, ensure it is so. Rails’ before_action can help here.
- Sanitize dynamic segments: As seen earlier, unsanitized dynamic segments can lead to injection attacks. Always sanitize and validate these segments.
- Limit HTTP verbs: If a route should only respond to a GET request, ensure it doesn’t accept POST or DELETE.
- Obfuscate routes: To prevent information leakage, avoid using too obvious names for your routes.
- Keep an eye on the logs: Unusual route requests often precede an attack. Keep an eye on your logs and set up alerts for suspicious activity.
Navigating the routing labyrinth can be complex, but as you delve deeper into Rails’ routing, it becomes an exhilarating journey. It’s a game of cat and mouse between you and potential infiltrators.
Conclusion
Well, fellow code warriors, I hope this deep-dive has given you a better understanding of Rails routing’s power and flexibility. There’s a lot more to routing than meets the eye, and I trust you now have a better grasp of it.
As red teamers and pen testers, you now have more tools in your arsenal. Play around with constraints, explore singular resources, and monitor those wildcard routes. Remember, you’re only as good as your tools.
In our next Programming Thursday, we’ll turn up the heat, talking about HTTP requests in depth - we’re going to dissect them like a frog in a biology class. Until then, happy hacking!