flawopen.com/Cross-Site Scripting/Swift
Imagine a guestbook where visitors write a public note. Stored XSS is like someone writing a note that isn't just text — it's a hidden trick that makes the guestbook page itself start doing things, like stealing the next visitor's login session, the moment anyone opens the page to read it.
On iOS, XSS shows up when a WKWebView loads untrusted HTML via loadHTMLString() without prior sanitization. The risk compounds when the same WebView also registers a WKUserContentController message handler, giving injected script a potential path back into native Swift code.
In 2005, an 19-year-old user exploited a stored XSS flaw on MySpace to create the "Samy" worm — a script that added itself to every profile that viewed it, infecting over a million profiles within about 20 hours and forcing MySpace offline to contain it. It remains one of the most-cited demonstrations of how quickly stored XSS can self-propagate.
Source: widely documented in security industry retrospectives — see References below.// untrusted HTML loaded with no sanitization
webView.loadHTMLString(
untrustedHtml, baseURL: nil
)
// sanitize before loading
let safeHtml = sanitize(untrustedHtml)
webView.loadHTMLString(
safeHtml, baseURL: nil
)
Sanitizing the HTML string against a strict allow-list of tags and attributes before it reaches loadHTMLString() strips out any <script> tags or event-handler attributes it contains. loadHTMLString() itself performs no sanitization — it renders exactly the markup it's given, script included.
window.webkit.messageHandlers.someHandler.postMessage(...) lets page JavaScript call back into native Swift code. If untrusted script runs in the same WebView, it can potentially invoke any registered handler — scope handlers narrowly and never register one on a WebView that loads untrusted content.
Building a JS string to run via webView.evaluateJavaScript("doSomething('\(userValue)')") can let a value with an embedded quote break out of the intended JS string literal — a variant of the same class of bug, in the native-to-web direction instead of web-to-native.
Older code still targeting UIWebView (deprecated in favor of WKWebView) carries identical risk — migrating away from it is a security improvement as well as a modernization one.
Any WKWebView rendering HTML carries the same fundamental risk as a browser tab, and a registered message handler can make the impact worse than typical browser XSS.
Type safety and output-encoding safety are unrelated properties — a well-typed String can still carry an injected script.
grep -rn "loadHTMLString(" --include="*.swift" .
grep -rn "WKUserContentController\|evaluateJavaScript(" --include="*.swift" .
Same underlying shape — untrusted data mixed into a command's structure without encoding — but the target and damage differ: SQL injection targets the database, XSS targets a rendered web context, potentially with a native bridge on iOS.
Only if that URL and everything it can redirect to is trusted — restricting navigation to an explicit allow-list of origins is the safer default for any WebView that might otherwise load attacker-influenced content.