> ## Documentation Index
> Fetch the complete documentation index at: https://docs.semgrep.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Prevent XSS for Ruby on Rails

This is a cross-site scripting (XSS) prevention cheat sheet by Semgrep, Inc. It contains code patterns of potential XSS in an application. Instead of scrutinizing code for exploitable vulnerabilities, the recommendations in this cheat sheet pave a safe road for developers that mitigate the possibility of XSS in your code. By following these recommendations, you can be reasonably sure your code is free of XSS.

Learn more about [Cross-site Scripting](/learn/vulnerabilities/cross-site-scripting) vulnerability concepts.

## Mitigation summary

In general, always let Rails render ERB template files rather than constructing them in code. If HTML escaping is needed, use `html_safe()` in Ruby code and review each individual usage carefully. Once reviewed, mark the line with `# nosem`. Beware of putting data in dangerous locations in templates. And as always, run a security checker continuously on your code.

### Check your project using Semgrep

```bash theme={null}
semgrep --config auto .
```

## 1. Unescaped variable enters template engine in Ruby code

### 1.A. Using **html\_safe()**

`html_safe()` marks the supplied string as "safe for HTML rendering." This bypasses HTML escaping and potentially creates XSS vulnerabilities.

Example:

```ruby theme={null}
html = "<div>#{name}</div>".html_safe
```

#### References

* [Brakeman scanner - Cross-site scripting](https://github.com/presidentbeef/brakeman/blob/main/warning_types/cross_site_scripting/index.markdown)
* [Preventing XSS in Ruby on Rails](https://www.netsparker.com/blog/web-security/preventing-xss-ruby-on-rails-web-applications/)

#### Mitigation

Ban `html_safe()`. Alternatively, If needed, review each usage and exempt with `# nosem`.

#### Semgrep rule

[`ruby.rails.security.audit.xss.avoid-html-safe.avoid-html-safe`](https://semgrep.dev/r/ruby.rails.security.audit.xss.avoid-html-safe.avoid-html-safe)

### 1.B. Using **content\_tag()**

`content_tag()`'s escaping behavior has changed between Rails 2 and 3. In Rails 2, no supplied content is escaped. In Rails 2 and 3, attribute names are not escaped. Further, the returned value is marked as "safe," the same as if `html_safe()` had been used. This confusing behavior makes it difficult to use `content_tag()` properly; improper use can create XSS vulnerabilities in your application.

Example:

```ruby theme={null}
content_tag :p, "Hello, #{name}"
```

#### References

* [Brakeman scanner - Content tag](https://brakemanscanner.org/warning_types/content_tag/)

#### Mitigation

Ban `content_tag()`. Alternatively, If necessary, prefer `html_safe()` due to its straightforward behavior.

#### Semgrep rule

[`ruby.rails.security.audit.xss.avoid-content-tag.avoid-content-tag`](https://semgrep.dev/r/ruby.rails.security.audit.xss.avoid-content-tag.avoid-content-tag)

### 1.C. Using **raw()**

`raw()` disables HTML escaping for the returned content. This permits raw HTML to be rendered in a template, which could create a XSS vulnerability.

Example:

```ruby theme={null}
raw @user.name
```

#### References

* [`raw()` documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/OutputSafetyHelper.html#method-i-raw)
* [Preventing XSS in Ruby on Rails](https://www.netsparker.com/blog/web-security/preventing-xss-ruby-on-rails-web-applications/)

#### Mitigation

Ban `raw()`. Alternatively, Prefer `html_safe()` if necessary.

#### Semgrep rule

[`ruby.rails.security.audit.xss.avoid-raw.avoid-raw`](https://semgrep.dev/r/ruby.rails.security.audit.xss.avoid-raw.avoid-raw)

### 1.D. Disabling of **ActiveSupport#escape\_html\_entities\_in\_json**

`ActiveSupport#escape_html_entities_in_json` is a setting which determines whether `Hash#to_json()` will escape HTML characters. Disabling this could create XSS vulnerabilities.

Example:

```ruby theme={null}
config.active_support.escape_html_entities_in_json = false
```

#### References

* [`escape_html_entities_in_json` documentation](https://rubydoc.info/rails/ActiveSupport/JSON/Encoding.escape_html_entities_in_json=)
* [Brakeman scanner - Cross-site scripting (JSON)](https://brakemanscanner.org/warning_types/cross_site_scripting_to_json/)
* [How to disable HTML escaping for JSON, but keep enabled for views?](https://stackoverflow.com/a/43877771)

#### Mitigation

Ban disabling of `ActiveSupport#escape_html_entities_in_json`. Alternatively, If HTML is needed in JSON, use `JSON.generate()` and review each usage carefully. Exempt each case with `# nosem`.

#### Semgrep rule

[`ruby.lang.security.json-entity-escape.json-entity-escape`](https://semgrep.dev/r/ruby.lang.security.json-entity-escape.json-entity-escape)

## 2. Bypassing the template engine

### 2.A. Manually creating an ERB template

Manually creating an ERB template could create a server-side template injection (SSTI) vulnerability if it is created with user input. (This could also result in XSS.) Due to the severity of this type of vulnerability, it is better to use a template file instead of creating templates in code.

Example:

```ruby theme={null}
ERB.new("<div>#{@user.name}</div>").result
```

#### References

* [Brakeman scanner - Template injection](https://github.com/presidentbeef/brakeman/blob/main/warning_types/template_injection/index.markdown)
* [Ruby ERB template injection](https://www.trustedsec.com/blog/rubyerb-template-injection/)

#### Mitigation

Ban template creation in code. Alternatively, Use ERB template files.

#### Semgrep rule

[`ruby.rails.security.audit.xss.manual-template-creation.manual-template-creation`](https://semgrep.dev/r/ruby.rails.security.audit.xss.manual-template-creation.manual-template-creation)

### 2.B. Rendering an inline template with **render inline**

`render inline:` is the same as creating a template manually and is therefore susceptible to the same vulnerabilities as manually creating an ERB template. This can result in a SSTI or XSS vulnerability.

Example:

```ruby theme={null}
render inline: "<div>#{@user.name}</div>"
```

#### References

* [Zen Rails Security Checklist](https://github.com/brunofacca/zen-rails-security-checklist#output-escaping--sanitization)
* [Inline renders - even worse than XSS!](https://brakemanpro.com/2017/09/08/cross-site-scripting-in-rails#inline-renders---even-worse-than-xss)

#### Mitigation

Ban `render inline:`. Alternatively, Use ERB template files.

#### Semgrep rule

[`ruby.rails.security.audit.xss.avoid-render-inline.avoid-render-inline`](https://semgrep.dev/r/ruby.rails.security.audit.xss.avoid-render-inline.avoid-render-inline)

### 2.C. Using **render text:**

`render text:` unintuitively sets the Content-Type to text/html. This means anything rendered through `render text:` will be interpreted as HTML. Templates rendered in this manner could create a XSS vulnerability.

Example:

```ruby theme={null}
render text: "<div>#{@user.name}</div>"
```

#### References

* [Inline renders - even worse than XSS!](https://brakemanpro.com/2017/09/08/cross-site-scripting-in-rails#inline-renders---even-worse-than-xss)

#### Mitigation

Ban `render text:`. Alternatively, Use ERB template files.

#### Semgrep rule

[`ruby.rails.security.audit.xss.avoid-render-text.avoid-render-text`](https://semgrep.dev/r/ruby.rails.security.audit.xss.avoid-render-text.avoid-render-text)

## 3. Templates: Variable explicitly unescaped

### 3.A. Using **html\_safe()**

`html_safe()` marks the supplied string as "safe for HTML rendering." This bypasses HTML escaping and potentially creates XSS vulnerabilities.

Example:

```erb theme={null}
<%= name.html_safe %>
```

#### References

* [Brakeman scanner - Cross-site scripting](https://github.com/presidentbeef/brakeman/blob/main/warning_types/cross_site_scripting/index.markdown)
* [Preventing XSS in Ruby on Rails](https://www.netsparker.com/blog/web-security/preventing-xss-ruby-on-rails-web-applications/)

#### Mitigation

Ban `html_safe()`. Alternatively, Prefer using `html_safe()` in Ruby code instead of templates.

#### Semgrep rule

[`ruby.rails.security.audit.xss.templates.avoid-html-safe.avoid-html-safe`](https://semgrep.dev/r/ruby.rails.security.audit.xss.templates.avoid-html-safe.avoid-html-safe)

### 3.B Using **content\_tag()**

`content_tag()`'s escaping behavior has changed between Rails 2 and 3. In Rails 2, no supplied content is escaped. In Rails 2 and 3, attribute names are not escaped. Further, the returned value is marked as "safe," the same as if `html_safe()` had been used. This confusing behavior makes it difficult to use `content_tag()` properly; improper use can create XSS vulnerabilities in your application.

Example:

```ruby theme={null}
<%= content_tag :p, "Hello, #{name}" %>
```

#### References

* [Brakeman scanner - Content tag](https://brakemanscanner.org/warning_types/content_tag/)

#### Mitigation

Ban `content_tag()`. Alternatively, If necessary, prefer `html_safe()` in Ruby code due to its straightforward behavior.

#### Semgrep rule

[`ruby.rails.security.audit.xss.templates.avoid-content-tag.avoid-content-tag`](https://semgrep.dev/r/ruby.rails.security.audit.xss.templates.avoid-content-tag.avoid-content-tag)

### 3.C. Using **raw()**

`raw()` disables HTML escaping for the returned content. This permits raw HTML to be rendered in a template, which could create a XSS vulnerability.

Example:

```ruby theme={null}
<%= raw @user.name =>
```

#### References

* [`raw()` documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/OutputSafetyHelper.html#method-i-raw)
* [Preventing XSS in Ruby on Rails](https://www.netsparker.com/blog/web-security/preventing-xss-ruby-on-rails-web-applications/)

#### Mitigation

Ban `raw()`. Alternatively, Prefer `html_safe()` in Ruby code if necessary.

#### Semgrep rule

[`ruby.rails.security.audit.xss.templates.avoid-raw.avoid-raw`](https://semgrep.dev/r/ruby.rails.security.audit.xss.templates.avoid-raw.avoid-raw)

### 3.D. Using **\<%== ... %>**, which is an alias for **html\_safe()**

The double-equals `==` is an ERB alias for `html_safe()`. This will mark the contents as "safe for rendering" and may introduce an XSS vulnerability.

Example:

```ruby theme={null}
<%== @user.name %>
```

#### References

* [Alias for `html_safe()`](https://medium.com/sumone-technical-blog/a-pretty-way-to-unescape-html-in-a-ruby-on-rails-application-efc22b850027)
* [Raw vs. html\_safe](https://stackoverflow.com/questions/4251284/raw-vs-html-safe-vs-h-to-unescape-html#:~:text===)

#### Mitigation

Ban `<%== ... %>`, which is an alias for `html_safe()`. Alternatively, Prefer `html_safe()` in Ruby code if necessary.

#### Semgrep rule

[`ruby.rails.security.audit.xss.templates.alias-for-html-safe.alias-for-html-safe`](https://semgrep.dev/r/ruby.rails.security.audit.xss.templates.alias-for-html-safe.alias-for-html-safe)

## 4. Templates: Variable in dangerous location

### 4.A Unquoted variable in HTML attribute

Unquoted template variables rendered into HTML attributes is a potential XSS vector because an attacker could inject JavaScript handlers which do not require HTML characters. An example handler might look like: `onmouseover=alert(1)`. HTML escaping will not mitigate this. The variable must be quoted to avoid this.

Example:

```ruby theme={null}
<div class=<%= classes %></div>
```

#### References:

* [Flask cross-site scripting considerations - unquoted variable in HTML attribute](https://flask.palletsprojects.com/en/1.1.x/security/#cross-site-scripting-xss)

#### Mitigation

Flag unquoted HTML attributes ERB expressions. Alternatively, Always use quotes around HTML attributes.

#### Semgrep rule

[`ruby.rails.security.audit.xss.templates.unquoted-attribute.unquoted-attribute`](https://semgrep.dev/r/ruby.rails.security.audit.xss.templates.unquoted-attribute.unquoted-attribute)

### 4.B. Variable in **href** attribute

Template variables in a `href` value could still accept the `javascript:` URI. This could be a XSS vulnerability. HTML escaping will not prevent this. Use `link_to` beginning with a literal forward slash to generate links.

Example:

```ruby theme={null}
<a href="<%= link %>"></a>
```

#### References

* [Flask cross-site scripting considerations - variable in `href`](https://flask.palletsprojects.com/en/1.1.x/security/#cross-site-scripting-xss)

#### Mitigation

Flag template variables in `href` attributes. Alternatively, Use `url_for` to generate links.

#### Semgrep rule

[`ruby.rails.security.audit.xss.templates.var-in-href.var-in-href`](https://semgrep.dev/r/ruby.rails.security.audit.xss.templates.var-in-href.var-in-href)

### 4.C. Using **link\_to** with unrestricted URL scheme

Detected a template variable used in 'link\_to'. This will generate dynamic data in the 'href' attribute. This allows a malicious actor to input the 'javascript:' URI and is subject to cross- site scripting (XSS) attacks. If using a relative URL, start with a literal forward slash and concatenate the URL, like this: `<%= link_to "Here", "/"+@link %>`. You may also consider setting the Content Security Policy (CSP) header.

Example:

```ruby theme={null}
<%= link_to "Here", @link %>
```

#### References

* [OWASP Cheatsheet - Ruby on Rails XSS](https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html#cross-site-scripting-xss)
* [Brakeman scanner - link\_to](https://github.com/presidentbeef/brakeman/blob/main/warning_types/link_to_href/index.markdown)

#### Mitigation

Flag `link_to` in templates. Alternatively, If you must use this, add a literal forward-slash at the beginning to create a relative url.

#### Semgrep rule

[`ruby.rails.security.audit.xss.templates.dangerous-link-to.dangerous-link-to`](https://semgrep.dev/r/ruby.rails.security.audit.xss.templates.dangerous-link-to.dangerous-link-to)

### 4.D. Variable in **\<script>** block

Template variables placed directly into JavaScript or similar are now directly in a code execution context. Normal HTML escaping will not prevent the possibility of code injection because code can be written without HTML characters. This creates the potential for XSS vulnerabilities, or worse.

Example:

```ruby theme={null}
<script>var name = <%= name %>;</script>
```

#### References

* [Template engines: Why default encoders are not enough](https://www.veracode.com/blog/secure-development/nodejs-template-engines-why-default-encoders-are-not-enough)
* [Protecting against XSS in Rails - JavaScript contexts](https://blog.ircmaxell.com/2018/06/protecting-rails-xss.html)
* [`escape_javascript` documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/JavaScriptHelper.html#method-i-escape_javascript)

#### Mitigation

Ban template variables in `&lt;script&gt;` blocks. Alternatively, If necessary, use the `escape_javascript` function or its alias, `j`. Review each usage carefully and exempt with `# nosem`.

#### Semgrep rule

[`ruby.rails.security.audit.xss.templates.var-in-script-tag.var-in-script-tag`](https://semgrep.dev/r/ruby.rails.security.audit.xss.templates.var-in-script-tag.var-in-script-tag)
