As part of the “security first” policy, we want to include the following changes for Lotus v0.3.0.
Markup escape
This involves three frameworks, Utils
, View
and Helpers
.
Lotus::Utils::Escape
This is a module that offers markup escaping facilities like Escape.html
.
It implements OWASP recommendations about escaping, and it’s based on their ESAPI implementation.
Technically speaking it differentiates between:
- Content tag sanitization (eg.
<div>sanitized content</div>
) - Attribute tag sanitization (eg.
<div title="sanitized content"></div>
) - URL sanitization via scheme whitelisting (eg.
<a href="sanitized url"></a>
).
https://github.com/lotus/utils/pull/51
Lotus::Helpers
This is a new framework that will be released contextually with the next version of Lotus.
The first and only feature that it will have for now is HTML generator. The output will be automatically escaped, using the mentioned Utils
facilities.
https://github.com/lotus/helpers/pull/7
It will also offer a helper to mixin in views that offers comfortable, and well known shortcuts for markup sanitization. I’m talking about:
-
#escape_html
(aliased as#h
) -
#escape_html_attribute
(aliased as#ha
) -
#escape_url
(aliased as#u
)
https://github.com/lotus/helpers/issues/8
Lotus::View
Until now, this framework didn’t escaped output. It demanded to the chosen template engine to do so. For instance, using erubis
will do it for free.
The main goal of Lotus::View
has always been to concentrate all the presentational logic in views and make the code independent from the current template engine of an application.
Automatic escape
We want to design Lotus::View
to do automatic escaping of the output at the level of the view.
At the same time we want to leave the choice of send unescaped output via #raw
.
class MyView
include Lotus::View
def username
user.username
end
def greet
raw "<script>alert('hello')</script>"
end
end
template = Lotus::View::Template.new('path/to/template.erb')
user = User.new(username: '<script>alert("xss!")</script>')
view = MyView.new(template, user: user)
view.username
# => "<script>alert("xss!")</script>"
view.greet
# => "<script>alert('hello')</script>"
Enhance Lotus::Presenter
The proposal above can be useful but it can be verbose too.
Imagine to have the need of print a lot of informations for that given user
. It would be unhandy to create concrete methods for all of them.
We want to introduce automatic markup escape for presenters.
class MyView
include Lotus::View
def user
escape(super)
end
end
template = Lotus::View::Template.new('path/to/template.erb')
user = User.new({
username: '<script>alert("xss!")</script>',
name: '<script>alert("hack!")</script>'
})
view = MyView.new(template, user: user)
u = view.user
u.username
# => "<script>alert("xss!")</script>"
u.name
# => "<script>alert("hack!")</script>"
Those two are breaking changes, we need to bump a minor release for Lotus::View (v0.4.0) and have an open discussion about those features.
Content-Security-Policy
Content-Security-Policy is a builtin XSS protection that all the modern browsers support. It allows only trusted assets and contents to be loaded and executed.
This is an ultimate protection that can save user’s data in markup escaping is not working as expected.
Because implementing CSP can be really hard at later stages of an application life, it’s better to plan it in advance. Lotus cares about code maintenance.
We want to be the first Ruby web framework to enable it by default.
Starting from Lotus 0.3.0 we want to generate the following setting in application’s configuration.
module Web
class Application < Lotus::Application
configure do
# ...
content_security_policy "connect-src 'self'; script-src 'self'; ..."
end
end
end
If present, that will be the content that the application will send as HTTP header in responses.
X-Frame-Options
Another important HTTP header for web security is X-Frame-Options. It helps to prevent Clickjacking attacks and to avoid web applications to be embedded by untrusted sources. Eg. evil.com cannot include myapp.com via <iframe>
tag.
Again, we want to be the first Ruby web framework to enable it by default too.
Similarly, the idea is to generate a setting for it.
module Web
class Application < Lotus::Application
configure do
# ...
x_frame_options "DENY"
end
end
end
HttpOnly cookies
If all the above mechanisms would fail for some reason, browser or Lotus bug, developer mistake, we want to prevent attackers to steal cookies via JavaScript. Eg. alert(document.cookie)
will fail.
Starting from Lotus::Controller 0.4.0, all the cookies will be HttpOnly
by default.
Read more about this subject.
Conclusion
Those mechanisms will be enabled by default, to help companies to ship secure applications and keep their user’s data safe.
XSS is one of the most common vulnerabilities of the web as of today. Lotus wants to make developers aware of the risks and offer advanced protections for this threat.