Learning path · Level 11 · Tutorial 1

Protect Sensitive Data at the Browser Boundary

Review a front-end feature for exposed private credentials and unnecessary sensitive data, and identify which operations require a server-controlled boundary.

  • Level: Intermediate
  • Duration: 12 min
  • 7 steps
Start tutorial Back to the path
Protect Sensitive Data at the Browser Boundary

What you will go through

  1. Identify What the Browser Receives Identify delivered code and data as browser-visible to the user, even when the interface does not render every value. 1 min
  2. Classify Values Before Handling Them Classify Parcel Desk values as intentionally public configuration, private credentials, or sensitive user data according to their purpose. 1 min
  3. Recognize Client Environment Variables as Public Configuration See why Vite client environment variables are public configuration rather than a place for private credentials. 2 min
  4. Find Unnecessary Copies in URLs and Messages Reduce unnecessary sensitive-data copies in Parcel Desk URLs, diagnostics, and error messages. 2 min
  5. Minimize Delivered and Retained Data Specify the smallest shipment-status record for the browser, then distinguish reduced retention from preventing unnecessary delivery. 2 min
  6. Move Private-Key Operations Behind a Server Boundary Redesign Parcel Desk shipment-status access so a private provider credential stays behind an application-controlled server operation. 2 min
  7. Apply the Browser-Boundary Review Apply an integrated browser-boundary review to a flawed Parcel Desk shipment-status feature and propose a minimal safer flow. 2 min

What you will learn

  • Classify example configuration values as intentionally public, private credentials, or sensitive user data.
  • Explain why a client-exposed environment variable cannot safely hold a private service key.
  • Identify unnecessary sensitive data in URLs, console messages, and retained browser state.
  • Redesign a client-side data flow so private credentials are not delivered to the browser.

Before you start

  • Run a Local Development Server with Vite
  • Save and Restore Browser Preferences
  • Represent Detail Views and Filters in the URL

Step 1 of 7

Identify What the Browser Receives

Identify delivered code and data as browser-visible to the user, even when the interface does not render every value.

Start at the browser boundary

Delivery, not appearance

The browser is the user’s environment

Treat anything delivered to the Browser as inspectable by its user. That includes JavaScript, client configuration, and response data.

A page can choose what to render, but that choice happens after the browser has received code and data. Developer tools can reveal delivered resources and network responses even when the interface never displays every value.

What crosses into the Browser

A data-flow diagram shows the Shipping provider, Application server, and Browser. The Browser receives JavaScript, client configuration, and a full response, while its visible page shows only selected response fields.

The browser boundary is what enters the Browser—not only what appears in the page.

Look beyond the visible interface

Undisplayed values still arrive

A selected display is not a filtered delivery

Suppose Parcel Desk renders only a shipment ID and its current status. If the response includes additional fields, the browser has already received those fields before the interface selects what to show.

The same principle applies to a value inside delivered JavaScript: the absence of a visible page element does not make the delivered value unavailable to the browser user.

Response record

Response received by the Browser; the interface renders only shipmentId and status.

json
{
  "shipmentId": "ship-1042",
  "status": "In transit",
  "recipientEmail": "avery@example.test",
  "deliveryAddress": "14 Example Lane"
}

Delivered JavaScript

This statement is part of delivered JavaScript even if no visible interface element displays its value.

typescript
const shipmentId = "ship-1042";

Check the boundary

Check 1

A response field is concealed from the browser user when the interface does not render it.

Check 2

A value inside delivered JavaScript is concealed from the browser user if no page element displays it.

Check 3

When reviewing the browser boundary, delivery into the Browser matters more than whether a value is visible on the page.

Step 2 of 7

Classify Values Before Handling Them

Classify Parcel Desk values as intentionally public configuration, private credentials, or sensitive user data according to their purpose.

Start with the value’s role

Three classifications

Classify by purpose, not appearance

Before Parcel Desk handles a value, ask two questions:

  1. What is this value designed to enable?
  2. Does the browser need it to perform its legitimate task?

Use these three classifications:

  • Intentionally public configuration: a value explicitly designed to be delivered to the client, such as a route base path or public application label.
  • Private credential: a value that grants privileged access to a service. It must stay outside the browser.
  • Sensitive user data: personal information about a person. A feature may need it, but delivery and retention should be limited to the specific task.

A value is not safe or unsafe because it looks like an ID, contains the word “key,” or is hidden by the interface. Its documented role and capabilities determine its classification.

Purpose determines handling

A three-part diagram distinguishes browser-safe public configuration, a private credential held behind a server, and personal recipient data minimized for a feature.

Classify each value by what it enables and whether the browser genuinely needs it.

Apply the categories consistently

Parcel Desk examples

Parcel Desk can classify common feature values like this. The API base path and client application ID are deliberately public labels for the client. The shipping-service key would authorize privileged provider access. Recipient details describe a person and should be sent or kept only when the interface has a specific need.

Tip

Do not classify by spelling

The name alone is not evidence. A provider may designate an identifier as publishable, but that does not make every key-like value browser-safe. Conversely, a public application ID is not a credential merely because it identifies an app. Check the value’s documented capability: if it can authorize privileged service actions, treat it as a private credential.

Value classification reference

Examples used by the shipment-status feature

Classifications follow intended exposure and capability.
ValuePurposeClassification
VITE_API_BASE_URL=/apiClient route base pathIntentionally public configuration
VITE_CLIENT_APP_ID=parcel-desk-webPublic client application labelIntentionally public configuration
DEMO_PRIVATE_KEY_NOT_REALPrivileged shipping-provider accessPrivate credential
recipientEmail: "avery@example.test"Recipient contact detailSensitive user data
deliveryAddress: "14 Example Lane"Recipient location detailSensitive user data

Check your classification

Classify the feature values

Match each Parcel Desk value and purpose to its classification.

Tap an item and then its matching pair.

Step 3 of 7

Recognize Client Environment Variables as Public Configuration

See why Vite client environment variables are public configuration rather than a place for private credentials.

Client exposure follows the value

A client reference makes exposure possible

When Parcel Desk client code references a Vite variable whose name starts with VITE_, Vite makes that value available through import.meta.env. A referenced value can then become part of the application delivered to the Browser.

The browser user can inspect delivered JavaScript, network requests, and runtime values. It does not matter whether the interface renders the value. A private credential must therefore not be used by browser code.

Unsafe example: a client-exposed key reaches the browser

A diagram shows a VITE-prefixed private key moving from a local environment file through a build process into browser-visible code and a request header.

A file location does not change the boundary: once client code uses the value, it can reach the Browser.

Tip

Boundary check

Treat the browser as user-controlled. “Not shown in the UI” is not the same as “not delivered to the user.”

Git exclusion does not make a client key private

Unsafe example: exclusion is not a delivery boundary

This setup has two separate concerns: Git exclusion and client delivery. The .gitignore entry can prevent an environment file from being committed. It does not prevent Vite from using a referenced VITE_ value while producing code for the browser.

.env.local — Unsafe example

Local environment configuration. The third line is unsafe because it is a private credential exposed to client code.

dotenv
VITE_API_BASE_URL=/api
VITE_CLIENT_APP_ID=parcel-desk-web
VITE_SHIPPING_SERVICE_KEY=DEMO_PRIVATE_KEY_NOT_REAL

.gitignore

Git exclusion is a separate file-level rule.

gitignore
.env.local

Warning

Keep the questions separate

.env.local may not be served as a file at a URL, and it should still be excluded from Git. But neither fact protects DEMO_PRIVATE_KEY_NOT_REAL if browser code reads import.meta.env.VITE_SHIPPING_SERVICE_KEY.

Git exclusion addresses repository inclusion. A server-controlled boundary concerns where an operation and its private credential can run.

Public configuration is not a secret mechanism

Unreadable-looking code is still browser code

Minification or obfuscation can make generated JavaScript less convenient to read, but the browser must still receive enough information to run it. A user can inspect runtime behavior and requests; obscuring code does not establish a private credential boundary.

With Vite's default behavior, removing VITE_ means import.meta.env.SHIPPING_SERVICE_KEY is not exposed to ordinary client code. That does not turn the client into a server or make a browser-dependent private-key operation safe. It only removes the client value that the design was trying to use.

Unsafe example: client code reads the key

This reference is an unsafe example because it makes browser code depend on a private credential.

typescript
const shippingServiceKey = import.meta.env.VITE_SHIPPING_SERVICE_KEY;

const providerRequestHeaders = {
  Authorization: `Bearer ${shippingServiceKey}`,
};

Apply the browser-boundary review

Complete the public configuration

Complete the Parcel Desk client configuration using only the designated public values.

dotenv
VITE_API_BASE_URL=
VITE_CLIENT_APP_ID=

Review the unsafe configuration

Write a revised .env.local excerpt containing only the two intentionally public configuration lines for Parcel Desk. Then explain why neither adding .env.local to .gitignore nor minifying generated JavaScript would protect DEMO_PRIVATE_KEY_NOT_REAL if client code read it through import.meta.env.VITE_SHIPPING_SERVICE_KEY.

Write at least 180 characters (0/180).

Step 4 of 7

Find Unnecessary Copies in URLs and Messages

Reduce unnecessary sensitive-data copies in Parcel Desk URLs, diagnostics, and error messages.

URLs create copies

Keep URLs purpose-limited

A value in a feature URL is easy to copy beyond the immediate request. If Parcel Desk requests status with recipient details in the query string, those details can be retained in browser history, copied with the link, or recorded by systems that record request URLs.

The status view needs the shipment identifier. It does not need the recipient email or delivery address in its URL. Use the purpose-limited application contract instead:

GET /api/shipments/ship-1042/status

Removing unnecessary query values reduces extra copies. It does not make a value secret if that value was already delivered to the browser.

Comparison showing personal data in a URL creating copies in browser history, copied links, and request records, while a purpose-limited request carries only a shipment identifier.

A sensitive query value can create multiple unnecessary copies after it is placed in a URL.

Review diagnostics and errors

Make messages useful, not exhaustive

Diagnostics should answer a specific question, not capture every available value. Logging an entire configuration object can expose a private credential; logging a full provider record can expose personal fields. A user-facing error should help the user recover without echoing raw upstream details.

Choose reviewed diagnostic fields such as the shipment identifier and a short event description. Keep raw provider errors and complete records out of browser messages unless a reviewed, necessary purpose requires a field.

Unsafe example and safer design

Compare the unsafe example with a safer design. The safer diagnostic identifies the failed feature request without copying configuration or the complete provider response.

typescript
// Unsafe example
console.error("Status request failed", import.meta.env);
console.error("Provider failure", providerResponse);
setError(providerError.message);

// Safer design
console.warn("Shipment status request failed", { shipmentId });
setError("We could not load the shipment status. Try again.");

Tip

A generic recovery message is not a claim that the underlying failure is harmless. It simply avoids reflecting provider details, sensitive request data, or internal implementation information into the interface.

Apply the message-and-URL review

Review the three copies

Parcel Desk currently uses all three unsafe patterns below:

  • GET /api/shipments/ship-1042/status?recipientEmail=avery@example.test&deliveryAddress=14%20Example%20Lane
  • console.error("Status request failed", import.meta.env);
  • setError(providerError.message);

Which replacement keeps the required navigation or feedback while removing unnecessary personal data and private-configuration exposure?

Step 5 of 7

Minimize Delivered and Retained Data

Specify the smallest shipment-status record for the browser, then distinguish reduced retention from preventing unnecessary delivery.

Define the minimal response

Send only what the view needs

A shipment-status view needs a small browser-facing record: the shipment identifier and its status. The full provider record may contain sensitive user data, but recipientEmail and deliveryAddress are not needed to show this screen.

Minimize delivery at the browser boundary. It is not enough to hide unnecessary fields after they arrive: a browser-facing response should already contain only the fields the feature needs.

Browser-facing response comparison

A comparison shows an unsafe browser response containing shipment ID, status, email, and address, beside a safer browser response containing only shipment ID and status.

The status view needs the compact record, not the recipient details.

Unsafe example — full record reaches the Browser

json
{
  "shipmentId": "ship-1042",
  "status": "In transit",
  "recipientEmail": "avery@example.test",
  "deliveryAddress": "14 Example Lane"
}

Safer design — minimal browser response

json
{
  "shipmentId": "ship-1042",
  "status": "In transit"
}

Separate delivery from later copies

Filtering afterward is too late

Filtering a record in client code can reduce later copies in component state or rendering, but it happens after every field in the original record has crossed into the Browser.

Likewise, omitting a field from JSX does not remove it from the response object, a state object, or a cached record. Treat the delivered response—not the visible screen—as the relevant boundary.

Unsafe example — client-side filtering after delivery

This creates a smaller later copy, but the full provider record has already been delivered to the browser.

typescript
const providerRecord = {
  shipmentId: "ship-1042",
  status: "In transit",
  recipientEmail: "avery@example.test",
  deliveryAddress: "14 Example Lane"
};

const statusView = {
  shipmentId: providerRecord.shipmentId,
  status: providerRecord.status
};

Warning

Boundary reminder

Do not mistake a smaller client-side object for a delivery control. It improves what is retained next, but it cannot undo the initial exposure of the full record.

Reduce retained data

Keep a small, short-lived snapshot

Retain a reviewed snapshot only when the feature needs it, and only for the needed period. A status view can hold shipmentId and status; it does not need to retain the recipient record by default.

In-memory state is not secret storage. Clearing a targeted value reduces ongoing retention, but cannot erase copies previously captured elsewhere.

Clear reviewed feature state

If this feature previously stored a reviewed snapshot under the named key, remove that targeted entry when its purpose ends.

typescript
type ShipmentStatusSnapshot = {
  shipmentId: string;
  status: string;
};

let lastShipment: ShipmentStatusSnapshot | null = {
  shipmentId: "ship-1042",
  status: "In transit"
};

function clearShipmentView(): void {
  lastShipment = null;
  localStorage.removeItem("parcel-desk:last-shipment");
}

Warning

Retention limit

Clearing targeted state limits future use of that copy. It does not retroactively remove data from places where it may already have been observed or recorded.

Apply the two decisions

Check the boundary and retention

A full provider record already reached the Browser before this code runs. Complete the minimal retained snapshot and the two boundary statements.

typescript
const lastShipment = {
  "": "ship-1042",
  "": "In transit"
};

const reducesRetention = ;
const preventsExtraDelivery = ;

Step 6 of 7

Move Private-Key Operations Behind a Server Boundary

Redesign Parcel Desk shipment-status access so a private provider credential stays behind an application-controlled server operation.

Move the privileged call

Put the credential behind the boundary

A private credential must not cross into the Browser. If Parcel Desk needs a shipping-provider operation that requires one, change the caller—not just the user interface.

In the safer flow, the Browser asks the Application server for a shipment-status capability. The Application server holds the server configuration, uses the private credential when it contacts the Shipping provider, and keeps that credential out of the delivered application.

Unsafe example versus Safer design

A comparison diagram: in the unsafe example, Browser sends a private key to Shipping provider. In the safer design, Browser requests status from Application server, which alone uses the key with Shipping provider and returns a small status record.

The private credential belongs only on the Application server to Shipping provider interaction.

Define a narrow server operation

Return only the feature result

Expose a defined feature capability, not a credential endpoint and not an unrestricted provider proxy. For this view, the illustrative capability is GET /api/shipments/ship-1042/status.

The Application server can obtain a broader provider result, but it should map that result to the minimal record needed by the Browser. It should also return a useful feature error rather than forwarding raw provider details.

Safer design response contract

Minimal browser-facing success response

json
{
  "shipmentId": "ship-1042",
  "status": "In transit"
}

Warning

Boundary check

This route is an illustrative design contract, not an available backend. A browser-only TypeScript helper or a local in-memory adapter still runs under browser control; neither creates a server-controlled boundary. The real server operation also needs validation and access controls, whose detailed design is outside this tutorial.

Check the design

Choose the boundary

Which design creates a server-controlled boundary for the Parcel Desk shipment-status operation?

Apply the complete flow

Build the safer flow

Arrange the safer Parcel Desk interactions from first to last.

  1. Application server returns { "shipmentId": "ship-1042", "status": "In transit" } to Browser.
  2. Application server uses server-held SHIPPING_SERVICE_KEY to request shipment status from Shipping provider.
  3. Shipping provider returns its result to Application server.
  4. Browser requests GET /api/shipments/ship-1042/status from Application server.

Step 7 of 7

Apply the Browser-Boundary Review

Apply an integrated browser-boundary review to a flawed Parcel Desk shipment-status feature and propose a minimal safer flow.

Set up the integrated review

Use one review path

A browser-boundary review follows one feature end to end—not just what appears in its interface. For Parcel Desk, trace each value through configuration, client code, request URLs, returned records, diagnostic messages, and retained browser state.

For every value, ask:

  1. What reaches the Browser?
  2. Why does the feature need it?
  3. Where is it copied or displayed?
  4. How long is it retained?
  5. Does an operation require a server-controlled boundary?

The review should produce concrete changes: keep intentionally public configuration available, remove unnecessary sensitive user data, and keep private credentials outside the Browser.

Trace every browser-visible copy

A review path traces a feature value from configuration through browser code, URLs, responses, messages, and retained state.

Review the whole path. A value can be unnecessary even when it is not rendered in the interface.

Tip

Review the complete feature

Do not treat a single cleanup as a complete fix. Removing a personal field from a URL, for example, reduces one copy; it does not change data that was already delivered in a response or stored in browser state. A complete review records each remaining browser-visible copy and its purpose.

Review the flawed shipment-status feature

Unsafe example: .env.local

dotenv
VITE_API_BASE_URL=/api
VITE_CLIENT_APP_ID=parcel-desk-web
VITE_SHIPPING_SERVICE_KEY=DEMO_PRIVATE_KEY_NOT_REAL

Unsafe example: browser feature excerpt

This feature excerpt, together with the flow diagram, is the evidence for your review.

typescript
const shipmentId = "ship-1042";
const recipientEmail = "avery@example.test";
const deliveryAddress = "14 Example Lane";
const serviceKey = import.meta.env.VITE_SHIPPING_SERVICE_KEY;

const requestUrl =
  `https://shipping-provider.example.test/status?shipmentId=${shipmentId}&recipientEmail=${recipientEmail}`;

console.error("Shipment request failed", {
  serviceKey,
  recipientEmail,
  deliveryAddress,
});

const shipment = {
  shipmentId,
  status: "In transit",
  recipientEmail,
  deliveryAddress,
};

localStorage.setItem(
  "parcel-desk:last-shipment",
  JSON.stringify(shipment),
);

Unsafe example flow

An unsafe Parcel Desk flow sends a direct request from Browser to Shipping provider while the browser contains configuration, URL, console, and local-storage copies.

Use the code and this flow to trace what reaches the Browser and where copies accumulate.

Final application: review and redesign

Write a concise five-part review of the unsafe Parcel Desk feature.

  1. Classify all three configuration values.
  2. Explain why the Vite-read value creates an exposure.
  3. Identify the unnecessary sensitive copies in the URL, diagnostic message, and retained state.
  4. State which values the shipment-status interface actually needs.
  5. Propose a revised flow: name the browser request, the component that contacts the Shipping provider, where the service key belongs, and the minimal JSON response returned to the Browser.

Use the fixed terms from this tutorial where they apply.

Write at least 300 characters (0/300).

Consolidate the decisions

Summary

Browser-boundary review checklist

  • Treat JavaScript, client configuration, and response data delivered to the Browser as inspectable—even when the interface does not show them.
  • Classify by purpose and capability: intentionally public configuration may reach the client; a private credential must not; sensitive user data should be limited to the task.
  • Trace URLs, diagnostics, responses, and retained state for unnecessary copies of sensitive values.
  • A server-controlled boundary performs the provider operation that needs the private credential and returns only the fields the Browser needs.

Step complete

Congratulations! You completed: Protect Sensitive Data at the Browser Boundary

You can now review a browser feature for exposed credentials, unnecessary sensitive data, and operations that require a server-controlled boundary.

20 XP

Download the App now to have access to + 5000 free courses, exercises, certificates and lots of content without paying anything!

  • 100% free online courses from start to finish

    Thousands of online courses in video, ebooks and audiobooks.

  • More than 60 thousand free exercises

    To test your knowledge during online courses

  • Valid free Digital Certificate with QR Code

    Generated directly from your cell phone's photo gallery and sent to your email

Cursa app on the ebook screen, the video course screen and the course exercises screen, plus the course completion certificate