Path Traversal
ID |
swift.path_traversal |
Severity |
critical |
Resource |
Path Resolution |
Language |
Swift |
Tags |
CWE:22, CWE:73, NIST.SP.800-53, OWASP:2021:A4, OWASP:2021:A5, PCI-DSS:6.5.8 |
Description
Improper limitation of a pathname to a restricted directory.
Path Traversal vulnerabilities exploit improper validation of user inputs when constructing file paths. Attackers can manipulate input to navigate the directory structure and access files outside the intended file directory. This typically involves injecting special characters such as ../, which, when processed, traverse the directory hierarchy.
Rationale
Allowing external input to control paths used in file system operations could allow an attacker to access or modify unintended files.
If the software concatenates external input into a path used during file operations, an attacker could "escape" from the directory reserved for such operations. Depending on the file operations performed by the software, an attacker could read or write arbitrary files, including sensitive application or operating system files, with the permissions granted to the process running the software.
Threat actors can:
-
Exfiltrate sensitive files from the software or the operating system.
-
Modify application configuration files for removing security controls and gaining further access.
-
Upload executable code (typically forcing execution of the uploaded code), which could install malware, open a reverse shell, or perform other malicious actions.
In Swift, a susceptible code example might look like this:
import Foundation
func readUserFile(request: URLRequest) throws -> String {
guard let url = request.url,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems,
let filename = queryItems.first(where: { $0.name == "file" })?.value else {
throw FileError.invalidRequest
}
// FLAW: User-controlled filename used directly in file path
let basePath = "/var/app/files/"
let filePath = basePath + filename
return try String(contentsOfFile: filePath, encoding: .utf8)
}
In this instance, the filename parameter from the URL query is directly concatenated into a file path. An attacker could provide a value like ../../../etc/passwd to traverse outside the intended directory and read sensitive system files.
Remediation
To protect against Path Traversal vulnerabilities in applications, consider the following remediation strategies:
-
Canonicalize the Path: Normalize file paths before processing them using file functions. This ensures that any navigational characters are resolved and the path refers to the correct location. Perform any checks on path after canonicalization.
-
Whitelisting: Maintain a whitelist of allowed file names or extensions that users can access, rejecting any requests for files not in the whitelist.
-
Input Validation: Validate incoming parameters rigorously. Reject or safely encode inputs containing harmful patterns, such as
.., or control characters. -
Least Privilege: Ensure that applications run with the least privilege necessary. Restrict file permissions to prevent unauthorized file access even if paths are manipulated.
-
Security Audits and SAST: Conduct regular security audits and integrate Static Application Security Testing tools to identify and mitigate Path Traversal vulnerabilities early during development.
By implementing these preventive measures, you can significantly reduce the risk of Path Traversal vulnerabilities, safeguarding your application against unauthorized file access and potential data breaches.
Here is the revised, secure code example:
import Foundation
func readUserFile(request: URLRequest) throws -> String {
guard let url = request.url,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems,
let filename = queryItems.first(where: { $0.name == "file" })?.value else {
throw FileError.invalidRequest
}
// FIXED: Validate and sanitize the filename
// 1. Remove path traversal sequences
let sanitized = filename.replacingOccurrences(of: "..", with: "")
.replacingOccurrences(of: "/", with: "")
// 2. Build the full path using URL for proper path handling
let baseURL = URL(fileURLWithPath: "/var/app/files/")
let fileURL = baseURL.appendingPathComponent(sanitized)
// 3. Ensure the resolved path is still within the base directory
let canonicalBase = baseURL.standardized.path
let canonicalFile = fileURL.standardized.path
guard canonicalFile.hasPrefix(canonicalBase) else {
throw FileError.pathTraversal
}
// 4. Use the validated path
return try String(contentsOf: fileURL, encoding: .utf8)
}
Alternatively, use a whitelist approach with allowed filenames:
import Foundation
func readUserFile(request: URLRequest) throws -> String {
guard let url = request.url,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let queryItems = components.queryItems,
let filename = queryItems.first(where: { $0.name == "file" })?.value else {
throw FileError.invalidRequest
}
// FIXED: Use whitelist of allowed files
let allowedFiles = ["report.pdf", "summary.txt", "data.json"]
guard allowedFiles.contains(filename) else {
throw FileError.fileNotAllowed
}
let baseURL = URL(fileURLWithPath: "/var/app/files/")
let fileURL = baseURL.appendingPathComponent(filename)
return try String(contentsOf: fileURL, encoding: .utf8)
}
When working with FileManager, always validate paths:
import Foundation
func deleteUserFile(filename: String, from basePath: String) throws {
let fileManager = FileManager.default
// Sanitize the filename
let sanitized = (filename as NSString).lastPathComponent
// Build and validate the path
let baseURL = URL(fileURLWithPath: basePath).standardized
let fileURL = baseURL.appendingPathComponent(sanitized).standardized
// Ensure the file is within the base directory
guard fileURL.path.hasPrefix(baseURL.path) else {
throw FileError.pathTraversal
}
// FIXED: Use validated path
try fileManager.removeItem(at: fileURL)
}
Configuration
The detector has the following configurable parameters:
-
sources, that indicates the source kinds to check. -
neutralizations, that indicates the neutralization kinds to check.
Unless you need to change the default behavior, you typically do not need to configure this detector.
References
-
CWE-22 : Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
-
CWE-73 : External Control of File Name or Path.
-
OWASP - Top 10 2021 Category A04 : Insecure Design.
-
NSString.standardizingPath - Returns a string with path components resolved.
-
FileManager - Swift’s interface to the file system.