---
title: "Key Web Vulnerabilities #6, Part 1: Path Traversal Is Not About Browsing Folders"
url: https://daily.dev/posts/key-web-vulnerabilities-6-part-1-path-traversal-is-not-about-browsing-folders-uhirkgj6o
source_url: https://daily.dev/posts/key-web-vulnerabilities-6-part-1-path-traversal-is-not-about-browsing-folders-uhirkgj6o
type: freeform
source: "Przemyslaw"
author: "Przemyslaw"
published: 2026-06-18T19:11:16.132Z
updated: 2026-06-18T19:11:38.599Z
tags: ["appsec", "web-security"]
reading_time: 6
upvotes: 0
comments: 0
language: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Key Web Vulnerabilities #6, Part 1: Path Traversal Is Not About Browsing Folders

**[Przemyslaw](https://daily.dev/sources/zijg9d2axrppyx4snlnev)** · [@przemyslaw91](https://daily.dev/przemyslaw91) · 6 min read · 0 upvotes · 0 comments

## Summary

Path Traversal vulnerabilities occur when user-controlled input influences server-side file paths without proper validation of the resolved path. Using a simple image-loading endpoint as an example, the core issue is that parameters like `filename=4.jpg` can be manipulated with `../` sequences to escape the intended directory and access arbitrary files like `/etc/passwd`. The safer design pattern avoids exposing filesystem paths to users entirely — instead, accepting opaque IDs that the server maps to actual file paths internally. Error messages can serve as clues about which backend subsystem processed the input. Key parameters to watch include: file, filename, path, page, lang, template, view, and download. Part 2 will cover bypasses including absolute paths, weak filtering, double URL encoding, and LFI/RFI.

## Content

**How a harmless-looking **`filename`** parameter can become server-side file access, and why the real issue is trusting user input as a path.**

After CSRF and SameSite cookies, I moved to a vulnerability that looked simple at first: Path Traversal.

At first, I thought about it as “someone puts `../` in a URL and reads `/etc/passwd`.” Technically, yes. That can happen. But that explanation is too small.

The useful model is this:

> User input influences a server-side file path, and the backend reads a file it should not expose.

The attacker is not browsing the server like they have a shell. They are making the application read a file for them.

And honestly, this topic changed how I look at boring parameters like `filename=4.jpg`.

## The innocent image request

A common vulnerable pattern starts with something completely normal:

```
GET /image?filename=4.jpg
```

The backend may do something like this:

```
app.get("/image", (req, res) => {
  res.sendFile("/var/www/images/" + req.query.filename);
});
```

From a frontend perspective, this looks harmless. We are loading an image. The parameter is called `filename`. The value ends with `.jpg`.

Nothing screams “security issue”.

But the backend is building a filesystem path from user-controlled input.

If the user sends:

```
GET /image?filename=../../../etc/passwd
```

The final path may escape the intended image directory and resolve to:

```
/etc/passwd
```

That is the mental model:

```
request parameter -> backend file logic -> filesystem -> response
```

The interesting part is not the strange-looking payload. The interesting part is that user input reached the filesystem logic.

## File name and file path are not the same thing

![4a808af6-ac03-49ed-8244-77f31ab26b2a.png](https://media.daily.dev/image/upload/s--XtzDQZ1F--/f_auto/v1781809723/ugc/content_d385c41d-f559-401e-9c60-52889af235e8?_a=BAMAMiWQ0)

This distinction helped me a lot.

A file name is just the name:

```
avatar.jpg
invoice.pdf
```

A file path describes where the file lives:

```
/var/www/images/avatar.jpg
../../config.php
/etc/passwd
```

A safer design avoids raw paths from users.

Instead of this:

```
GET /download?file=../../private/report.pdf
```

Use a safe identifier:

```
GET /download?id=report_123
```

Then the server maps that ID to a known file and checks whether the user is allowed to access it:

```
report_123 -> /app/private/reports/report-123.pdf
```

The user controls the choice, not the filesystem path.

That difference sounds small, but it is massive. One model exposes the filesystem shape to user input. The other keeps file selection inside application logic.

## Why `../` works

`../` means “go one directory up”.

So this:

```
/var/www/images/../
```

resolves to:

```
/var/www/
```

And this:

```
/var/www/images/../../../etc/passwd
```

can resolve to:

```
/etc/passwd
```

The raw string may still contain `/var/www/images/`, but the final resolved path points somewhere else.

That is why security checks must validate the resolved path, not only the original input string.

## The first lab: simple traversal

The first practical case used an image-loading endpoint:

```
GET /image?filename=4.jpg
```

Changing the filename to a random value returned:

```
No such file
```

That response was useful. It suggested the value was being used in file access logic.

Then the traversal payload worked:

```
GET /image?filename=../../../etc/passwd
```

My first reaction was not even “wow, I can read `/etc/passwd`”. It was more like: why are the basic labs this easy? Change one parameter, add a few `../` sequences, and the server gives back a system file.

Later, I realised that was the point.

The lab was simple because the vulnerable trust decision was simple:

```
user-controlled filename = safe file path
```

That was the actual bug.

The server did not need a complex exploit chain. It only needed to accept user input and pass it into file access without checking where the final path ended up.

That is also why beginner labs can feel weirdly easy. They are stripped down to expose one broken assumption. Real applications may add routing, encoding, framework behaviour, authentication, and messy business logic around it. But the core mistake can still be painfully simple.

## Errors are clues

The `No such file` message was useful because it told me something about the backend.

Different errors point toward different places:

```
No such file       -> filesystem access
SQL syntax error   -> database query
Template error     -> template rendering
Invalid include    -> file include logic
```

That does not mean every error is exploitable. But errors can reveal which subsystem received your input.

This is one of the practical things I am learning in AppSec: do not only ask whether a payload worked. Ask how the application reacted.

A boring error can be a map.

## Where I would actually test

![ca2866d3-5000-4523-849e-4380fa2df7fb.png](https://media.daily.dev/image/upload/s--Zn0IL0oL--/f_auto/v1781809826/ugc/content_6a878591-0a21-4a65-b19d-84e4b26da9cd?_a=BAMAMiWQ0)

Not every input is worth testing for Path Traversal.

A normal search request usually goes to search logic:

```
GET /search?q=test
```

Path Traversal testing makes more sense around parameters and features that suggest file access:

```
file
filename
path
page
lang
template
view
document
download
image
avatar
attachment
export
```

I also pay attention to features such as:

- image loaders;
- file downloads;
- PDF previews;
- attachments;
- language files;
- templates;
- exports and reports.

The practical question is always:

> Can user input influence which file the backend reads, loads, streams, or includes?

That question is more useful than blindly throwing `../../../etc/passwd` into every input field.

## Path Traversal is a trust-boundary problem

![3117b619-3694-47c9-88e6-b1962c1c0d32.png](https://media.daily.dev/image/upload/s--G_E5hhmW--/f_auto/v1781809843/ugc/content_b923d43f-79d9-4a64-a0f0-7510cea33afb?_a=BAMAMiWQ0)

From a frontend point of view, `filename=4.jpg` may look like harmless UI plumbing.

From a backend security point of view, it can be a trust-boundary crossing:

```
Browser input -> server filesystem
```

The browser should not be allowed to decide raw filesystem paths. It can request a resource, but the server should decide what that request maps to.

A safer flow looks like this:

```
User selects report
      ↓
Frontend sends report ID
      ↓
Backend checks ownership/permission
      ↓
Backend maps ID to stored file path
      ↓
Backend streams the file
```

The dangerous flow is:

```
User sends path
      ↓
Backend reads that path
```

That shortcut is where Path Traversal lives.

## Summary

Path Traversal is not about manually browsing folders on the server.

It is about making the application read a file on your behalf.

The bug appears when user-controlled input influences file access and the backend fails to validate the final resolved path. The payload may look like the exciting part, but the real issue is the trust decision behind it.

In Part 2, I will go into the bypasses that made this topic more interesting: absolute paths, weak filtering, double URL encoding, prefix checks, LFI/RFI, and safer Node.js path validation.

## My takeaway

Before this topic, I looked at parameters like `filename=4.jpg` as boring plumbing.

Now I see them as questions.

Where does this value go? Does it touch the filesystem? Is it a safe key or a raw path? What happens after decoding? What is the final resolved file?

That shift is probably the biggest thing I am getting from AppSec as a frontend engineer: the dangerous part is often not the weird payload. It is the ordinary parameter that quietly crosses a trust boundary.

I’m also testing a more relaxed visual style for this series. Do you like the direction? Let me know in the comments!

---

Tags: [#appsec](https://daily.dev/tags/appsec), [#web-security](https://daily.dev/tags/web-security)

[View this post on daily.dev](https://daily.dev/posts/key-web-vulnerabilities-6-part-1-path-traversal-is-not-about-browsing-folders-uhirkgj6o)
