Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better handling for weird data passed to the API via limit and offset #12827

Merged
merged 1 commit into from
Apr 15, 2023

Conversation

snipe
Copy link
Owner

@snipe snipe commented Apr 12, 2023

So, every now and then, people think they're cute and they start passing bad data to the API to see how it breaks. Currently it will just return a 500 error, which is fine I suppose, but it tends to poop all over our logging system.

This should set us up with sane default, while also still making sure that you're never going to get more results than the MAX_RESULTS in the .env.

When I had some debugging code in there, I tested this with multiple different URL parameters, and the numbers all look the way I'd expect:

[2023-04-12 11:58:32] local.DEBUG: Max Results: 200
[2023-04-12 11:58:32] local.DEBUG: Original Requested Limit: 10
[2023-04-12 11:58:32] local.DEBUG: Intval Limit: 10
[2023-04-12 11:58:32] local.DEBUG: Modified Limit: 10
[2023-04-12 11:58:32] local.DEBUG: Original Requested Offset: -2
[2023-04-12 11:58:32] local.DEBUG: Modified Offset: 0
[2023-04-12 11:58:32] local.DEBUG: Total Results: 209
[2023-04-12 11:58:32] local.DEBUG: -----------------------------
[2023-04-12 11:58:35] local.DEBUG: Max Results: 200
[2023-04-12 11:58:35] local.DEBUG: Original Requested Limit: 10
[2023-04-12 11:58:35] local.DEBUG: Intval Limit: 10
[2023-04-12 11:58:35] local.DEBUG: Modified Limit: 10
[2023-04-12 11:58:35] local.DEBUG: Original Requested Offset: -20
[2023-04-12 11:58:35] local.DEBUG: Modified Offset: 0
[2023-04-12 11:58:35] local.DEBUG: Total Results: 209
[2023-04-12 11:58:35] local.DEBUG: -----------------------------
[2023-04-12 11:58:37] local.DEBUG: Max Results: 200
[2023-04-12 11:58:37] local.DEBUG: Original Requested Limit: 100
[2023-04-12 11:58:37] local.DEBUG: Intval Limit: 100
[2023-04-12 11:58:37] local.DEBUG: Modified Limit: 100
[2023-04-12 11:58:37] local.DEBUG: Original Requested Offset: -20
[2023-04-12 11:58:37] local.DEBUG: Modified Offset: 0
[2023-04-12 11:58:37] local.DEBUG: Total Results: 209
[2023-04-12 11:58:37] local.DEBUG: -----------------------------
[2023-04-12 11:58:40] local.DEBUG: Max Results: 200
[2023-04-12 11:58:40] local.DEBUG: Original Requested Limit: 1000
[2023-04-12 11:58:40] local.DEBUG: Intval Limit: 1000
[2023-04-12 11:58:40] local.DEBUG: Modified Limit: 200
[2023-04-12 11:58:40] local.DEBUG: Original Requested Offset: -20
[2023-04-12 11:58:40] local.DEBUG: Modified Offset: 0
[2023-04-12 11:58:40] local.DEBUG: Total Results: 209
[2023-04-12 11:58:40] local.DEBUG: -----------------------------
[2023-04-12 11:58:43] local.DEBUG: Max Results: 200
[2023-04-12 11:58:43] local.DEBUG: Original Requested Limit: -1000
[2023-04-12 11:58:43] local.DEBUG: Intval Limit: -1000
[2023-04-12 11:58:43] local.DEBUG: Modified Limit: 1
[2023-04-12 11:58:43] local.DEBUG: Original Requested Offset: -20
[2023-04-12 11:58:43] local.DEBUG: Modified Offset: 0
[2023-04-12 11:58:43] local.DEBUG: Total Results: 209
[2023-04-12 11:58:43] local.DEBUG: -----------------------------
[2023-04-12 11:58:47] local.DEBUG: Max Results: 200
[2023-04-12 11:58:47] local.DEBUG: Original Requested Limit: -1000
[2023-04-12 11:58:47] local.DEBUG: Intval Limit: -1000
[2023-04-12 11:58:47] local.DEBUG: Modified Limit: 1
[2023-04-12 11:58:47] local.DEBUG: Original Requested Offset: farts
[2023-04-12 11:58:47] local.DEBUG: Modified Offset: 0
[2023-04-12 11:58:47] local.DEBUG: Total Results: 209
[2023-04-12 11:58:47] local.DEBUG: -----------------------------
[2023-04-12 11:58:53] local.DEBUG: Max Results: 200
[2023-04-12 11:58:53] local.DEBUG: Original Requested Limit: -5) OR 584=(SELECT 584 FROM PG_SLEEP(15))--
[2023-04-12 11:58:53] local.DEBUG: Intval Limit: -5
[2023-04-12 11:58:53] local.DEBUG: Modified Limit: 1
[2023-04-12 11:58:53] local.DEBUG: Original Requested Offset: 20
[2023-04-12 11:58:53] local.DEBUG: Modified Offset: 20
[2023-04-12 11:58:53] local.DEBUG: Total Results: 209
[2023-04-12 11:58:53] local.DEBUG: -----------------------------
[2023-04-12 11:59:15] local.DEBUG: Max Results: 200
[2023-04-12 11:59:15] local.DEBUG: Original Requested Limit: -5) OR 584=(SELECT 584 FROM PG_SLEEP(15))--
[2023-04-12 11:59:15] local.DEBUG: Intval Limit: -5
[2023-04-12 11:59:15] local.DEBUG: Modified Limit: 1
[2023-04-12 11:59:15] local.DEBUG: Original Requested Offset: farts
[2023-04-12 11:59:15] local.DEBUG: Modified Offset: 0
[2023-04-12 11:59:15] local.DEBUG: Total Results: 209
[2023-04-12 11:59:15] local.DEBUG: -----------------------------

I've tested this and all looks well, however this touches basically every major listing endpoint we have, so I'd appreciate some additional testing :)

@what-the-diff
Copy link

what-the-diff bot commented Apr 12, 2023

PR Summary

  • Improved Input Validation
    In multiple controllers, the offset is now an integer instead of a string, preventing potential SQL injection attacks through better input validation.

  • Limit Restriction
    The API now restricts the limit to a maximum value defined in config (default: 250). This helps by not returning more results than necessary and improves performance.

  • Offset and Limit Validation
    The offset and limit values are now checked and validated as integers before use.

  • Offset Handling
    If the provided offset is greater than the number of items, it now returns 0 results instead of an error message, improving error handling.

  • Prevent Server Error
    By not allowing the limit to exceed a maximum value (previously 1000), server errors are prevented when attempting to fetch excessive amounts of records at once.

Copy link
Collaborator

@uberbrady uberbrady left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems pretty reasonable, but I do have a couple of questions -

  • We don't use the intval() thing when comparing the $request->input('limit') value to the config('app.max_results') value - I don't know if that's a big deal or not though.
  • We are doing a second max() call when we already compared these values before. Is that necessary?
  • All of this feels a little...repetitive. Is there something we can do that's just a little more systemic so that we aren't repeating ourselves all the time? Maybe something from here: https://laravel.com/docs/9.x/pagination ?

All of that being said, those are just tiny nits and I don't think sufficient to block this from acceptance, if I can do a couple of key tests first (which, I will do).

Please let me know your thoughts on this and I'll get to testing.

@marcusmoore
Copy link
Collaborator

We don't use the intval() thing when comparing the $request->input('limit') value to the config('app.max_results') value - I don't know if that's a big deal or not though.

@uberbrady I think we can safely assume config('app.max_results') is an integer since it pulls MAX_RESULTS from the environment or defaults to an integer. It wouldn't hurt to wrap it in intval() but I don't think it's necessary.

((config('app.max_results') >= $request->input('limit')) && ($request->filled('limit'))) ? $limit = $request->input('limit') : $limit = config('app.max_results');
// Make sure the offset and limit are actually integers and do not exceed system limits
$offset = ($request->input('offset') > $accessories->count()) ? $accessories->count() : intval(request('offset'));
$limit = ($request->input('limit') > config('app.max_results')) ? config('app.max_results') : max(intval(request('offset')), config('app.max_results'));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something but let's say MAX_RESULTS=50 and the user requests ?limit=10 then $limit would be set to 50. Is that expected?

Copy link
Owner Author

@snipe snipe Apr 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so?

[2023-04-12 11:58:32] local.DEBUG: Max Results: 200
[2023-04-12 11:58:32] local.DEBUG: Original Requested Limit: 10
[2023-04-12 11:58:32] local.DEBUG: Intval Limit: 10
[2023-04-12 11:58:32] local.DEBUG: Modified Limit: 10
[2023-04-12 11:58:32] local.DEBUG: Original Requested Offset: -2
[2023-04-12 11:58:32] local.DEBUG: Modified Offset: 0
[2023-04-12 11:58:32] local.DEBUG: Total Results: 209

I can dig deeper shortly, but I believe it's doing the right thing here.

@snipe
Copy link
Owner Author

snipe commented Apr 13, 2023

@uberbrady

We don't use the intval() thing when comparing the $request->input('limit') value to the config('app.max_results') value - I don't know if that's a big deal or not though.

I don't think we really need it there - if you pass us a real number, it wouldn't matter. If you pass us garbage, that will shake out when we finally decide what value it needs to be.

We are doing a second max() call when we already compared these values before. Is that necessary?

I'm not sure what you mean.

All of this feels a little...repetitive. Is there something we can do that's just a little more systemic so that we aren't repeating ourselves all the time? Maybe something from here: https://laravel.com/docs/9.x/pagination ?

As discussed on the call this AM, yes and no. Bootstrap-table requires our data to exist in a specific format (which is why we have all of those API transformers, etc.) It's possible we can get the best of both worlds - it's something I've been thinking about for a while. This PR is just meant to be a middle-step so shitheels who bought Burpsuite for Dummies don't poop all over our logs, but it's very possible that there's a better longer term solution possible here.

Laravel is a PHP web application framework with expressive, elegant syntax. We’ve already laid the foundation — freeing you to create without sweating the small things.

@snipe
Copy link
Owner Author

snipe commented Apr 13, 2023

An alternative to this PR would be a piece of middleware (or in a provider or error handler) that simply rejects limit and offset in any request if their values are not greater than 0 and are not numbers.

@marcusmoore
Copy link
Collaborator

marcusmoore commented Apr 13, 2023

An alternative to this PR would be a piece of middleware (or in a provider or error handler) that simply rejects limit and offset if their values are not greater than 0 and are not numbers.

Hm...I like that idea a lot...

@snipe
Copy link
Owner Author

snipe commented Apr 15, 2023

I'm going to merge this into develop now and keep testing

@snipe snipe merged commit a97b2d1 into develop Apr 15, 2023
3 checks passed
@snipe snipe deleted the fixes/tighter_handling_of_offset_and_limit branch April 15, 2023 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants