Swift on the Front Line: Why Security is Not an Option, But a Necessity
\nYou write in Swift, proud of its performance and code elegance. But have you considered how vulnerable your application is to attacks? SQL injections and XSS (Cross-Site Scripting) are not just scary stories from textbooks. They are real threats that can cost you user data, reputation, and money.
\nIn this article, we will break down how to protect a Swift application (whether it's an iOS client or a server-side Vapor app) from the two most dangerous types of attacks. No fluff — only practical examples, ready-made solutions, and live code. Let's go!
\n\nSQL Injections in Swift: How Hackers "Trick" the Database
\nImagine: a user enters '; DROP TABLE Users; -- into a text field. If you directly insert this string into an SQL query, the database will execute the delete command. A classic scenario.
In Swift, this vulnerability is most often found in server-side frameworks (Vapor, Kitura) or when working with Core Data and SQLite through unsafe wrappers.
\n\nDangerous Example: How NOT to Do It
\nimport SQLite\n\nlet username = userInput.text // "admin' OR '1'='1"\nlet query = "SELECT * FROM users WHERE name = '\\(username)'"\ntry db.execute(query) // THREAT! Compromised query\n\nThis code is a red flag. An attacker can gain access to any data or destroy the table.
\n\nThe Right Approach: Parameterized Queries
\nGolden Rule: never insert user data directly into a query string. Use parameterization.
\nimport SQLite\n\nlet db = try Connection("path/to/db.sqlite3")\nlet users = Table("users")\nlet name = Expression<String>("name")\n\nlet username = userInput.text // "admin' OR '1'='1"\nlet query = users.filter(name == username) // SAFE\nlet result = try db.prepare(query)\n\nThe SQLite.swift library escapes special characters automatically. For Vapor with Fluent, use models and filters:
\nimport Fluent\nimport Vapor\n\nstruct UserController: RouteCollection {\n func login(req: Request) async throws -> String {\n let input = try req.content.decode(LoginRequest.self)\n // Fluent automatically parameterizes the query\n let user = try await User.query(on: req.db)\n .filter(\\.$name == input.username)\n .first()\n return user?.name ?? "Not found"\n }\n}\n\nImportant: even if you use raw SQL in Vapor, apply SQLBind for parameter binding.
ORM and Core Data: The Illusion of Security
\nMany developers believe that Core Data or Fluent automatically protect against injections. This is true — if you don't use NSExpression with string formatting or raw SQL. But remember: any NSPredicate built via string interpolation is a risk.
// DANGEROUS\nlet predicate = NSPredicate(format: "name == %@", userInput) // Safe?\n// NO! If userInput contains '%', injection can occur through substitution.\n\nAlways use %@ with variables, not %K with keys, and never insert strings via \\().
XSS Attacks in Swift: When an Attacker Injects JavaScript
\nXSS is an attack where a malicious script executes in the user's browser. For iOS applications, XSS is relevant when using WKWebView to display HTML content (e.g., in news feeds or chats).
Real Scenario: Chat with HTML Formatting
\nA user writes: Hello! <script>alert('XSS')</script>. If you display this message in a WebView without processing — the script will execute.
// DANGEROUS\nlet html = "<div>\\(message.text)</div>"\nwebView.loadHTMLString(html, baseURL: nil)\n