Skip to content

Commit 99eed49

Browse files
committed
clarify, document, and test behavior when encountering unknown hashes
1 parent e45276b commit 99eed49

File tree

3 files changed

+40
-30
lines changed

3 files changed

+40
-30
lines changed

README.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ body {
229229
```
230230

231231
```
232+
232233
Content-Security-Policy: ...
233234
script-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
234235
style-src 'nonce-/jRAxuLJsDXAxqhNBB7gg7h55KETtDQBXe4ZL+xIXwI=' ...;
@@ -286,26 +287,33 @@ styles:
286287
- "'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE='"
287288
```
288289
289-
```
290-
Content-Security-Policy: ...
291-
script-src 'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg=' ... ;
292-
style-src 'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY=' 'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE=' ...;
293-
```
294-
295290
##### Helpers
296291
297-
You can use a helper that will raise exceptions when it encounters unknown hash values. Note: this is pretty limited as it will only raise if called on a file with no associated hashes. It will not raise an error if an unknown script is added to a file with known hashses. Also, it won't compute dynamic hashes by design. The output of both helpers will be a plain `script`/`style` tag without modification.
292+
**This will not compute dynamic hashes** by design. The output of both helpers will be a plain `script`/`style` tag without modification and the known hashes for a given file will be added to `script-src`/`style-src` when `hashed_javascript_tag` and `hashed_style_tag` are used. You can use `raise_error_on_unrecognized_hash = true` to be extra paranoid that you have precomputed hash values for all of your inline content. By default, this will raise an error in non-production environments.
298293

299294
```erb
300-
<%= hashed_javascript_tag do %>
301-
console.log("hai");
295+
<%= hashed_style_tag do %>
296+
body {
297+
background-color: black;
298+
}
302299
<% end %>
303300
304301
<%= hashed_style_tag do %>
305302
body {
306-
background-color: black;
303+
font-size: 30px;
304+
font-color: green;
307305
}
308306
<% end %>
307+
308+
<%= hashed_javascript_tag do %>
309+
console.log(1)
310+
<% end %>
311+
```
312+
313+
```
314+
Content-Security-Policy: ...
315+
script-src 'sha256-yktKiAsZWmc8WpOyhnmhQoDf9G2dAZvuBBC+V0LGQhg=' ... ;
316+
style-src 'sha256-SLp6LO3rrKDJwsG9uJUxZapb4Wp2Zhj6Bu3l+d9rnAY=' 'sha256-HSGHqlRoKmHAGTAJ2Rq0piXX4CnEbOl1ArNd6ejp2TE=' ...;
309317
```
310318
311319
### Public Key Pins

lib/secure_headers/view_helper.rb

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ def content_security_policy_nonce(type)
3838
# Checks to see if the hashed code is expected and adds the hash source
3939
# value to the current CSP.
4040
#
41-
# In development/test/etc. an exception will be raised. In production,
42-
# it will dynamically compute the value so things don't break.
41+
# By default, in development/test/etc. an exception will be raised.
4342
def hashed_javascript_tag(raise_error_on_unrecognized_hash = nil, &block)
4443
hashed_tag(
4544
:script,
@@ -70,16 +69,12 @@ def hashed_tag(type, directive, hashes, raise_error_on_unrecognized_hash, block)
7069
content = capture(&block)
7170
file_path = File.join('app', 'views', self.instance_variable_get(:@virtual_path) + '.html.erb')
7271

73-
if hashes.nil?
74-
raise UnexpectedHashedScriptException.new(unexpected_hash_error_message(file_path, content))
75-
end
72+
if raise_error_on_unrecognized_hash
73+
hash_value = hash_source(content)
74+
message = unexpected_hash_error_message(file_path, content, hash_value)
7675

77-
if hashes[file_path].nil?
78-
message = unexpected_hash_error_message(file_path, content)
79-
if raise_error_on_unrecognized_hash
76+
if hashes.nil? || hashes[file_path].nil? || !hashes[file_path].include?(hash_value)
8077
raise UnexpectedHashedScriptException.new(message)
81-
else
82-
warn message
8378
end
8479
end
8580

@@ -88,8 +83,7 @@ def hashed_tag(type, directive, hashes, raise_error_on_unrecognized_hash, block)
8883
content_tag type, content
8984
end
9085

91-
def unexpected_hash_error_message(file_path, content)
92-
hash_value = hash_source(content)
86+
def unexpected_hash_error_message(file_path, content, hash_value)
9387
<<-EOF
9488
\n\n*** WARNING: Unrecognized hash in #{file_path}!!! Value: #{hash_value} ***
9589
#{content}

spec/lib/secure_headers/view_helpers_spec.rb

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class Message < ERB
66

77
def self.template
88
<<-TEMPLATE
9-
<% hashed_javascript_tag do %>
9+
<% hashed_javascript_tag(raise_error_on_unrecognized_hash = true) do %>
1010
console.log(1)
1111
<% end %>
1212
@@ -67,17 +67,25 @@ module SecureHeaders
6767
describe ViewHelpers do
6868
let(:app) { lambda { |env| [200, env, "app"] } }
6969
let(:middleware) { Middleware.new(app) }
70+
let(:request) { Rack::Request.new("HTTP_USER_AGENT" => USER_AGENTS[:chrome]) }
7071

71-
it "uses view helpers" do
72+
before(:each) do
73+
Configuration.default do |config|
74+
config.csp[:script_src] = %w('self')
75+
config.csp[:style_src] = %w('self')
76+
end
77+
end
78+
79+
it "raises an error when attempting to hash unknown content" do
80+
expect {
81+
Message.new(request).result
82+
}.to raise_error(ViewHelpers::UnexpectedHashedScriptException)
83+
end
84+
85+
it "adds known hash values to the corresponding headers when the helper is used" do
7286
begin
7387
allow(SecureRandom).to receive(:base64).and_return("abc123")
7488

75-
Configuration.default do |config|
76-
config.csp[:script_src] = %w('self')
77-
config.csp[:style_src] = %w('self')
78-
end
79-
request = Rack::Request.new("HTTP_USER_AGENT" => USER_AGENTS[:chrome])
80-
8189
expected_hash = "sha256-3/URElR9+3lvLIouavYD/vhoICSNKilh15CzI/nKqg8="
8290
Configuration.instance_variable_set(:@script_hashes, "app/views/asdfs/index.html.erb" => ["'#{expected_hash}'"])
8391
expected_style_hash = "sha256-7oYK96jHg36D6BM042er4OfBnyUDTG3pH1L8Zso3aGc="

0 commit comments

Comments
 (0)