This content originally appeared on DEV Community and was authored by Khalyomede
When developing web apps, we often rely on fetching informations from the database. Frameworks offer a way to make this easy thanks to ORMs.
Most of the time, the ORM will find your model using the primary key as a reliable identifier. On the vast majority, primary keys are auto incremented integers.
Your URLs then look like this:
https://example.com/cart/12
https://example.com/user/15/post/41
...
Providing an incorrect or faulty authorization layer can create data leaks: users become able to navigate from data to data, which is something we would not want to allow if it is about sensitive data like users personal info.
Obfuscating the identifier
An easy way to mitigate this security breach is to use a key that is:
- Hard to predict
- Random enough to be able to create a lot of items while keeping the unicity between them
- Easy to generate from your code and the database
- Checkable (we can know by analyzing its integrity if it is valid or not)
Thanks for us, UUIDs are a very good candidate for it. It checks all the points above, and are very easy to use thanks to a wide range of package ready for use.
Your URLS now become harder to predict, which mitigate any developers mistake regarding authorization policies in your app:
https://example.com/cart/f6e4208f-5df4-466e-9225-01f296e2a09c
https://example.com/user/b1b44b12-34bc-4ed7-a666-9657b8b8c31b/post/e530d034-42f7-467b-91e3-1cc9313312eb
Example on a Laravel app
In order to practice developing a Laravel package, and using my first ever Github Workflow, I created a package to make this job a breeze.
Here is how you can use khalyomede/laravel-eloquent-uuid-slug in your app now.
Install the package
First, head in your console, and type this command:
composer require khalyomede/laravel-eloquent-uuid-slug
Add your slug column to your migration
Then, go to the migration of the model of your choice, or create a new one if it has already been installed, and add the slug column.
namespace Database\Migrations;
use App\Models\Cart;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
final class AddSlugColumnToCartTable extends Migration {
public function up(): void
{
Schema::table('carts', function (Blueprint $table): void {
Cart::addSlugColumn($table);
});
}
public function down(): void
{
Schema::table('carts', function (Blueprint $table): void {
Cart::dropSlugColumn($table); // available soon in v0.2.0
});
}
};
Add the trait to your model
This is the last step, which will help configure how your model is retreived in your routes using Route model binding.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Khalyomede\EloquentUuidSlug\Sluggable;
final class Cart extends Model
{
use Sluggable;
}
Use it in your controller
Now you are ready to take advantage of the package. The great thing with it is that your code does not change! You can keep using the route()
method like you are used to.
// routes/web.php
use App\Models\Cart;
use Illuminate\Http\RedirectResponse
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\View\View;
Route::get("cart/{cart}", function (Cart $cart): View {
return view("cart.show", [
"cart" => $cart,
"saveCartRoute" => route("cart.store", $cart),
])->name("cart.show");
});
Route::post("cart/{cart}", function (Request $request, Cart $cart): RedirectResponse {
$cart->update($request->only(["name"]));
return redirect()->route("cart.show", $cart);
})->name("cart.store");
And voilà! This package will no interfer with your existing logic as you can see. The only thing that changes is now your routes are not exposed.
route("cart.show", $cart); // https://example.com/cart/398e76a7-7c16-467c-93a8-04c06c6df703
Conclusion
While this solution is not a magic way to resolve the initial problem of data leak, I find it is a very easy actionable mecanism to reduce the possibilities for malicious users to trick your system.
This does not prevent you to add an authorization or guard mecanism, like Laravel Policies for example. For example, if a user navigates to a cart that have not been created by him/her, should not be able to view it.
Other folks here already talked about this subject, so make sure to give it a go if you want to read more about using UUIDs:
4 packages to generate UUIDs in JavaScript
Rahul Banerjee ・ Aug 16 ・ 1 min read
Why you shouldn't expose your incrementing IDs
Jeroen van Rensen ・ Mar 7 ・ 3 min read
How to use UUIDs in URLs in Laravel
Jeroen van Rensen ・ Mar 10 ・ 2 min read
Happy URL hardening!
This content originally appeared on DEV Community and was authored by Khalyomede
Khalyomede | Sciencx (2021-08-22T18:36:45+00:00) Do not expose database ids in your URLs. Retrieved from https://www.scien.cx/2021/08/22/do-not-expose-database-ids-in-your-urls/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.