---
Introduction
In March 2023, a security vulnerability in Ruby on Rails' ActiveSupport component caught the attention of developers everywhere. Tagged as CVE-2023-28120, this issue is about how the new bytesplice method interacts with SafeBuffer objects. If an attacker can supply untrusted input, this vulnerability could allow cross-site scripting (XSS) attacks—one of the most dangerous types of web exploits.
This article will break down the vulnerability in plain English, walk through original links and resources, provide code snippets, and explain how an exploit could happen. Let’s dive in.
What is SafeBuffer and bytesplice?
SafeBuffer is a special string type in Rails that is marked as safe for HTML output. Rails uses it to prevent XSS by making sure that only trusted content is sent to your users. If a string is not marked safe, Rails will automatically escape it.
The bytesplice method was introduced in Ruby 3.1 to make binary string changes more efficient. It lets you replace or insert byte sequences directly in a string.
Here’s a quick demo
s = "Hello, world!"
s.bytesplice(7, , "beautiful ")
puts s
# Output: "Hello, beautiful world!"
Where’s the Vulnerability?
When you use bytesplice on a SafeBuffer, the method does not keep track of the buffer's special "safe" status. If user input gets inserted into a SafeBuffer with bytesplice, it might *not* be escaped—meaning an attacker could put malicious JavaScript right into your page.
For example, given user-supplied data
def greet(username)
safe = ActiveSupport::SafeBuffer.new("Hello, ")
# Danger! Unescaped content added via bytesplice
safe.bytesplice(7, , username)
safe << "!"
safe
end
puts greet('<script>alert("XSS")</script>')
Expected Output:
Hello, <script>alert("XSS")</script>!
Actual Output (Vulnerable):
Hello, <script>alert("XSS")</script>!
This means an attacker can run their own JavaScript in your users’ browsers, stealing cookies, session tokens, or more.
Let’s make this painfully clear with a minimalist Rails controller. Imagine you have
class GreetingsController < ApplicationController
def show
name = params[:name] # No sanitization!
safe = ActiveSupport::SafeBuffer.new("Hi, ")
safe.bytesplice(4, , name)
render html: safe
end
end
Request:
GET /greetings/show?name=%3Cscript%3Ealert('XSS')%3C/script%3E
Browser Output:
Hi, <script>alert('XSS')</script>
The attacker just injected a script, and Rails didn't stop it because bytesplice forgot to "unsafeguard" the buffer.
References and Official Fixes
* Github Security Advisory - CVE-2023-28120
* Original Commit Fixing the Issue
* Ruby on Rails Changelog
* MITRE CVE database
The Rails team’s fix checks if bytesplice is operating on a SafeBuffer and marks it as unsafe again if bytesplice is called with non-ASCII or tainted data.
How to Protect Your Application
- Update Rails to a version that includes the patch (7..4.2 or later, 6.1.7.3 or later, or 6..6.3 or later).
- If you extend or monkey-patch strings in Rails, make sure to review your code for dangerous uses of bytesplice on SafeBuffer.
Conclusion
CVE-2023-28120 is a simple but dangerous bug that could let attackers run code in your user’s browsers if you’re not careful with the new bytesplice method and Rails’ SafeBuffer. It’s a good reminder that even “safe” strings can become unsafe with new APIs—and it’s always important to stay up to date and review your code carefully.
Make sure you patch your applications and watch for places where user input is mixed with any kind of HTML-safe buffer or string.
Further Reading
- Rails Guides: Security
- Understanding SafeBuffer in Rails
Timeline
Published on: 01/09/2025 01:15:07 UTC
Last modified on: 01/09/2025 22:15:26 UTC