Creation of Immutable Text Using String Concatenation
Description
Creation of Immutable Text Using String Concatenation occurs when software creates immutable text strings using repeated string concatenation operations, typically within loops. In languages where strings are immutable (like Java, C#, Python), each concatenation operation creates a new string object in memory, copying all existing content plus the new content. When performed repeatedly, especially in loops, this results in O(n²) time complexity and excessive memory allocation, creating both performance and potential security concerns.
Risk
While primarily a performance issue, this weakness can have security implications. Attackers who can trigger code paths with inefficient string concatenation may exploit this for denial-of-service attacks by causing excessive CPU usage and memory allocation. Processing large inputs through inefficient string building can exhaust server resources. In web applications, this can lead to slow response times making the system vulnerable to slowloris-style attacks. Memory pressure from excessive string objects can trigger garbage collection pauses, further degrading performance.
Solution
Use mutable string builder classes instead of concatenation: StringBuilder in Java/C#, StringBuffer for thread-safe scenarios, list joining in Python, or string streams in C++. Pre-allocate capacity when the final size is known or can be estimated. For simple cases with few concatenations, the performance impact is negligible, but always use builders in loops or when processing variable-length input. Profile string-heavy code paths to identify bottlenecks. Consider using string formatting or interpolation where appropriate.
Common Consequences
| Impact | Details |
|---|---|
| Availability | Scope: Availability DoS: Resource Consumption (CPU) - Repeated string concatenation in loops causes O(n²) time complexity, consuming excessive CPU cycles. |
| Availability | Scope: Availability DoS: Resource Consumption (Memory) - Each concatenation creates new string objects, potentially exhausting memory. |
| Other | Scope: Other Reduce Performance - General performance degradation that may enable denial-of-service attacks. |
Example Code
Vulnerable Code
// Vulnerable: String concatenation in loop (Java)
public class VulnerableStringBuilder {
public String vulnerableBuildHTML(List<String> items) {
String html = "<ul>";
// Vulnerable: Each += creates a new String object
// O(n²) complexity: for n items, copies approximately n²/2 characters
for (String item : items) {
html += "<li>" + item + "</li>"; // Creates multiple new strings
}
html += "</ul>";
return html;
}
// With 10,000 items, this creates ~50 million character copies
// Memory usage spikes during execution
}
// Vulnerable: String concatenation in C#
public class VulnerableProcessor
{
public string VulnerableBuildReport(DataTable data)
{
string report = "Report:\n";
// Vulnerable: Immutable string concatenation
foreach (DataRow row in data.Rows)
{
// Each line creates multiple new string objects
report += "ID: " + row["id"] + ", ";
report += "Name: " + row["name"] + ", ";
report += "Value: " + row["value"] + "\n";
}
return report;
}
}
# Vulnerable: String concatenation in Python
def vulnerable_build_csv(records):
csv_data = ""
# Vulnerable: Python strings are immutable
# Each += creates a new string object
for record in records:
line = ""
for field in record:
line += str(field) + "," # Multiple allocations per field
line = line[:-1] # Remove trailing comma
csv_data += line + "\n" # More allocations
return csv_data
# Processing 100,000 records becomes extremely slow
// Vulnerable: String concatenation in JavaScript
function vulnerableBuildJSON(items) {
let json = '{"items":[';
// Vulnerable: Inefficient in tight loops with many items
for (let i = 0; i < items.length; i++) {
json += '{"id":' + items[i].id + ',';
json += '"name":"' + items[i].name + '"}';
if (i < items.length - 1) {
json += ',';
}
}
json += ']}';
return json;
}
// Vulnerable: String concatenation in Go
func vulnerableBuildLog(entries []LogEntry) string {
result := ""
// Vulnerable: Go strings are immutable
// Each concatenation allocates new memory
for _, entry := range entries {
result += fmt.Sprintf("[%s] %s: %s\n",
entry.Timestamp, entry.Level, entry.Message)
}
return result
}
Fixed Code
// Fixed: Using StringBuilder in Java
public class FixedStringBuilder {
public String fixedBuildHTML(List<String> items) {
// Fixed: StringBuilder is mutable, O(n) complexity
StringBuilder html = new StringBuilder();
// Pre-allocate capacity if size is known
// Estimate: ~20 chars per item + wrapper
html.ensureCapacity(items.size() * 25 + 10);
html.append("<ul>");
for (String item : items) {
html.append("<li>")
.append(escapeHtml(item)) // Also fix XSS!
.append("</li>");
}
html.append("</ul>");
return html.toString();
}
// Thread-safe version using StringBuffer
public String fixedBuildHTMLThreadSafe(List<String> items) {
StringBuffer html = new StringBuffer();
html.append("<ul>");
for (String item : items) {
synchronized (html) {
html.append("<li>")
.append(escapeHtml(item))
.append("</li>");
}
}
html.append("</ul>");
return html.toString();
}
// Modern Java: String.join for simple cases
public String fixedBuildList(List<String> items) {
return String.join(", ", items);
}
}
// Fixed: Using StringBuilder in C#
public class FixedProcessor
{
public string FixedBuildReport(DataTable data)
{
// Fixed: StringBuilder with estimated capacity
var report = new StringBuilder(data.Rows.Count * 100);
report.AppendLine("Report:");
foreach (DataRow row in data.Rows)
{
report.Append("ID: ").Append(row["id"]).Append(", ");
report.Append("Name: ").Append(row["name"]).Append(", ");
report.Append("Value: ").Append(row["value"]).AppendLine();
}
return report.ToString();
}
// Alternative: Using string interpolation with StringBuilder
public string FixedBuildReportInterpolated(DataTable data)
{
var report = new StringBuilder();
foreach (DataRow row in data.Rows)
{
report.AppendLine($"ID: {row["id"]}, Name: {row["name"]}, Value: {row["value"]}");
}
return report.ToString();
}
}
# Fixed: Using list join in Python
def fixed_build_csv(records):
# Fixed: Build list of lines, then join once
lines = []
for record in records:
# Use join for fields within each line
line = ",".join(str(field) for field in record)
lines.append(line)
# Single join operation at the end
return "\n".join(lines)
# Alternative: Using StringIO for stream-like building
from io import StringIO
def fixed_build_csv_stringio(records):
buffer = StringIO()
for record in records:
buffer.write(",".join(str(field) for field in record))
buffer.write("\n")
return buffer.getvalue()
# Best for CSV: Use the csv module
import csv
from io import StringIO
def fixed_build_csv_proper(records):
buffer = StringIO()
writer = csv.writer(buffer)
writer.writerows(records)
return buffer.getvalue()
// Fixed: Using Array.join in JavaScript
function fixedBuildJSON(items) {
// Fixed: Build array, join once
const parts = items.map(item =>
`{"id":${item.id},"name":"${escapeJson(item.name)}"}`
);
return `{"items":[${parts.join(',')}]}`;
}
// Better: Use JSON.stringify
function fixedBuildJSONProper(items) {
return JSON.stringify({ items: items });
}
// For HTML building
function fixedBuildHTML(items) {
const listItems = items.map(item =>
`<li>${escapeHtml(item)}</li>`
);
return `<ul>${listItems.join('')}</ul>`;
}
// Template literals for complex building
function fixedBuildReport(data) {
const rows = data.map(row => `
<tr>
<td>${escapeHtml(row.id)}</td>
<td>${escapeHtml(row.name)}</td>
</tr>
`);
return `<table>${rows.join('')}</table>`;
}
// Fixed: Using strings.Builder in Go
import (
"strings"
"fmt"
)
func fixedBuildLog(entries []LogEntry) string {
// Fixed: strings.Builder is efficient for string building
var builder strings.Builder
// Pre-grow buffer if size is known
builder.Grow(len(entries) * 100)
for _, entry := range entries {
fmt.Fprintf(&builder, "[%s] %s: %s\n",
entry.Timestamp, entry.Level, entry.Message)
}
return builder.String()
}
// Alternative: Using bytes.Buffer
import "bytes"
func fixedBuildLogBuffer(entries []LogEntry) string {
var buffer bytes.Buffer
for _, entry := range entries {
fmt.Fprintf(&buffer, "[%s] %s: %s\n",
entry.Timestamp, entry.Level, entry.Message)
}
return buffer.String()
}
// For simple joining
func fixedJoinStrings(parts []string) string {
return strings.Join(parts, ", ")
}
// Fixed: Using String capacity in Rust
fn fixed_build_html(items: &[String]) -> String {
// Pre-allocate estimated capacity
let mut html = String::with_capacity(items.len() * 25 + 10);
html.push_str("<ul>");
for item in items {
html.push_str("<li>");
html.push_str(&escape_html(item));
html.push_str("</li>");
}
html.push_str("</ul>");
html
}
// Using iterators and collect
fn fixed_build_csv(records: &[Vec<String>]) -> String {
records
.iter()
.map(|record| record.join(","))
.collect::<Vec<_>>()
.join("\n")
}
CVE Examples
This CWE is primarily a performance/quality issue. While no direct CVEs are attributed specifically to string concatenation, performance vulnerabilities caused by inefficient string handling have contributed to denial-of-service conditions in various applications.
Related CWEs
- CWE-1176: Inefficient CPU Computation (parent)
- CWE-1006: Bad Coding Practices (category member)
- CWE-400: Uncontrolled Resource Consumption (can lead to)
References
- MITRE Corporation. "CWE-1046: Creation of Immutable Text Using String Concatenation." https://cwe.mitre.org/data/definitions/1046.html
- Oracle. "Java StringBuilder Class Documentation."
- Microsoft. "StringBuilder Class (System.Text)."