Back to Blog
SecurityTechnical Note

Keeping Raw Data Behind a Flask Dashboard

A monthly sales chart needed totals. Its API was sending individual records. I moved the selection and aggregation into Flask and SQL.

February 18, 20193 min read
FlaskSQLDashboardsData SecurityParameterized QueriesAccess Control

I checked the network responses behind a dashboard and found more data than the charts needed. Some routes returned full records even when the interface only displayed totals.

Give the chart its own response

Hiding a field in the interface leaves it in the response. Anyone with access to the page can inspect that response, including rows the chart never displays.

I gave each view a narrower response. The sales summary groups orders by month and returns the order count and gross revenue. Flask sends those values as a chart series, without the individual orders.

Authenticated Flask sales-summary route passing the session user and requested dates to a query helper.
The sales-summary route takes the user from the session and the date range from the request.

Scope before grouping

The SQL filters by account and date before grouping. That order matters: aggregating everyone's rows and filtering the result afterward can include another account's totals.

SQL query grouping records by month with user and date filters.
The account and date filters apply to the rows that feed the monthly totals.

The route shows a session user; the query expects an account ID. Resolving which account that user may access is a separate authorization check. These excerpts don't show that mapping.

Keep filter values out of the SQL string

The category query uses named parameters for the account, category, and date range. The database driver binds those values without treating them as query syntax.

Parameterized SQL query using bound values for dashboard filters.
The query structure stays fixed while the driver binds the filter values.

Those values still need validation: valid dates, a sensible range, and a category the endpoint accepts. Parameter binding handles SQL syntax; it doesn't decide whether a request makes sense. The shown helper doesn't include those checks.

For this view, the response can stay small: month, order count, and gross revenue. A future drill-down would need its own scoped endpoint rather than adding all the source rows back to the summary.

Have a similar project?

Talk to Me