Unsafe Cookie
ID |
swift.unsafe_cookie |
Severity |
high |
Resource |
Misconfiguration |
Language |
Swift |
Tags |
CWE:1004, CWE:315, CWE:539, CWE:614, NIST.SP.800-53, OWASP:2021:A05, PCI-DSS:6.5.10 |
Description
Unsafe cookie handling encompasses multiple vulnerabilities related to the improper management of cookies, which can lead to security issues such as disclosure of sensitive information or session hijacking.
Relevant weaknesses include improper storage (CWE-315), expired session management (CWE-539), insufficient transport security (CWE-614), and exposure to cross-site scripting risks (CWE-1004).
In iOS applications, cookies are commonly used by web services, WKWebView components, and server-side Swift frameworks like Vapor. Insecure cookie configurations can expose sensitive session data to attackers through man-in-the-middle attacks, cross-site scripting (XSS), or unauthorized access to stored cookies.
Rationale
Cookies are often used to store session identifiers and other sensitive information. Several potential vulnerabilities arise if cookies are not handled securely:
-
CWE-315: Cleartext Storage of Sensitive Information in a Cookie: Storing sensitive information in cookies without encryption can lead to unauthorized disclosure.
-
CWE-539: Use of Persistent Cookies Containing Sensitive Information: Persistent cookies that remain valid after a session can be exploited if not handled properly.
-
CWE-614: Sensitive Cookie in HTTPS Session without 'Secure' Attribute: Cookies without the Secure attribute can be transmitted over unencrypted connections, exposing them to interception.
-
CWE-1004: Sensitive Cookie without 'HttpOnly' Flag: Cookies accessible to client-side scripts can be stolen via cross-site scripting attacks.
Consider these examples illustrating unsafe practices in Swift:
Foundation HTTPCookie (iOS/macOS)
import Foundation
class CookieManager {
// VULNERABLE: Creating insecure cookie with Foundation
func createUnsafeCookie() -> HTTPCookie? {
// FLAW: Missing .secure (CWE-614) and .httpOnly (CWE-1004) flags
let properties: [HTTPCookiePropertyKey: Any] = [
.name: "sessionId",
.value: "abc123xyz",
.domain: "example.com",
.path: "/", // FLAW - too broad path
.expires: Date().addingTimeInterval(86400), // FLAW: Persistent cookie (CWE-539)
]
return HTTPCookie(properties: properties)
}
}
Vapor Framework (Server-Side Swift)
import Vapor
class VaporCookieHandler {
// VULNERABLE: Insecure cookie in Vapor
func createUnsafeCookie_vulnerable3(req: Request) throws -> Response {
let cookie = HTTPCookies.Value(
name: "SESSIONID", // FLAW: Sensitive name pattern
value: "user_session_12345",
expires: Date().addingTimeInterval(3600 * 24 * 7), // FLAW: Persistent (CWE-539)
domain: nil, // Using default domain, ok
path: "/", // FLAW: Root path
isSecure: false, // FLAW: No Secure flag (CWE-614)
isHTTPOnly: false, // FLAW: No HttpOnly flag (CWE-1004)
sameSite: nil // Missing SameSite protection, but not enforced
)
var response = Response(status: .ok)
response.cookies[cookie.name] = cookie
return response
}
// VULNERABLE: Cookie with default insecure settings
func createDefaultCookie_vulnerable4(req: Request) -> Response {
var response = Response(status: .ok)
// FLAW: Using minimal cookie configuration, defaults to insecure
// values for path: "/", secure: false, httpOnly: false
let cookie = HTTPCookies.Value(
name: "userData",
value: "sensitive_info"
)
response.cookies[cookie.name] = cookie
return response
}
}
In these examples:
-
Persistent cookies: Using
expiresormaxAgecreates persistent cookies that survive browser restarts, increasing the window for exploitation (CWE-539). -
Missing Secure flag: Cookies without
isSecure: trueor.securecan be transmitted over unencrypted HTTP connections, exposing them to interception (CWE-614). -
Missing HttpOnly flag: Cookies without
isHTTPOnly: trueor.httpOnlycan be accessed by JavaScript, making them vulnerable to XSS attacks (CWE-1004). -
Overly broad scope: Using root domain (
.com) and root path (/) exposes cookies to more requests than necessary. -
Sensitive naming: Cookie names like "SESSIONID", "SESSION", "SESSID" indicate session management cookies that require strict security.
Remediation
To secure cookies in web applications, implement the following practices:
-
Use the Secure Attribute: Always set the
Secureattribute on cookies if your application supports HTTPS. This ensures cookies are only sent over secure channels. -
Set the HttpOnly Attribute: Apply the
HttpOnlyattribute to cookies that store sensitive data, preventing access from client-side scripts and mitigating XSS risks. -
Avoid Storing Sensitive Data in Cookies: Encrypt any sensitive data stored in cookies and, where possible, avoid storing information like passwords or sensitive session data directly.
-
Manage Cookie Expirations Wisely: Use session cookies rather than persistent ones for sensitive information, ensuring they expire appropriately and reduce the risk of exploitation.
-
Regularly Audit Cookie Usage: Review cookies in use on your website to ensure best practices are consistently applied.
Here are secure implementations in Swift:
Foundation HTTPCookie (Secure Configuration)
import Foundation
class SecureCookieManager {
// SECURE: Creating secure cookie with Foundation
func createSecureCookie_secure1() -> HTTPCookie? {
let properties: [HTTPCookiePropertyKey: Any] = [
.name: "authToken",
.value: "secure_token_value",
.domain: "api.example.com", // Specific domain
.path: "/api/v1", // Specific path
.secure: true, // SECURE: Only sent over HTTPS
.init(rawValue: "HttpOnly"): true, // SECURE: Not accessible to JavaScript
// No .expires or .maximumAge = session cookie (non-persistent)
]
return HTTPCookie(properties: properties)
}
// SECURE: Session cookie with SameSite protection
func createSessionCookie_secure2() -> HTTPCookie? {
let properties: [HTTPCookiePropertyKey: Any] = [
.name: "session",
.value: UUID().uuidString,
.domain: "myapp.com",
.path: "/",
.secure: true, // HTTPS only
.init(rawValue: "HttpOnly"): true, // JavaScript cannot access
.sameSitePolicy: "Strict", // CSRF protection
// Session cookie: no expiration
]
return HTTPCookie(properties: properties)
}
}
Vapor Framework (Secure Configuration)
import Vapor
class SecureVaporCookieHandler {
// SECURE: Properly configured secure cookie in Vapor
func createSecureCookie_secure3(req: Request) throws -> Response {
let cookie = HTTPCookies.Value(
name: "sessionToken",
value: generateSecureToken(),
// No expires/maxAge = session cookie (non-persistent)
domain: req.url.host, // Specific to current host
path: req.url.path, // Specific path
isSecure: true, // SECURE: HTTPS only (CWE-614 fixed)
isHTTPOnly: true, // SECURE: XSS protection (CWE-1004 fixed)
sameSite: .strict // SECURE: CSRF protection
)
var response = Response(status: .ok)
response.cookies[cookie.name] = cookie
return response
}
// SECURE: Using Vapor's HTTPCookies builder with all security flags
func createCookieViaBuilder_secure4(req: Request) -> Response {
var response = Response(status: .ok)
response.cookies["secure_session"] = HTTPCookies.Value(
name: "secure_session",
value: generateSecureToken(),
isSecure: true, // Only HTTPS
isHTTPOnly: true, // No script access
sameSite: .lax // Balance between security and usability
)
return response
}
private func generateSecureToken() -> String {
// ... cryptographically secure random token generation
}
}
Best Practice for Foundation HTTPCookie: Add extension method to create cookies enforcing a strict configuration.
extension HTTPCookie {
/// Factory method to create secure session cookies
static func secureSessionCookie(name: String, value: String, domain: String, path: String) -> HTTPCookie? {
let properties: [HTTPCookiePropertyKey: Any] = [
.name: name,
.value: value,
.domain: domain,
.path: path,
.secure: true, // HTTPS only
.setByJavaScript: true, // No JavaScript access
.sameSitePolicy: "Lax", // some CSRF protection
// No expiration = session cookie
]
return HTTPCookie(properties: properties)
}
}
// Usage
if let cookie = HTTPCookie.secureSessionCookie(
name: "session",
value: sessionId,
domain: "api.example.com",
path: "/api"
) {
HTTPCookieStorage.shared.setCookie(cookie)
}
These secure examples demonstrate:
-
Secure flag:
isSecure: trueor.secure: trueensures cookies are only transmitted over HTTPS -
HttpOnly flag:
isHTTPOnly: trueor.httpOnly: trueprevents JavaScript access, mitigating XSS -
Session cookies: Omitting
expires/maxAgecreates non-persistent cookies that expire with the session -
SameSite attribute:
.sameSite: .strictor.laxhelps prevent CSRF attacks -
Specific scope: Using specific domain and path limits cookie exposure
-
Secure token generation: Using cryptographically secure random values
Configuration
The detector has the following configurable parameters:
-
checkPersistence, that indicates if the persistence of the cookie must be checked. -
invalidCookieNamePattern, that indicates the pattern used to detect invalid cookie names. -
invalidDomainPattern, that indicates the pattern used to detect invalid domain names. -
invalidPathPattern, that indicates the pattern used to detect invalid paths. -
enforceHttpOnly, that indicates if the HttpOnly flag of the cookie must be checked. -
enforceSecure, that indicates if the Secure flag of the cookie must be checked.
properties: checkPersistence: true invalidCookieNamePattern: '.*SESS(ION)?ID.*' invalidDomainPattern: '\.[^\.]+' invalidPathPattern: '/' enforceHttpOnly: true enforceSecure: true
This configuration checks too-broad top-level domains and paths, non-persistent cookies, and httpOnly + secure flags set. You may weaken this strict policy or mute issues for specific cases.
References
-
CWE-315 : Cleartext Storage of Sensitive Information in a Cookie.
-
CWE-539 : Use of Persistent Cookies Containing Sensitive Information.
-
CWE-614 : Sensitive Cookie in HTTPS Session without 'Secure' Attribute.
-
CWE-1004 : Sensitive Cookie without 'HttpOnly' Flag.
-
OWASP - Top 10 2021 Category A05 : Security Misconfiguration.
-
Documentation for Foundation’s HTTPCookie and HTTPCookiePropertyKey.
-
Documentation for Vapor’s HTTPCookies.Value.